|
6 | 6 | // option. This file may not be copied, modified, or distributed |
7 | 7 | // except according to those terms. |
8 | 8 |
|
9 | | -use proc_macro2::{Span, TokenStream}; |
10 | | -use quote::quote; |
11 | | -use syn::{ |
12 | | - parse::{Parse, ParseStream}, |
13 | | - parse_macro_input, parse_quote, |
14 | | - punctuated::Punctuated, |
15 | | - token::Comma, |
16 | | - Ident, ItemTrait, Path, PredicateType, Result, ReturnType, Token, TraitBound, |
17 | | - TraitBoundModifier, TraitItem, Type, WherePredicate, |
18 | | -}; |
19 | | - |
20 | | -struct Attrs { |
21 | | - traits: Punctuated<Transform, Comma>, |
22 | | -} |
23 | | - |
24 | | -impl Parse for Attrs { |
25 | | - fn parse(input: ParseStream) -> Result<Self> { |
26 | | - Ok(Self { |
27 | | - traits: input.parse_terminated(Transform::parse, Token![,])?, |
28 | | - }) |
29 | | - } |
30 | | -} |
31 | | - |
32 | | -struct Transform { |
33 | | - subtrait_name: Ident, |
34 | | - #[allow(dead_code)] |
35 | | - colon: Token![:], |
36 | | - subtrait: Path, |
37 | | -} |
38 | | - |
39 | | -impl Parse for Transform { |
40 | | - fn parse(input: ParseStream) -> Result<Self> { |
41 | | - Ok(Self { |
42 | | - subtrait_name: input.parse()?, |
43 | | - colon: input.parse()?, |
44 | | - subtrait: input.parse()?, |
45 | | - }) |
46 | | - } |
47 | | -} |
| 9 | +mod transformer; |
48 | 10 |
|
49 | 11 | #[proc_macro_attribute] |
50 | 12 | pub fn trait_transformer( |
51 | 13 | attr: proc_macro::TokenStream, |
52 | 14 | item: proc_macro::TokenStream, |
53 | 15 | ) -> proc_macro::TokenStream { |
54 | | - let attrs = parse_macro_input!(attr as Attrs); |
55 | | - let item = parse_macro_input!(item as ItemTrait); |
56 | | - |
57 | | - let transformed_trait = transform_trait(&attrs, &item); |
58 | | - let output = quote! { |
59 | | - #item |
60 | | - #transformed_trait |
61 | | - }; |
62 | | - |
63 | | - output.into() |
64 | | -} |
65 | | - |
66 | | -fn transform_trait(attrs: &Attrs, tr: &ItemTrait) -> TokenStream { |
67 | | - let traits = attrs |
68 | | - .traits |
69 | | - .iter() |
70 | | - .map(|attr| { |
71 | | - let subtrait = &attr.subtrait; |
72 | | - let fn_bounds = tr.items.iter().filter_map(|item| { |
73 | | - match item { |
74 | | - TraitItem::Fn(item_fn) => { |
75 | | - let is_async = item_fn.sig.asyncness.is_some(); |
76 | | - let returns_impl_trait = |
77 | | - if let ReturnType::Type(_, ty) = &item_fn.sig.output { |
78 | | - matches!(**ty, Type::ImplTrait(_)) |
79 | | - } else { |
80 | | - false |
81 | | - }; |
82 | | - |
83 | | - if is_async || returns_impl_trait { |
84 | | - let name = &item_fn.sig.ident; |
85 | | - return Some(quote! { #name(): #subtrait }); |
86 | | - } |
87 | | - } |
88 | | - _ => (), |
89 | | - } |
90 | | - None |
91 | | - }); |
92 | | - |
93 | | - let tr_ident = &tr.ident; |
94 | | - let supertrait = syn::TypeParamBound::Verbatim(quote! { |
95 | | - #tr_ident<#(#fn_bounds),*> |
96 | | - }); |
97 | | - |
98 | | - ItemTrait { |
99 | | - attrs: Vec::new(), |
100 | | - ident: attr.subtrait_name.clone(), |
101 | | - items: Vec::new(), |
102 | | - supertraits: Punctuated::from_iter(vec![ |
103 | | - supertrait, |
104 | | - syn::TypeParamBound::Trait(TraitBound { |
105 | | - paren_token: None, |
106 | | - modifier: TraitBoundModifier::None, |
107 | | - lifetimes: None, |
108 | | - path: attr.subtrait.clone(), |
109 | | - }), |
110 | | - ].into_iter()), |
111 | | - ..tr.clone() |
112 | | - } |
113 | | - }) |
114 | | - .collect::<Vec<_>>(); |
115 | | - |
116 | | - quote! { #(#traits)* } |
| 16 | + transformer::trait_transformer(attr, item) |
117 | 17 | } |
0 commit comments