portrait_framework/
derive_completer.rs

1extern crate proc_macro;
2
3use proc_macro2::{Span, TokenStream};
4use quote::{quote, ToTokens};
5use syn::parse::Parse;
6use syn::spanned::Spanned;
7use syn::Result;
8
9use crate::{derive_filler, FillDerive};
10
11/// One-line wrapper that declares a filler macro.
12///
13/// # Example
14/// ```
15/// # extern crate proc_macro;
16/// #
17/// portrait_framework::proc_macro_derive_filler!(foo, Generator);
18/// struct Generator(portrait_framework::NoArgs);
19/// impl portrait_framework::GenerateDerive for Generator {
20///     fn generate_const(
21///         &mut self,
22///         context: portrait_framework::DeriveContext,
23///         item: &syn::TraitItemConst,
24///     ) -> syn::Result<syn::ImplItemConst> {
25///         todo!()
26///     }
27///     fn generate_fn(
28///         &mut self,
29///         context: portrait_framework::DeriveContext,
30///         item: &syn::TraitItemFn,
31///     ) -> syn::Result<syn::ImplItemFn> {
32///         todo!()
33///     }
34///     fn generate_type(
35///         &mut self,
36///         context: portrait_framework::DeriveContext,
37///         item: &syn::TraitItemType,
38///     ) -> syn::Result<syn::ImplItemType> {
39///         todo!()
40///     }
41/// }
42/// ```
43///
44/// This declares a filler macro called `foo`,
45/// where each missing item is generated by calling the corresponding funciton.
46#[macro_export]
47macro_rules! proc_macro_derive_filler {
48    ($ident:ident, $generator:path) => {
49        pub fn $ident(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
50            portrait_framework::completer_derive_filler(input, $generator)
51        }
52    };
53}
54
55/// Shorthand from [`fn@derive_filler`] to [`complete_derive`] ([`proc_macro`] version).
56pub fn completer_derive_filler<ArgsT: Parse, GeneratorT: GenerateDerive>(
57    input: proc_macro::TokenStream,
58    ctor: fn(ArgsT) -> GeneratorT,
59) -> proc_macro::TokenStream {
60    completer_derive_filler2(input.into(), ctor)
61        .unwrap_or_else(syn::Error::into_compile_error)
62        .into()
63}
64
65/// Shorthand from [`fn@derive_filler`] to [`complete_derive`] ([`proc_macro2`] version).
66pub fn completer_derive_filler2<ArgsT: Parse, GeneratorT: GenerateDerive>(
67    input: TokenStream,
68    ctor: fn(ArgsT) -> GeneratorT,
69) -> Result<TokenStream> {
70    struct Filler<GenerateT, ArgsT>(fn(ArgsT) -> GenerateT);
71
72    impl<GenerateT: GenerateDerive, ArgsT: Parse> FillDerive for Filler<GenerateT, ArgsT> {
73        type Args = ArgsT;
74
75        fn fill(
76            self,
77            trait_path: &syn::Path,
78            portrait: &[syn::TraitItem],
79            args: Self::Args,
80            input: &syn::DeriveInput,
81        ) -> Result<TokenStream> {
82            let tokens = complete_derive(trait_path, portrait, input, self.0(args))?;
83            Ok(quote!(#tokens))
84        }
85    }
86
87    derive_filler(input, Filler(ctor))
88}
89
90/// Invokes the generator on each unimplemented item
91/// and returns a clone of `impl_block` with the generated items.
92pub fn complete_derive(
93    trait_path: &syn::Path,
94    trait_items: &[syn::TraitItem],
95    input: &syn::DeriveInput,
96    mut generator: impl GenerateDerive,
97) -> syn::Result<syn::ItemImpl> {
98    let ctx = DeriveContext { trait_path, all_trait_items: trait_items, input };
99
100    let mut generics_params: Vec<_> = input.generics.params.iter().cloned().collect();
101    let mut generics_where: Vec<_> = input
102        .generics
103        .where_clause
104        .iter()
105        .flat_map(|clause| clause.predicates.iter().cloned())
106        .collect();
107    // TODO generic trait support (this behavior may be filler-dependent)
108    generator.extend_generics(
109        DeriveContext { ..ctx },
110        &mut generics_params,
111        &mut generics_where,
112    )?;
113
114    let self_ty = syn::Type::Path({
115        let input_ident = &input.ident;
116        let self_generics = (!input.generics.params.is_empty()).then(|| {
117            let type_param_names = input.generics.params.iter().map(|param| match param {
118                syn::GenericParam::Lifetime(lt) => lt.lifetime.to_token_stream(),
119                syn::GenericParam::Type(ty) => ty.ident.to_token_stream(),
120                syn::GenericParam::Const(const_) => const_.ident.to_token_stream(),
121            });
122
123            quote!(<#(#type_param_names),*>)
124        });
125
126        syn::parse_quote!(#input_ident #self_generics)
127    });
128
129    let mut items = Vec::new();
130    for trait_item in trait_items {
131        let item = match trait_item {
132            syn::TraitItem::Const(const_item) => {
133                syn::ImplItem::Const(generator.generate_const(DeriveContext { ..ctx }, const_item)?)
134            }
135            syn::TraitItem::Fn(fn_item) => {
136                syn::ImplItem::Fn(generator.generate_fn(DeriveContext { ..ctx }, fn_item)?)
137            }
138            syn::TraitItem::Type(type_item) => {
139                syn::ImplItem::Type(generator.generate_type(DeriveContext { ..ctx }, type_item)?)
140            }
141            _ => continue, // assume other tokens do not generate an item
142        };
143        items.push(item);
144    }
145
146    let mut attrs: Vec<_> =
147        input.attrs.iter().filter(|attr| attr.path().is_ident("cfg")).cloned().collect();
148    generator.extend_attrs(DeriveContext { ..ctx }, &mut attrs)?;
149
150    Ok(syn::ItemImpl {
151        attrs,
152        defaultness: None,
153        unsafety: None, // TODO support explicit unsafe derive
154        impl_token: syn::Token![impl](Span::call_site()),
155        generics: syn::Generics {
156            lt_token:     (!generics_params.is_empty())
157                .then(|| syn::Token![<](input.generics.span())),
158            gt_token:     (!generics_params.is_empty())
159                .then(|| syn::Token![>](input.generics.span())),
160            params:       generics_params.into_iter().collect(),
161            where_clause: (!generics_where.is_empty()).then(|| syn::WhereClause {
162                where_token: syn::Token![where](Span::call_site()),
163                predicates:  generics_where.into_iter().collect(),
164            }),
165        },
166        trait_: Some((None, trait_path.clone(), syn::Token![for](Span::call_site()))),
167        self_ty: Box::new(self_ty),
168        brace_token: syn::token::Brace::default(),
169        items,
170    })
171}
172
173/// Available context parameters passed to generators.
174#[non_exhaustive]
175pub struct DeriveContext<'t> {
176    /// The path to reference the implemented trait.
177    pub trait_path:      &'t syn::Path,
178    /// All known trait items in the portrait.
179    pub all_trait_items: &'t [syn::TraitItem],
180    /// The input struct/enum/union.
181    pub input:           &'t syn::DeriveInput,
182}
183
184/// Generates missing items.
185pub trait GenerateDerive {
186    /// Implements an associated constant.
187    fn generate_const(
188        &mut self,
189        ctx: DeriveContext,
190        item: &syn::TraitItemConst,
191    ) -> Result<syn::ImplItemConst>;
192
193    /// Implements an associated function.
194    fn generate_fn(
195        &mut self,
196        ctx: DeriveContext,
197        item: &syn::TraitItemFn,
198    ) -> Result<syn::ImplItemFn>;
199
200    /// Implements an associated type.
201    fn generate_type(
202        &mut self,
203        ctx: DeriveContext,
204        item: &syn::TraitItemType,
205    ) -> Result<syn::ImplItemType>;
206
207    /// Provides additional type bounds for the `impl` block.
208    fn extend_generics(
209        &mut self,
210        _ctx: DeriveContext,
211        _generics_params: &mut Vec<syn::GenericParam>,
212        _generics_where: &mut Vec<syn::WherePredicate>,
213    ) -> Result<()> {
214        Ok(())
215    }
216
217    /// Provides additional attributes for the `impl` block.
218    fn extend_attrs(
219        &mut self,
220        _ctx: DeriveContext,
221        _attrs: &mut Vec<syn::Attribute>,
222    ) -> Result<()> {
223        Ok(())
224    }
225}