Skip to content

Commit

Permalink
feat(enum): impl TryInto
Browse files Browse the repository at this point in the history
  • Loading branch information
makcandrov committed Oct 8, 2024
1 parent 08b40f1 commit ba889de
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 11 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

- `Default` - Implements the [`Default`] trait.
- `From` - Implements the [`From`] trait.
- `TryInto` - Implements the [`TryInto`] trait.

### Structures methods

Expand Down Expand Up @@ -45,6 +46,7 @@
[`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html
[`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
[`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
[`TryInto`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html

## Usage

Expand Down
2 changes: 1 addition & 1 deletion src/components/enums/methods/try_into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub fn enum_method_try_into(
#keywords fn #method_ident(self) -> Result<#ty, Self> {
match self {
Self::#variant_ident #destruct => Ok(#ret),
item => Err(item),
other => Err(other),
}
}
})
Expand Down
4 changes: 3 additions & 1 deletion src/components/enums/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use methods::enum_method_try_into;
use syn::DataEnum;
use traits::enum_trait_try_into;

use self::methods::{
enum_method_as_ref, enum_method_as_ref_mut, enum_method_from, enum_method_into, enum_method_is,
Expand All @@ -10,7 +11,7 @@ use crate::expand::{Context, Implems};
use crate::idents::methods::{
METHOD_AS_REF, METHOD_AS_REF_MUT, METHOD_FROM, METHOD_INTO, METHOD_IS, METHOD_TRY_INTO,
};
use crate::idents::traits::{TRAIT_DEFAULT, TRAIT_FROM};
use crate::idents::traits::{TRAIT_DEFAULT, TRAIT_FROM, TRAIT_TRY_INTO};

mod methods;
mod traits;
Expand Down Expand Up @@ -52,6 +53,7 @@ pub fn enum_impl(
let tokens = match attribute.ident.to_string().as_str() {
TRAIT_FROM => enum_trait_from(context, &variant, attribute)?,
TRAIT_DEFAULT => enum_trait_default(context, &variant, attribute)?,
TRAIT_TRY_INTO => enum_trait_try_into(context, &variant, attribute)?,
_ => {
return Err(syn::Error::new_spanned(
&attribute.ident,
Expand Down
7 changes: 5 additions & 2 deletions src/components/enums/traits/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
mod default;
pub use default::enum_trait_default;

mod from;
pub use from::enum_trait_from;

mod default;
pub use default::enum_trait_default;
mod try_into;
pub use try_into::enum_trait_try_into;
62 changes: 62 additions & 0 deletions src/components/enums/traits/try_into.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use proc_macro2::{Delimiter, Ident, TokenStream};
use quote::quote;
use syn::Variant;

use crate::attributes::Attribute;
use crate::config::{build_config, build_enum_doc};
use crate::expand::Context;
use crate::tokens::{destructure_data, destructure_types, get_delimiter};

build_enum_doc! {
ConfigDoc,
"Converts into the associated data if it is the [`{}::{}`] variant. Otherwise, returns `Err(self)`.",
}

build_config! {
Config,
(doc, ConfigDoc, false),
}

pub fn enum_trait_try_into(
context: &Context,
variant: &Variant,
attribute: &Attribute,
) -> syn::Result<TokenStream> {
let config = Config::new(context, attribute, variant)?;

let fields = &variant.fields;
let delimiter = get_delimiter(fields);

let ty = destructure_types(fields, quote! {}, quote! { () }, false);
let destruct = destructure_data(fields, quote! {}, quote! {}, delimiter, true);
let ret = destructure_data(
fields,
quote! {},
quote! { () },
Delimiter::Parenthesis,
false,
);

let variant_ident = &variant.ident;
let doc = &config.doc;
let trait_ident = syn::Ident::new("TryInto", attribute.ident.span());
let method_ident = Ident::new("try_into", attribute.ident.span());

let content = quote! {
type Error = Self;

#[doc = #doc]
fn #method_ident (self) -> Result<#ty, Self> {
match self {
Self:: #variant_ident #destruct => Ok(#ret),
other => Err(other),
}
}
};

Ok(context.in_impl(
quote! { ::core::convert::#trait_ident<#ty> for },
&content,
None,
))
}
1 change: 1 addition & 0 deletions src/idents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ pub mod traits {
pub const TRAIT_DEREF_MUT: &str = "DerefMut";
pub const TRAIT_FROM: &str = "From";
pub const TRAIT_INTO: &str = "Into";
pub const TRAIT_TRY_INTO: &str = "TryInto";
}
15 changes: 8 additions & 7 deletions tests/test_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use quick_impl::QuickImpl;
fn test_enum_variant_unit() {
#[derive(Debug, Eq, PartialEq, QuickImpl)]
enum Test {
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, pub try_into, impl From, impl Default)]
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, pub try_into, impl Default, impl From, impl TryInto)]
A,
}

Expand All @@ -24,15 +24,16 @@ fn test_enum_variant_unit() {
fn test_enum_variant_single_unnamed() {
#[derive(Debug, Clone, Eq, PartialEq, QuickImpl)]
enum Test {
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, pub try_into, impl From, impl Default)]
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, pub try_into, impl From, impl Default, impl TryInto)]
A(usize),
}

let a = Test::A(12);
assert!(a.is_a());
assert_eq!(*a.as_a().unwrap(), 12);
assert_eq!(a.clone().into_a().unwrap(), 12);
assert_eq!(a.try_into_a().unwrap(), 12);
assert_eq!(a.clone().try_into_a().unwrap(), 12);
assert_eq!(TryInto::<usize>::try_into(a.clone()).unwrap(), 12);

let mut a = Test::A(12);
assert_eq!(*a.as_a_mut().unwrap(), 12);
Expand All @@ -45,7 +46,7 @@ fn test_enum_variant_single_unnamed() {
fn test_enum_variant_single_named() {
#[derive(Debug, Eq, PartialEq, QuickImpl)]
enum Test {
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, impl From, impl Default)]
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, impl From, impl Default, impl TryInto)]
A { a: usize },
}

Expand All @@ -65,7 +66,7 @@ fn test_enum_variant_single_named() {
fn test_enum_variant_multiple_unnamed() {
#[derive(Debug, Eq, PartialEq, QuickImpl)]
enum Test {
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, impl From, impl Default)]
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, impl From, impl Default, impl TryInto)]
A(usize, isize, char),
}

Expand All @@ -88,7 +89,7 @@ fn test_enum_variant_multiple_unnamed() {
fn test_enum_variant_multiple_named() {
#[derive(Debug, Eq, PartialEq, QuickImpl)]
enum Test {
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, impl From, impl Default)]
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, impl From, impl Default, impl TryInto)]
A { a: usize, b: isize, c: char },
}

Expand Down Expand Up @@ -130,7 +131,7 @@ fn test_enum_variant_multiple_named() {
fn test_enum_generics() {
#[derive(Debug, Eq, PartialEq, QuickImpl)]
enum Test<T, U> {
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, impl From, impl Default)]
#[quick_impl(pub(crate) const is, const as_ref, pub as_ref_mut, pub(crate) from, pub(crate) into, impl From, impl Default, impl TryInto)]
A { a: T, b: U },
}

Expand Down

0 comments on commit ba889de

Please sign in to comment.