impl_for/
lib.rs

1use std::{collections::HashMap, mem};
2
3use darling::{export::NestedMeta, util::PathList, FromMeta, ToTokens};
4use proc_macro2::Ident;
5use replace_types::{ReplaceTypes, VisitMut};
6use syn::{parse_macro_input, Attribute, ItemImpl, Path, TypePath};
7
8fn parse_substitutions(
9    nested: impl AsRef<[NestedMeta]>,
10) -> darling::Result<HashMap<TypePath, TypePath>> {
11    let substitutions = HashMap::<Ident, TypePath>::from_list(nested.as_ref())?;
12
13    let substitutions: HashMap<TypePath, TypePath> = substitutions
14        .into_iter()
15        .map(|(ident, type_path)| {
16            (
17                TypePath {
18                    qself: None,
19                    path: Path::from(ident),
20                },
21                type_path,
22            )
23        })
24        .collect();
25
26    Ok(substitutions)
27}
28
29/// Repeat an implementation with type substitutions
30///
31/// ## Example
32///
33/// ```
34/// use impl_for::impl_for;
35///
36/// pub trait IntoBytes {
37///     fn into_bytes(self) -> Vec<u8>;
38/// }
39///
40/// #[impl_for(T = "i8")]
41/// #[impl_for(T = "u8")]
42/// #[impl_for(T = "i16")]
43/// #[impl_for(T = "u16")]
44/// #[impl_for(T = "i32")]
45/// #[impl_for(T = "u32")]
46/// #[impl_for(T = "i64")]
47/// #[impl_for(T = "u64")]
48/// #[impl_for(T = "isize")]
49/// #[impl_for(T = "usize")]
50/// impl IntoBytes for T {
51///     fn into_bytes(self) -> Vec<u8> {
52///         let mut buf = ::itoa::Buffer::new();
53///         let s = buf.format(self);
54///         s.as_bytes().to_vec()
55///     }
56/// }
57/// ```
58#[proc_macro_attribute]
59pub fn impl_for(
60    args: proc_macro::TokenStream,
61    input: proc_macro::TokenStream,
62) -> proc_macro::TokenStream {
63    let mut input = parse_macro_input!(input as ItemImpl);
64
65    let mut errors: Vec<darling::Error> = Vec::new();
66    let mut substitutions_list: Vec<HashMap<TypePath, TypePath>> = Vec::new();
67
68    match NestedMeta::parse_meta_list(args.into())
69        .map_err(darling::Error::from)
70        .and_then(parse_substitutions)
71    {
72        Ok(substitutions) => {
73            substitutions_list.push(substitutions);
74        }
75        Err(err) => {
76            errors.push(err);
77        }
78    }
79
80    let mut attrs: Vec<Attribute> = Vec::new();
81
82    let input_attrs = mem::take(&mut input.attrs);
83
84    for attr in input_attrs.into_iter() {
85        if attr.path().is_ident("impl_for") {
86            match attr.meta.require_list() {
87                Ok(list) => {
88                    match NestedMeta::parse_meta_list(list.tokens.to_owned())
89                        .map_err(darling::Error::from)
90                        .and_then(parse_substitutions)
91                    {
92                        Ok(substitutions) => {
93                            substitutions_list.push(substitutions);
94                        }
95                        Err(err) => {
96                            errors.push(err);
97                        }
98                    }
99                }
100                Err(err) => {
101                    errors.push(err.into());
102                }
103            }
104        } else {
105            attrs.push(attr);
106        }
107    }
108
109    if !errors.is_empty() {
110        return darling::Error::multiple(errors).write_errors().into();
111    }
112
113    input.attrs = attrs;
114
115    substitutions_list
116        .into_iter()
117        .map(|substitutions| {
118            let mut item_impl = input.clone();
119            ReplaceTypes::new(substitutions).visit_item_impl_mut(&mut item_impl);
120            proc_macro::TokenStream::from(item_impl.into_token_stream())
121        })
122        .collect::<proc_macro::TokenStream>()
123}
124
125/// Repeat an implementation for each type with `T` replaced
126///
127/// ## Example
128///
129/// ```
130/// use impl_for::impl_for_each;
131///
132/// pub trait IntoBytes {
133///     fn into_bytes(self) -> Vec<u8>;
134/// }
135///
136/// #[impl_for_each(i8, u8, i16, u16, i32, u32, i64, isize, usize)]
137/// impl IntoBytes for T {
138///     fn into_bytes(self) -> Vec<u8> {
139///         let mut buf = ::itoa::Buffer::new();
140///         let s = buf.format(self);
141///         s.as_bytes().to_vec()
142///     }
143/// }
144/// ```
145#[proc_macro_attribute]
146pub fn impl_for_each(
147    args: proc_macro::TokenStream,
148    input: proc_macro::TokenStream,
149) -> proc_macro::TokenStream {
150    let input = parse_macro_input!(input as ItemImpl);
151
152    let substitutions_list: Vec<HashMap<TypePath, TypePath>> =
153        match NestedMeta::parse_meta_list(args.into())
154            .map_err(darling::Error::from)
155            .and_then(|meta_list| PathList::from_list(meta_list.as_slice()))
156        {
157            Ok(substitutions) => {
158                let t_type = TypePath {
159                    qself: None,
160                    path: Path::from(Ident::from_string("T").unwrap()),
161                };
162
163                substitutions
164                    .iter()
165                    .map(|path| {
166                        HashMap::<TypePath, TypePath>::from(
167                            [(
168                                t_type.clone(),
169                                TypePath {
170                                    qself: None,
171                                    path: path.to_owned(),
172                                },
173                            ); 1],
174                        )
175                    })
176                    .collect()
177            }
178            Err(err) => {
179                return err.write_errors().into();
180            }
181        };
182
183    substitutions_list
184        .into_iter()
185        .map(|substitutions| {
186            let mut item_impl = input.clone();
187            ReplaceTypes::new(substitutions).visit_item_impl_mut(&mut item_impl);
188            proc_macro::TokenStream::from(item_impl.into_token_stream())
189        })
190        .collect::<proc_macro::TokenStream>()
191}