diff options
| -rw-r--r-- | src/lib.rs | 23 | ||||
| -rw-r--r-- | src/transform.rs | 98 | ||||
| -rw-r--r-- | ui-tests/src/bin/unconvertible_type_map.rs | 8 | ||||
| -rw-r--r-- | ui-tests/src/bin/unconvertible_type_map.stderr | 5 | ||||
| -rw-r--r-- | ui-tests/src/bin/unconvertible_type_vec.stderr | 6 | 
5 files changed, 95 insertions, 45 deletions
| @@ -53,9 +53,11 @@ mod syn_utils;  mod transform;  macro_rules! abort { -    ($span:expr, $message:literal) => { -        quote_spanned! {$span => compile_error!($message);}.into() -    }; +    ($span:expr, $message:literal $(,$args:tt)*) => {{ +        let msg = format!($message $(,$args)*); +        let tokens = quote_spanned! {$span => compile_error!(#msg);}.into(); +        tokens +    }};  }  #[proc_macro_attribute] @@ -119,6 +121,21 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream {                      MethodError::Transform(TransformError::UnsupportedType) => {                          return abort!(span, "dynamize does not know how to convert this type")                      } +                    MethodError::Transform(TransformError::ExpectedAtLeastNTypes(n)) => { +                        return abort!( +                            span, +                            "dynamize expects at least {} generic type arguments for this type", +                            n +                        ) +                    } +                    MethodError::Transform(TransformError::AssocTypeAfterFirstNTypes(n, ident)) => { +                        return abort!( +                            span, +                            "for {} dynamize supports associated types only within the first {} generic type parameters", +                            ident, +                            n +                        ) +                    }                      MethodError::UnconvertedAssocType => {                          return abort!(span, "dynamize does not support associated types here")                      } diff --git a/src/transform.rs b/src/transform.rs index 60ac6e8..05866b6 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -23,26 +23,32 @@ pub struct TypeConverter<'a> {  pub enum TransformError {      AssocTypeWithoutDestType,      UnsupportedType, +    ExpectedAtLeastNTypes(usize), +    AssocTypeAfterFirstNTypes(usize, Ident),  }  impl TypeConverter<'_> { -    /// Returns true for types that take one generic type parameter T -    /// and implement IntoIterator<Item=T> and FromIterator<T> -    fn known_as_collection_with_one_type(&self, ident: &Ident) -> bool { +    /// A return type of Some(1) means that the type implements +    /// IntoIterator<Item=T1> and FromIterator<T1> +    /// with T1 being its first generic type parameter. +    /// +    /// A return type of Some(2) means that the type implements +    /// IntoIterator<Item=(T1, T2)> and FromIterator<(T1, T2)> +    /// with T1 and T2 being its first two generic type parameters. +    /// +    /// ... etc. A return type of None means the type isn't recognized. +    #[rustfmt::skip] +    fn get_collection_type_count(&self, ident: &Ident) -> Option<usize> {          // when adding a type here don't forget to document it in the README -        ident == "Vec" -            || ident == "VecDeque" -            || ident == "LinkedList" -            || ident == "HashSet" -            || ident == "BinaryHeap" -            || ident == "BTreeSet" -    } - -    /// Returns true for types that take two generic type parameter K and V -    /// and implement IntoIterator<Item=(K, V)> and FromIterator<(K, V)> -    fn known_as_collection_with_two_types(&self, ident: &Ident) -> bool { -        // when adding a type here don't forget to document it in the README -        ident == "HashMap" || ident == "BTreeMap" +        if ident == "Vec"        { return Some(1); } +        if ident == "VecDeque"   { return Some(1); } +        if ident == "LinkedList" { return Some(1); } +        if ident == "HashSet"    { return Some(1); } +        if ident == "BinaryHeap" { return Some(1); } +        if ident == "BTreeSet"   { return Some(1); } +        if ident == "HashMap"    { return Some(2); } +        if ident == "BTreeMap"   { return Some(2); } +        None      }      pub fn convert_type(&self, type_: &mut Type) -> Result<TypeTransform, (Span, TransformError)> { @@ -113,36 +119,50 @@ impl TypeConverter<'_> {                  if let PathArguments::AngleBracketed(args) = &mut last_seg.arguments {                      let mut args: Vec<_> = type_arguments_mut(&mut args.args).collect(); -                    if args.len() == 1 { -                        if iter_type(args[0]).any(match_assoc_type) { -                            if (last_seg.ident == "Option" && path_len == 1) -                                || last_seg.ident == "Result" -                            { -                                return Ok(TypeTransform::Map(self.convert_type(args[0])?.into())); -                            } else if self.known_as_collection_with_one_type(&last_seg.ident) -                                && path_len == 1 -                            { -                                return Ok(TypeTransform::IntoIterMapCollect(vec![ -                                    self.convert_type(args[0])? -                                ])); +                    if path_len == 1 { +                        if let Some(type_count) = self.get_collection_type_count(&last_seg.ident) { +                            if args.len() < type_count { +                                return Err(( +                                    last_seg.span(), +                                    TransformError::ExpectedAtLeastNTypes(type_count), +                                )); +                            } +                            for i in type_count..args.len() { +                                if iter_type(args[i]).any(match_assoc_type) { +                                    return Err(( +                                        args[i].span(), +                                        TransformError::AssocTypeAfterFirstNTypes( +                                            type_count, +                                            last_seg.ident.clone(), +                                        ), +                                    )); +                                } +                            } +                            let mut transforms = Vec::new(); +                            for arg in args { +                                transforms.push(self.convert_type(arg)?);                              } +                            return Ok(TypeTransform::IntoIterMapCollect(transforms)); +                        } +                    } + +                    if args.len() == 1 { +                        if iter_type(args[0]).any(match_assoc_type) +                            && ((last_seg.ident == "Option" && path_len == 1) +                                || last_seg.ident == "Result") +                        { +                            return Ok(TypeTransform::Map(self.convert_type(args[0])?.into()));                          }                      } else if args.len() == 2                          && path_len == 1                          && (iter_type(args[0]).any(match_assoc_type)                              || iter_type(args[1]).any(match_assoc_type)) +                        && last_seg.ident == "Result"                      { -                        if last_seg.ident == "Result" { -                            return Ok(TypeTransform::Result( -                                self.convert_type(args[0])?.into(), -                                self.convert_type(args[1])?.into(), -                            )); -                        } else if self.known_as_collection_with_two_types(&last_seg.ident) { -                            return Ok(TypeTransform::IntoIterMapCollect(vec![ -                                self.convert_type(args[0])?, -                                self.convert_type(args[1])?, -                            ])); -                        } +                        return Ok(TypeTransform::Result( +                            self.convert_type(args[0])?.into(), +                            self.convert_type(args[1])?.into(), +                        ));                      }                  }              } diff --git a/ui-tests/src/bin/unconvertible_type_map.rs b/ui-tests/src/bin/unconvertible_type_map.rs new file mode 100644 index 0000000..40f883b --- /dev/null +++ b/ui-tests/src/bin/unconvertible_type_map.rs @@ -0,0 +1,8 @@ +#[dynamize::dynamize] +trait Trait { +    type A: Into<String>; + +    fn a(&self) -> HashMap<Self::A>; +} + +fn main() {} diff --git a/ui-tests/src/bin/unconvertible_type_map.stderr b/ui-tests/src/bin/unconvertible_type_map.stderr new file mode 100644 index 0000000..a6833d2 --- /dev/null +++ b/ui-tests/src/bin/unconvertible_type_map.stderr @@ -0,0 +1,5 @@ +error: dynamize expects at least 2 generic type arguments for this type + --> src/bin/unconvertible_type_map.rs:5:20 +  | +5 |     fn a(&self) -> HashMap<Self::A>; +  |                    ^^^^^^^ diff --git a/ui-tests/src/bin/unconvertible_type_vec.stderr b/ui-tests/src/bin/unconvertible_type_vec.stderr index 65306f0..f966ede 100644 --- a/ui-tests/src/bin/unconvertible_type_vec.stderr +++ b/ui-tests/src/bin/unconvertible_type_vec.stderr @@ -1,5 +1,5 @@ -error: dynamize does not know how to convert this type - --> src/bin/unconvertible_type_vec.rs:5:20 +error: for Vec dynamize supports associated types only within the first 1 generic type parameters + --> src/bin/unconvertible_type_vec.rs:5:32    |  5 |     fn a(&self) -> Vec<String, Self::A>; -  |                    ^^^ +  |                                ^^^^ | 
