diff --git a/src/render/fixture.rs b/src/render/fixture.rs index 5d52c64..3366ae4 100644 --- a/src/render/fixture.rs +++ b/src/render/fixture.rs @@ -3,7 +3,7 @@ use syn::{parse_quote, Ident, ItemFn}; use quote::quote; -use super::{generics_clean_up, render_exec_call, resolve_new_args}; +use super::{generics_clean_up, render_exec_call, resolve_aruments}; use crate::parse::fixture::FixtureInfo; use crate::resolver::{self, Resolver}; use crate::utils::{fn_args, fn_args_idents}; @@ -35,7 +35,7 @@ pub(crate) fn render<'a>(fixture: ItemFn, info: FixtureInfo) -> TokenStream { .map(|tp| &tp.ident) .cloned() .collect::>(); - let inject = resolve_new_args(fixture.sig.inputs.iter(), &resolver, &generics_idents); + let inject = resolve_aruments(fixture.sig.inputs.iter(), &resolver, &generics_idents); let partials = (1..=orig_args.len()).map(|n| render_partial_impl(&fixture, n, &resolver, &info)); @@ -86,7 +86,7 @@ fn render_partial_impl( .map(|tp| &tp.ident) .cloned() .collect::>(); - let inject = resolve_new_args(fixture.sig.inputs.iter().skip(n), resolver, &genercs_idents); + let inject = resolve_aruments(fixture.sig.inputs.iter().skip(n), resolver, &genercs_idents); let sign_args = fn_args(fixture).take(n); let fixture_args = fn_args_idents(fixture).cloned().collect::>(); diff --git a/src/render/mod.rs b/src/render/mod.rs index b397929..bd31c8f 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -209,7 +209,7 @@ fn single_test_case<'a>( if trace_me.len() > 0 { attributes.add_trace(format_ident!("trace")); } - let inject = resolve_new_args(args.iter(), &resolver, generic_types); + let inject = resolve_aruments(args.iter(), &resolver, generic_types); let args = args .iter() .filter_map(MaybeIdent::maybe_ident) @@ -268,7 +268,113 @@ fn trace_arguments<'a>( fn default_fixture_resolve(ident: &Ident) -> Cow { Cow::Owned(parse_quote! { #ident::default() }) } -fn fnarg_2_fixture(arg: &FnArg, resolver: &impl Resolver, generic_types: &[Ident]) -> Option { + +fn handling_magic_conversion_code(fixture: Cow, arg_type: &Type) -> Expr { + parse_quote! { + { + struct __Wrap(std::marker::PhantomData); + + trait __ViaParseDebug<'a, T> { + fn magic_conversion(&self, input: &'a str) -> T; + } + + impl<'a, T> __ViaParseDebug<'a, T> for &&__Wrap + where + T: std::str::FromStr, + T::Err: std::fmt::Debug, + { + fn magic_conversion(&self, input: &'a str) -> T { + T::from_str(input).unwrap() + } + } + + trait __ViaParse<'a, T> { + fn magic_conversion(&self, input: &'a str) -> T; + } + + impl<'a, T> __ViaParse<'a, T> for &__Wrap + where + T: std::str::FromStr, + { + fn magic_conversion(&self, input: &'a str) -> T { + match T::from_str(input) { + Ok(v) => v, + Err(_) => { + panic!("Cannot parse '{}' to get {}", input, std::stringify!(#arg_type)); + } + } + } + } + + trait __ViaIdent<'a, T> { + fn magic_conversion(&self, input: &'a str) -> T; + } + + impl<'a> __ViaIdent<'a, &'a str> for &&__Wrap<&'a str> { + fn magic_conversion(&self, input: &'a str) -> &'a str { + input + } + } + (&&&__Wrap::<#arg_type>(std::marker::PhantomData)).magic_conversion(#fixture) + } + } +} + +trait IsLiteralExpression { + fn is_literal(&self) -> bool; +} + +impl> IsLiteralExpression for E { + fn is_literal(&self) -> bool { + match self.as_ref() { + &Expr::Lit(syn::ExprLit { ref lit, .. }) => match lit { + syn::Lit::Str(_) => true, + _ => false, + }, + _ => false, + } + } +} + +trait IsExplicitType { + fn is_explicit(&self) -> bool; +} + +impl IsExplicitType for &Type { + fn is_explicit(&self) -> bool { + match self { + Type::ImplTrait(_) + | Type::TraitObject(_) + | Type::Infer(_) + | Type::Group(_) + | Type::Macro(_) + | Type::Never(_) + | Type::Paren(_) + | Type::Verbatim(_) => false, + _ => true, + } + } +} + +fn ident_contains(haystack: &[Ident], needle: &Ident) -> bool { + haystack.iter().find(|&id| id == needle).is_some() +} + +fn type_can_be_get_from_literal_str(t: &Type, generics: &[Ident]) -> bool { + if !t.is_explicit() { + return false + } + match t.maybe_ident() { + Some(id) => !ident_contains(generics, id), + None => false + } +} + +fn resolve_argument( + arg: &FnArg, + resolver: &impl Resolver, + generic_types: &[Ident], +) -> Option { let ident = arg.maybe_ident()?; let arg_type = arg.maybe_type()?; let id_str = ident.to_string(); @@ -283,93 +389,22 @@ fn fnarg_2_fixture(arg: &FnArg, resolver: &impl Resolver, generic_types: &[Ident .map(|e| e.clone()) .unwrap_or_else(|| default_fixture_resolve(&fixture_name)); - let is_explicit_type = match arg_type { - Type::ImplTrait(_) - | Type::TraitObject(_) - | Type::Infer(_) - | Type::Group(_) - | Type::Macro(_) - | Type::Never(_) - | Type::Paren(_) - | Type::Verbatim(_) => false, - _ => true, - }; - - let is_literal_str = match fixture.as_ref() { - &Expr::Lit(syn::ExprLit { ref lit, .. }) => match lit { - syn::Lit::Str(_) => true, - _ => false, - }, - _ => false, - }; - if is_literal_str - && is_explicit_type - && arg_type - .maybe_ident() - .map(|id| generic_types.iter().find(|&tp| tp == id).is_none()) - .unwrap_or_default() + if fixture.is_literal() + && type_can_be_get_from_literal_str(arg_type, generic_types) { - let new_fixture: Expr = parse_quote! { - { - struct __Wrap(std::marker::PhantomData); - - trait __ViaParseDebug<'a, T> { - fn magic_conversion(&self, input: &'a str) -> T; - } - - impl<'a, T> __ViaParseDebug<'a, T> for &&__Wrap - where - T: std::str::FromStr, - T::Err: std::fmt::Debug, - { - fn magic_conversion(&self, input: &'a str) -> T { - T::from_str(input).unwrap() - } - } - - trait __ViaParse<'a, T> { - fn magic_conversion(&self, input: &'a str) -> T; - } - - impl<'a, T> __ViaParse<'a, T> for &__Wrap - where - T: std::str::FromStr, - { - fn magic_conversion(&self, input: &'a str) -> T { - match T::from_str(input) { - Ok(v) => v, - Err(_) => { - panic!("Cannot parse '{}' to get {}", input, std::stringify!(#arg_type)); - } - } - } - } - - trait __ViaIdent<'a, T> { - fn magic_conversion(&self, input: &'a str) -> T; - } - - impl<'a> __ViaIdent<'a, &'a str> for &&__Wrap<&'a str> { - fn magic_conversion(&self, input: &'a str) -> &'a str { - input - } - } - (&&&__Wrap::<#arg_type>(std::marker::PhantomData)).magic_conversion(#fixture) - } - }; - fixture = Cow::Owned(new_fixture); + fixture = Cow::Owned(handling_magic_conversion_code(fixture, arg_type)); } Some(parse_quote! { let #ident = #fixture; }) } -fn resolve_new_args<'a>( +fn resolve_aruments<'a>( args: impl Iterator, resolver: &impl Resolver, generic_types: &[Ident], ) -> TokenStream { - let define_vars = args.map(|arg| fnarg_2_fixture(arg, resolver, generic_types)); + let define_vars = args.map(|arg| resolve_argument(arg, resolver, generic_types)); quote! { #(#define_vars)* }