aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2021-11-25 10:30:54 +0100
committerMartin Fischer <martin@push-f.com>2021-11-26 11:45:53 +0100
commit7489a3c2246e7ea2483446dd2ed3fdbfaf462c1a (patch)
treefc2670f56f7cc16567eb10d88f9439abb9098298 /src
parent43950edc4f26a07055fb917fa8bbb262276e2a08 (diff)
introduce #[convert] attribute
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs15
-rw-r--r--src/parse_assoc_type.rs8
-rw-r--r--src/parse_attrs.rs59
-rw-r--r--src/trait_sig.rs9
-rw-r--r--src/transform.rs16
5 files changed, 89 insertions, 18 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 017a58b..785827a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -93,9 +93,10 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream {
}
let mut type_converter = TypeConverter {
- collections: method_attrs.collections,
- assoc_type_conversions: HashMap::new(),
trait_ident: original_trait.ident.clone(),
+ assoc_type_conversions: HashMap::new(),
+ collections: method_attrs.collections,
+ type_conversions: method_attrs.type_conversions,
};
for item in &original_trait.items {
@@ -122,9 +123,10 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream {
let mut objectifiable_methods: Vec<(Signature, SignatureChanges)> = Vec::new();
- for item in &original_trait.items {
+ for item in &mut original_trait.items {
if let TraitItem::Method(method) = item {
let mut signature = method.sig.clone();
+
match convert_trait_signature(&mut signature, &type_converter) {
Ok(parsed_method) => objectifiable_methods.push((signature, parsed_method)),
Err((span, err)) => match err {
@@ -169,7 +171,7 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream {
return abort!(span, "dynamize does not support qualified associated types")
}
MethodError::Transform(TransformError::SelfQualifiedAsOtherTrait) => {
- return abort!(span, "dynamize does not support Self qualified as another trait")
+ return abort!(span, "dynamize does not know how to convert this type (you can tell it with #[convert])")
}
MethodError::UnconvertedAssocType => {
return abort!(span, "dynamize does not support associated types here")
@@ -471,6 +473,11 @@ impl TypeTransform {
quote! {#arg.into_iter().map(|(#(#idents),*)| (#(#transforms),*)).collect()}
}
TypeTransform::NoOp => arg,
+ TypeTransform::Verbatim(convert) => {
+ let ident = &convert.ident;
+ let block = &convert.block;
+ quote! { {let #ident = #arg; #block }}
+ }
}
}
}
diff --git a/src/parse_assoc_type.rs b/src/parse_assoc_type.rs
index adde237..e52dd14 100644
--- a/src/parse_assoc_type.rs
+++ b/src/parse_assoc_type.rs
@@ -30,12 +30,12 @@ impl ToTokens for BoxType {
}
}
-pub enum DestType<'a> {
- Into(&'a Type),
+pub enum DestType {
+ Into(Type),
Box(BoxType),
}
-impl DestType<'_> {
+impl DestType {
pub fn get_dest(&self) -> Type {
match self {
DestType::Into(ty) => (*ty).clone(),
@@ -78,7 +78,7 @@ pub fn parse_assoc_type(
));
}
- return Ok((&assoc_type.ident, DestType::Into(into_type)));
+ return Ok((&assoc_type.ident, DestType::Into(into_type.clone())));
}
}
}
diff --git a/src/parse_attrs.rs b/src/parse_attrs.rs
index d9725ff..06a5123 100644
--- a/src/parse_attrs.rs
+++ b/src/parse_attrs.rs
@@ -1,8 +1,14 @@
-use std::collections::{HashMap, HashSet};
+use std::{
+ collections::{HashMap, HashSet},
+ rc::Rc,
+};
use proc_macro2::{Group, TokenStream};
use quote::quote;
-use syn::{parenthesized, parse::Parse, Attribute, Error, Ident, LitInt, Token};
+use syn::{
+ braced, parenthesized, parse::Parse, spanned::Spanned, Attribute, Error, Ident, LitInt, Token,
+ Type,
+};
struct Collection {
pub id: Ident,
@@ -32,8 +38,9 @@ impl Parse for Collection {
pub struct TraitAttrs {
pub blanket_impl_attrs: Vec<TokenStream>,
pub dyn_trait_attrs: Vec<TokenStream>,
- pub collections: HashMap<Ident, usize>,
pub dynamized_supertraits: HashSet<Ident>,
+ pub collections: HashMap<Ident, usize>,
+ pub type_conversions: HashMap<Type, Rc<Convert>>,
}
struct Dynamized {
@@ -82,6 +89,21 @@ impl TraitAttrs {
};
let tokens = group.stream();
parsed.dyn_trait_attrs.push(quote! {#[#tokens]});
+ } else if attrs[i].path.is_ident("convert") {
+ let attr = attrs.remove(i);
+ let convert: Convert = syn::parse2(attr.tokens)?;
+ let span = convert.original_type.span();
+
+ if parsed
+ .type_conversions
+ .insert(convert.original_type.clone(), Rc::new(convert))
+ .is_some()
+ {
+ return Err(Error::new(
+ span,
+ format_args!("conversion is defined multiple times for this type"),
+ ));
+ }
} else if attrs[i].path.is_ident("collection") {
let attr = attrs.remove(i);
let coll: Collection = syn::parse2(attr.tokens)?;
@@ -118,3 +140,34 @@ impl TraitAttrs {
Ok(parsed)
}
}
+
+#[derive(Debug, Clone)]
+pub struct Convert {
+ pub ident: Ident,
+ pub original_type: Type,
+ pub dest_type: Type,
+ pub block: TokenStream,
+}
+
+impl Parse for Convert {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+ let _: Token![=] = input.parse()?;
+ let _: Token![|] = input.parse()?;
+ let param: Ident = input.parse()?;
+ let _: Token![:] = input.parse()?;
+ let original_type: Type = input.parse()?;
+ let _: Token![|] = input.parse()?;
+ let _: Token![->] = input.parse()?;
+ let dest_type: Type = input.parse()?;
+
+ let inner;
+ braced!(inner in input);
+
+ Ok(Self {
+ ident: param,
+ original_type,
+ dest_type,
+ block: inner.parse()?,
+ })
+ }
+}
diff --git a/src/trait_sig.rs b/src/trait_sig.rs
index 5d8eddf..a1be44f 100644
--- a/src/trait_sig.rs
+++ b/src/trait_sig.rs
@@ -1,4 +1,5 @@
use std::collections::HashMap;
+use std::rc::Rc;
use proc_macro2::Span;
use syn::{
@@ -8,6 +9,7 @@ use syn::{Ident, Signature, TypeImplTrait};
use crate::match_assoc_type;
use crate::parse_assoc_type::BoxType;
+use crate::parse_attrs::Convert;
use crate::syn_utils::{iter_type, trait_bounds};
use crate::transform::{dynamize_function_bounds, TransformError, TypeConverter};
@@ -21,6 +23,7 @@ pub enum TypeTransform {
IntoIterMapCollect(Vec<TypeTransform>),
Iterator(BoxType, Box<TypeTransform>),
Result(Box<TypeTransform>, Box<TypeTransform>),
+ Verbatim(Rc<Convert>),
}
#[derive(Debug)]
@@ -83,6 +86,7 @@ pub fn convert_trait_signature(
},
ReturnType::Default => TypeTransform::NoOp,
};
+
Ok(SignatureChanges {
return_type,
type_param_transforms,
@@ -151,11 +155,12 @@ mod tests {
transform::{TransformError, TypeConverter},
};
- fn test_converter() -> TypeConverter<'static> {
+ fn test_converter() -> TypeConverter {
TypeConverter {
assoc_type_conversions: HashMap::new(),
collections: HashMap::new(),
trait_ident: format_ident!("test"),
+ type_conversions: HashMap::new(),
}
}
@@ -185,7 +190,7 @@ mod tests {
let mut type_converter = test_converter();
let ident = format_ident!("A");
let dest_inner = Type::Verbatim(quote! {Example});
- let dest = DestType::Into(&dest_inner);
+ let dest = DestType::Into(dest_inner);
type_converter.assoc_type_conversions.insert(ident, dest);
assert!(matches!(
diff --git a/src/transform.rs b/src/transform.rs
index 75138c9..ae345c8 100644
--- a/src/transform.rs
+++ b/src/transform.rs
@@ -1,4 +1,4 @@
-use std::collections::HashMap;
+use std::{collections::HashMap, rc::Rc};
use proc_macro2::Span;
use quote::quote;
@@ -10,14 +10,16 @@ use syn::{
use crate::{
filter_map_assoc_paths, match_assoc_type,
parse_assoc_type::{BoxType, DestType},
+ parse_attrs::Convert,
syn_utils::{iter_path, iter_type, type_arguments_mut},
trait_sig::{MethodError, TypeTransform},
};
-pub struct TypeConverter<'a> {
- pub assoc_type_conversions: HashMap<Ident, DestType<'a>>,
+pub struct TypeConverter {
+ pub assoc_type_conversions: HashMap<Ident, DestType>,
pub collections: HashMap<Ident, usize>,
pub trait_ident: Ident,
+ pub type_conversions: HashMap<Type, Rc<Convert>>,
}
#[derive(Debug)]
@@ -30,7 +32,7 @@ pub enum TransformError {
SelfQualifiedAsOtherTrait,
}
-impl TypeConverter<'_> {
+impl TypeConverter {
/// 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.
@@ -59,6 +61,10 @@ impl TypeConverter<'_> {
}
pub fn convert_type(&self, type_: &mut Type) -> Result<TypeTransform, (Span, TransformError)> {
+ if let Some(conv) = self.type_conversions.get(type_) {
+ *type_ = conv.dest_type.clone();
+ return Ok(TypeTransform::Verbatim(conv.clone()));
+ }
if !iter_type(type_).any(match_assoc_type) {
return Ok(TypeTransform::NoOp);
}
@@ -139,7 +145,7 @@ impl TypeConverter<'_> {
return Ok(dest_type.type_transformation());
}
- return Err((path.span(), TransformError::SelfQualifiedAsOtherTrait));
+ return Err((type_.span(), TransformError::SelfQualifiedAsOtherTrait));
}
}
} else if let Type::Path(TypePath { path, qself: None }) = type_ {