diff options
| author | Martin Fischer <martin@push-f.com> | 2021-11-19 11:49:22 +0100 | 
|---|---|---|
| committer | Martin Fischer <martin@push-f.com> | 2021-11-19 12:09:55 +0100 | 
| commit | 323762d238ebb9d9b8fa65bd1290aaa39648615c (patch) | |
| tree | bba4645bf2fef6307018fe9187a19687149cbfbd | |
| parent | a11255acdf3b3fac12d8f51048f0bed2c0df8a11 (diff) | |
if first type bound isn't Into<T> auto-box it
| -rw-r--r-- | README.md | 12 | ||||
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/parse_assoc_type.rs | 34 | ||||
| -rw-r--r-- | src/parse_trait_sig.rs | 2 | ||||
| -rw-r--r-- | src/syn_utils.rs | 13 | ||||
| -rw-r--r-- | tests/tests.rs | 25 | 
6 files changed, 82 insertions, 7 deletions
| @@ -78,6 +78,14 @@ in `E` need to be mapped with `map_err()`. Dynamize also understands  options and results, so e.g. `Result<Option<Self::Item>, Self::Error>` also  just works. +The destination type of an associated type is +determined by looking at its first trait bound: + +* `Into<T>` is mapped to `T` + +* `SomeTrait` is mapped to `Box<dyn SomeTrait>`   +  (for this `SomeTrait` of course needs to be object-safe) +  ## Dynamize supports async  Dynamize supports async out of the box. Since Rust however does not yet support @@ -90,7 +98,7 @@ async functions in traits, you'll have to additionally use another library like  #[blanket_impl_attr(async_trait)]  #[async_trait]  trait Client: Sync { -    type Error: Into<SuperError>; +    type Error: std::error::Error;      async fn get(&self, url: String) -> Result<Vec<u8>, Self::Error>;  } @@ -109,7 +117,7 @@ The following also just works:  ```rust  #[dynamize::dynamize]  trait TraitWithCallback { -    type A: Into<String>; +    type A: SomeTrait;      fn fun_with_callback<F: Fn(Self::A)>(&self, a: F);  } @@ -365,6 +365,9 @@ impl TypeTransform {      fn convert(&self, arg: proc_macro2::TokenStream) -> proc_macro2::TokenStream {          match self {              TypeTransform::Into => quote! {#arg.into()}, +            TypeTransform::Box(box_type) => { +                quote! {Box::new(#arg) as #box_type} +            }              TypeTransform::Map(opt) => {                  let inner = opt.convert(quote!(x));                  quote! {#arg.map(|x| #inner)} diff --git a/src/parse_assoc_type.rs b/src/parse_assoc_type.rs index a255455..7efc7da 100644 --- a/src/parse_assoc_type.rs +++ b/src/parse_assoc_type.rs @@ -1,9 +1,10 @@  use proc_macro2::Span; +use quote::{quote, ToTokens};  use syn::spanned::Spanned; -use syn::{GenericArgument, Ident, PathArguments, PathSegment, TraitItemType, Type}; +use syn::{GenericArgument, Ident, Path, PathArguments, PathSegment, TraitItemType, Type};  use crate::parse_trait_sig::TypeTransform; -use crate::syn_utils::{find_in_type, trait_bounds}; +use crate::syn_utils::{find_in_type, lifetime_bounds, trait_bounds};  use crate::AssocTypeMatcher;  #[derive(Debug)] @@ -13,20 +14,39 @@ pub enum AssocTypeParseError {      NoIntoBound,  } +#[derive(Debug, Clone)] +pub struct BoxType { +    trait_name: Path, +    placeholder_lifetime: bool, +} + +impl ToTokens for BoxType { +    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { +        let path = &self.trait_name; +        match self.placeholder_lifetime { +            true => tokens.extend(quote! {Box<dyn #path + '_>}), +            false => tokens.extend(quote! {Box<dyn #path>}), +        } +    } +} +  pub enum DestType<'a> {      Into(&'a Type), +    Box(BoxType),  }  impl DestType<'_> {      pub fn get_dest(&self) -> Type {          match self {              DestType::Into(ty) => (*ty).clone(), +            DestType::Box(b) => Type::Verbatim(quote!(#b)),          }      }      pub fn type_transformation(&self) -> TypeTransform {          match self {              DestType::Into(_) => TypeTransform::Into, +            DestType::Box(b) => TypeTransform::Box(b.clone()),          }      }  } @@ -34,7 +54,7 @@ impl DestType<'_> {  pub fn parse_assoc_type(      assoc_type: &TraitItemType,  ) -> Result<(&Ident, DestType), (Span, AssocTypeParseError)> { -    for bound in trait_bounds(&assoc_type.bounds) { +    if let Some(bound) = trait_bounds(&assoc_type.bounds).next() {          if let PathSegment {              ident,              arguments: PathArguments::AngleBracketed(args), @@ -59,6 +79,14 @@ pub fn parse_assoc_type(                  }              }          } +        return Ok(( +            &assoc_type.ident, +            DestType::Box(BoxType { +                trait_name: bound.path.clone(), +                placeholder_lifetime: !lifetime_bounds(&assoc_type.bounds) +                    .any(|l| l.ident == "static"), +            }), +        ));      }      Err((assoc_type.span(), AssocTypeParseError::NoIntoBound))  } diff --git a/src/parse_trait_sig.rs b/src/parse_trait_sig.rs index b7b7b3f..76402ac 100644 --- a/src/parse_trait_sig.rs +++ b/src/parse_trait_sig.rs @@ -6,6 +6,7 @@ use syn::{  };  use syn::{Ident, Signature, TypeImplTrait}; +use crate::parse_assoc_type::BoxType;  use crate::syn_utils::{find_in_type, trait_bounds, TypeMatcher};  use crate::transform::{dynamize_function_bounds, AssocTypeConversions, TransformError};  use crate::AssocTypeMatcher; @@ -14,6 +15,7 @@ use crate::AssocTypeMatcher;  pub enum TypeTransform {      NoOp,      Into, +    Box(BoxType),      Map(Box<TypeTransform>),      Result(Box<TypeTransform>, Box<TypeTransform>),  } diff --git a/src/syn_utils.rs b/src/syn_utils.rs index 4588186..61ae27f 100644 --- a/src/syn_utils.rs +++ b/src/syn_utils.rs @@ -1,6 +1,6 @@  use syn::{ -    punctuated::Punctuated, GenericArgument, Path, PathArguments, ReturnType, TraitBound, Type, -    TypeParamBound, +    punctuated::Punctuated, GenericArgument, Lifetime, Path, PathArguments, ReturnType, TraitBound, +    Type, TypeParamBound,  };  pub trait TypeMatcher<T> { @@ -21,6 +21,15 @@ pub fn trait_bounds<T>(      })  } +pub fn lifetime_bounds<T>( +    bounds: &Punctuated<TypeParamBound, T>, +) -> impl Iterator<Item = &Lifetime> { +    bounds.iter().filter_map(|b| match b { +        TypeParamBound::Trait(_) => None, +        TypeParamBound::Lifetime(l) => Some(l), +    }) +} +  pub fn find_in_type<'a, T>(t: &'a Type, matcher: &dyn TypeMatcher<T>) -> Option<&'a T> {      if let Some(ret) = matcher.match_type(t) {          return Some(ret); diff --git a/tests/tests.rs b/tests/tests.rs index 4d09847..652be48 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -152,3 +152,28 @@ trait AsyncWithCallback: Sync {      async fn test1(&self) -> Self::A;      async fn fun_with_callback<F: Fn(Self::A) + Sync + Send + 'static>(&self, a: F);  } + +#[dynamize::dynamize] +trait TypeTraitBound { +    type A: std::error::Error; + +    fn foobar(&self, text: &str) -> Result<i32, Self::A>; +} + +fn type_trait_bound_test<T: TypeTraitBound>(t: T) { +    let dyn_trait: &dyn DynTypeTraitBound = &t; +    let _: Result<i32, Box<dyn std::error::Error>> = dyn_trait.foobar("test"); +} + +#[dynamize::dynamize] +trait TypeTraitBoundStatic { +    type A: std::error::Error + 'static; + +    fn foobar(&self, text: &str) -> Result<i32, Self::A>; +} + +fn type_trait_bound_static_test( +    t: Box<dyn DynTypeTraitBoundStatic>, +) -> Option<Box<dyn std::error::Error>> { +    t.foobar("test").err() +} | 
