portrait_framework/
impl_completer.rs

1extern crate proc_macro;
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use syn::parse::Parse;
6use syn::Result;
7
8use crate::{impl_filler, subtract_items, FillImpl};
9
10/// One-line wrapper that declares a filler macro.
11///
12/// # Example
13/// ```
14/// # extern crate proc_macro;
15/// #
16/// portrait_framework::proc_macro_impl_filler!(foo, Generator);
17/// struct Generator(portrait_framework::NoArgs);
18/// impl portrait_framework::GenerateImpl for Generator {
19///     fn generate_const(
20///         &mut self,
21///         context: portrait_framework::ImplContext,
22///         item: &syn::TraitItemConst,
23///     ) -> syn::Result<syn::ImplItemConst> {
24///         todo!()
25///     }
26///     fn generate_fn(
27///         &mut self,
28///         context: portrait_framework::ImplContext,
29///         item: &syn::TraitItemFn,
30///     ) -> syn::Result<syn::ImplItemFn> {
31///         todo!()
32///     }
33///     fn generate_type(
34///         &mut self,
35///         context: portrait_framework::ImplContext,
36///         item: &syn::TraitItemType,
37///     ) -> syn::Result<syn::ImplItemType> {
38///         todo!()
39///     }
40/// }
41/// ```
42///
43/// This declares a filler macro called `foo`,
44/// where each missing item is generated by calling the corresponding funciton.
45#[macro_export]
46macro_rules! proc_macro_impl_filler {
47    ($ident:ident, $generator:path) => {
48        pub fn $ident(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
49            portrait_framework::completer_impl_filler(input, $generator)
50        }
51    };
52}
53
54/// Shorthand from [`fn@impl_filler`] to [`complete_impl`] ([`proc_macro`] version).
55pub fn completer_impl_filler<ArgsT: Parse, GeneratorT: GenerateImpl>(
56    input: proc_macro::TokenStream,
57    ctor: fn(ArgsT) -> GeneratorT,
58) -> proc_macro::TokenStream {
59    completer_impl_filler2(input.into(), ctor).unwrap_or_else(syn::Error::into_compile_error).into()
60}
61
62/// Shorthand from [`fn@impl_filler`] to [`complete_impl`] ([`proc_macro2`] version).
63pub fn completer_impl_filler2<ArgsT: Parse, GeneratorT: GenerateImpl>(
64    input: TokenStream,
65    ctor: fn(ArgsT) -> GeneratorT,
66) -> Result<TokenStream> {
67    struct Filler<GenerateT, ArgsT>(fn(ArgsT) -> GenerateT);
68
69    impl<GenerateT: GenerateImpl, ArgsT: Parse> FillImpl for Filler<GenerateT, ArgsT> {
70        type Args = ArgsT;
71
72        fn fill(
73            self,
74            portrait: &[syn::TraitItem],
75            args: Self::Args,
76            item_impl: &syn::ItemImpl,
77        ) -> Result<TokenStream> {
78            let tokens = complete_impl(portrait, item_impl, self.0(args))?;
79            Ok(quote!(#tokens))
80        }
81    }
82
83    impl_filler(input, Filler(ctor))
84}
85
86/// Invokes the generator on each unimplemented item
87/// and returns a clone of `impl_block` with the generated items.
88pub fn complete_impl(
89    trait_items: &[syn::TraitItem],
90    impl_block: &syn::ItemImpl,
91    mut generator: impl GenerateImpl,
92) -> syn::Result<syn::ItemImpl> {
93    let mut output = impl_block.clone();
94
95    let ctx = ImplContext { all_trait_items: trait_items, impl_block };
96
97    let items = subtract_items(trait_items, impl_block)?;
98    for trait_item in items.consts.values() {
99        let impl_item = generator.generate_const(ImplContext { ..ctx }, trait_item)?;
100        output.items.push(syn::ImplItem::Const(impl_item));
101    }
102    for trait_item in items.fns.values() {
103        let impl_item = generator.generate_fn(ImplContext { ..ctx }, trait_item)?;
104        output.items.push(syn::ImplItem::Fn(impl_item));
105    }
106    for trait_item in items.types.values() {
107        let impl_item = generator.generate_type(ImplContext { ..ctx }, trait_item)?;
108        output.items.push(syn::ImplItem::Type(impl_item));
109    }
110
111    Ok(output)
112}
113
114/// Available context parameters passed to generators.
115#[non_exhaustive]
116pub struct ImplContext<'t> {
117    /// All known trait items in the portrait.
118    pub all_trait_items: &'t [syn::TraitItem],
119    /// The input impl block.
120    pub impl_block:      &'t syn::ItemImpl,
121}
122
123/// Generates missing items.
124pub trait GenerateImpl {
125    /// Implements an associated constant.
126    fn generate_const(
127        &mut self,
128        ctx: ImplContext,
129        item: &syn::TraitItemConst,
130    ) -> Result<syn::ImplItemConst>;
131
132    /// Implements an associated function.
133    fn generate_fn(&mut self, ctx: ImplContext, item: &syn::TraitItemFn)
134        -> Result<syn::ImplItemFn>;
135
136    /// Implements an associated type.
137    fn generate_type(
138        &mut self,
139        ctx: ImplContext,
140        item: &syn::TraitItemType,
141    ) -> Result<syn::ImplItemType>;
142}