Skip to main content

impl_tools_lib/
split_impl.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! `#[split_impl]`
7
8use crate::utils::{self, PathAsStr, copy_non_doc_attrs};
9use proc_macro_error3::emit_error;
10use proc_macro2::TokenStream;
11use quote::quote;
12use syn::{Generics, ImplItem, ItemTrait, Token, TraitItem, Type, parse_quote};
13
14/// `#[split_impl]` attribute
15pub struct SplitImpl {
16    for_: Token![for],
17    generics: Generics,
18    target: Box<Type>,
19}
20
21mod parsing {
22    use super::*;
23    use syn::parse::{Parse, ParseStream, Result};
24
25    impl Parse for SplitImpl {
26        fn parse(input: ParseStream) -> Result<Self> {
27            let for_ = input.parse::<Token![for]>()?;
28            let mut generics = if input.peek(Token![<]) {
29                input.parse()?
30            } else {
31                Generics::default()
32            };
33            let target = input.parse()?;
34            if input.peek(Token![where]) {
35                generics.where_clause = Some(input.parse()?);
36            }
37
38            Ok(SplitImpl {
39                for_,
40                generics,
41                target,
42            })
43        }
44    }
45}
46
47impl SplitImpl {
48    /// Process input
49    pub fn process(self, mut trait_: ItemTrait) -> TokenStream {
50        let mut attrs = Vec::with_capacity(trait_.attrs.len());
51        for attr in &trait_.attrs {
52            if utils::propegate_attr_to_impl(attr) {
53                attrs.push(attr.clone());
54            }
55        }
56
57        let mut items = Vec::with_capacity(trait_.items.len());
58        for item in trait_.items.iter_mut() {
59            match item {
60                TraitItem::Const(item) => {
61                    let Some((eq_token, expr)) = item.default.take() else {
62                        emit_error!(item, "definition not found");
63                        continue;
64                    };
65
66                    items.push(ImplItem::Const(syn::ImplItemConst {
67                        attrs: copy_non_doc_attrs(&item.attrs),
68                        vis: syn::Visibility::Inherited,
69                        defaultness: None,
70                        const_token: item.const_token,
71                        ident: item.ident.clone(),
72                        generics: item.generics.clone(),
73                        colon_token: item.colon_token,
74                        ty: item.ty.clone(),
75                        eq_token,
76                        expr,
77                        semi_token: item.semi_token,
78                    }));
79                }
80                TraitItem::Fn(item) => {
81                    let Some(block) = item.default.take() else {
82                        emit_error!(item, "definition not found");
83                        continue;
84                    };
85
86                    items.push(ImplItem::Fn(syn::ImplItemFn {
87                        attrs: copy_non_doc_attrs(&item.attrs),
88                        vis: syn::Visibility::Inherited,
89                        defaultness: None,
90                        sig: item.sig.clone(),
91                        block,
92                    }));
93
94                    item.attrs.retain(|attr| attr.path_as_string() != "inline");
95                }
96                TraitItem::Type(item) => {
97                    let Some((eq_token, ty)) = item.default.take() else {
98                        emit_error!(item, "definition not found");
99                        continue;
100                    };
101
102                    items.push(ImplItem::Type(syn::ImplItemType {
103                        attrs: copy_non_doc_attrs(&item.attrs),
104                        vis: syn::Visibility::Inherited,
105                        defaultness: None,
106                        type_token: item.type_token,
107                        ident: item.ident.clone(),
108                        generics: item.generics.clone(),
109                        eq_token,
110                        ty,
111                        semi_token: item.semi_token,
112                    }));
113                }
114                other => emit_error!(other, "unsupported trait item"),
115            }
116        }
117
118        let mut generics = self.generics;
119        utils::extend_generics(&mut generics, &trait_.generics);
120
121        let ident = &trait_.ident;
122        let (_, ty_generics, _) = trait_.generics.split_for_impl();
123        let path = parse_quote! { #ident #ty_generics };
124
125        let impl_ = syn::ItemImpl {
126            attrs,
127            defaultness: None,
128            unsafety: None,
129            impl_token: Default::default(),
130            generics,
131            trait_: Some((None, path, self.for_)),
132            self_ty: self.target,
133            brace_token: Default::default(),
134            items,
135        };
136
137        quote! { #trait_ #impl_ }
138    }
139}