Skip to content

Commit c1c531e

Browse files
committed
Create trait variant
1 parent f389523 commit c1c531e

File tree

1 file changed

+79
-6
lines changed

1 file changed

+79
-6
lines changed

trait_transformer/src/variant.rs

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9+
use std::iter;
10+
911
use proc_macro2::TokenStream;
1012
use quote::quote;
1113
use syn::{
1214
parse::{Parse, ParseStream},
1315
parse_macro_input,
1416
punctuated::Punctuated,
15-
token::Comma,
16-
Ident, ItemTrait, Path, Result, ReturnType, Token, TraitBound, TraitBoundModifier, TraitItem,
17-
Type,
17+
token::Plus,
18+
Ident, ItemTrait, Result, ReturnType, Signature, Token, TraitBound, TraitItem, TraitItemFn,
19+
Type, TypeImplTrait, TypeParamBound,
1820
};
1921

2022
struct Attrs {
@@ -33,15 +35,15 @@ struct Variant {
3335
name: Ident,
3436
#[allow(unused)]
3537
colon: Token![:],
36-
supertrait: Path,
38+
bounds: Punctuated<TraitBound, Plus>,
3739
}
3840

3941
impl Parse for Variant {
4042
fn parse(input: ParseStream) -> Result<Self> {
4143
Ok(Self {
4244
name: input.parse()?,
4345
colon: input.parse()?,
44-
supertrait: input.parse()?,
46+
bounds: input.parse_terminated(TraitBound::parse, Token![+])?,
4547
})
4648
}
4749
}
@@ -53,5 +55,76 @@ pub fn variant(
5355
let attrs = parse_macro_input!(attr as Attrs);
5456
let item = parse_macro_input!(item as ItemTrait);
5557

56-
quote! {}.into()
58+
let variant = make_variant(&attrs, &item);
59+
let output = quote! {
60+
#item
61+
#variant
62+
};
63+
64+
output.into()
65+
}
66+
67+
fn make_variant(attrs: &Attrs, tr: &ItemTrait) -> TokenStream {
68+
let Variant {
69+
ref name,
70+
colon: _,
71+
ref bounds,
72+
} = attrs.variant;
73+
let bounds: Vec<_> = bounds
74+
.into_iter()
75+
.map(|b| TypeParamBound::Trait(b.clone()))
76+
.collect();
77+
let variant = ItemTrait {
78+
ident: name.clone(),
79+
supertraits: tr.supertraits.iter().chain(&bounds).cloned().collect(),
80+
items: tr
81+
.items
82+
.iter()
83+
.map(|item| transform_item(item, &bounds))
84+
.collect(),
85+
..tr.clone()
86+
};
87+
quote! { #variant }
88+
}
89+
90+
fn transform_item(item: &TraitItem, bounds: &Vec<TypeParamBound>) -> TraitItem {
91+
let TraitItem::Fn(fn_item @ TraitItemFn { sig, .. }) = item else {
92+
return item.clone();
93+
};
94+
let (arrow, output) = if sig.asyncness.is_some() {
95+
let orig = match &sig.output {
96+
ReturnType::Default => quote! { () },
97+
ReturnType::Type(_, ty) => quote! { #ty },
98+
};
99+
let future = syn::parse2(quote! { ::core::future::Future<Output = #orig> }).unwrap();
100+
let ty = Type::ImplTrait(TypeImplTrait {
101+
impl_token: syn::parse2(quote! { impl }).unwrap(),
102+
bounds: iter::once(TypeParamBound::Trait(future))
103+
.chain(bounds.iter().cloned())
104+
.collect(),
105+
});
106+
(syn::parse2(quote! { -> }).unwrap(), ty)
107+
} else {
108+
match &sig.output {
109+
ReturnType::Type(arrow, ty) => match &**ty {
110+
Type::ImplTrait(it) => {
111+
let ty = Type::ImplTrait(TypeImplTrait {
112+
impl_token: it.impl_token.clone(),
113+
bounds: it.bounds.iter().chain(bounds).cloned().collect(),
114+
});
115+
(arrow.clone(), ty)
116+
}
117+
_ => return item.clone(),
118+
},
119+
ReturnType::Default => return item.clone(),
120+
}
121+
};
122+
TraitItem::Fn(TraitItemFn {
123+
sig: Signature {
124+
asyncness: None,
125+
output: ReturnType::Type(arrow, Box::new(output)),
126+
..sig.clone()
127+
},
128+
..fn_item.clone()
129+
})
57130
}

0 commit comments

Comments
 (0)