aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2021-11-19 11:49:22 +0100
committerMartin Fischer <martin@push-f.com>2021-11-19 12:09:55 +0100
commit323762d238ebb9d9b8fa65bd1290aaa39648615c (patch)
treebba4645bf2fef6307018fe9187a19687149cbfbd
parenta11255acdf3b3fac12d8f51048f0bed2c0df8a11 (diff)
if first type bound isn't Into<T> auto-box it
-rw-r--r--README.md12
-rw-r--r--src/lib.rs3
-rw-r--r--src/parse_assoc_type.rs34
-rw-r--r--src/parse_trait_sig.rs2
-rw-r--r--src/syn_utils.rs13
-rw-r--r--tests/tests.rs25
6 files changed, 82 insertions, 7 deletions
diff --git a/README.md b/README.md
index 967738e..972f82c 100644
--- a/README.md
+++ b/README.md
@@ -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);
}
diff --git a/src/lib.rs b/src/lib.rs
index 4512141..99abfdb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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()
+}