natural_derive/
lib.rs

1//! Proc macros for naturally deriving basic trait impls for new types, i.e. respecting the
2//! structure and semantics of the inner type.
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse, Data, DataStruct, DeriveInput, Field, Fields, FieldsUnnamed, Ident};
7
8/// Derive macro generating an impl with a associated `new` function for a new type.
9#[proc_macro_derive(New)]
10pub fn derive_new(input: TokenStream) -> TokenStream {
11    for_new_type(&parse(input).unwrap(), "New", |name, field| {
12        let ty = &field.ty;
13        quote! {
14            impl #name {
15                pub fn new(value: #ty) -> Self {
16                    Self(value)
17                }
18            }
19        }
20        .into()
21    })
22}
23
24/// Derive macro generating an impl with a associated `inner` function for a new type.
25#[proc_macro_derive(Inner)]
26pub fn derive_inner(input: TokenStream) -> TokenStream {
27    for_new_type(&parse(input).unwrap(), "Inner", |name, field| {
28        let ty = &field.ty;
29        quote! {
30            impl #name {
31                pub fn inner(&self) -> &#ty {
32                    &self.0
33                }
34            }
35        }
36        .into()
37    })
38}
39
40/// Derive macro generating an impl of the trait `std::convert::From` for a new type.
41#[proc_macro_derive(From)]
42pub fn derive_from(input: TokenStream) -> TokenStream {
43    for_new_type(&parse(input).unwrap(), "From", |name, field| {
44        let ty = &field.ty;
45        quote! {
46            impl std::convert::From<#ty> for #name {
47                fn from(value: #ty) -> Self {
48                    Self(value)
49                }
50            }
51        }
52        .into()
53    })
54}
55
56/// Derive macro generating an impl of the trait `std::ops::Add` for a new type.
57#[proc_macro_derive(Add)]
58pub fn derive_add(input: TokenStream) -> TokenStream {
59    for_new_type(&parse(input).unwrap(), "Add", |name, _| {
60        quote! {
61            impl std::ops::Add for #name {
62                type Output = Self;
63                fn add(self, rhs: Self) -> Self::Output {
64                    Self(self.0 + rhs.0)
65                }
66            }
67        }
68        .into()
69    })
70}
71
72/// Derive macro generating an impl of the trait `std::ops::AddAssign` for a new type.
73#[proc_macro_derive(AddAssign)]
74pub fn derive_add_assign(input: TokenStream) -> TokenStream {
75    for_new_type(&parse(input).unwrap(), "AddAssign", |name, _| {
76        quote! {
77            impl std::ops::AddAssign for #name {
78                fn add_assign(&mut self, rhs: Self) {
79                    self.0 += rhs.0;
80                }
81            }
82        }
83        .into()
84    })
85}
86
87/// Derive macro generating an impl of the trait `std::ops::Sub` for a new type.
88#[proc_macro_derive(Sub)]
89pub fn derive_sub(input: TokenStream) -> TokenStream {
90    for_new_type(&parse(input).unwrap(), "Sub", |name, _| {
91        quote! {
92            impl std::ops::Sub for #name {
93                type Output = Self;
94                fn sub(self, rhs: Self) -> Self::Output {
95                    Self(self.0 - rhs.0)
96                }
97            }
98        }
99        .into()
100    })
101}
102
103/// Derive macro generating an impl of the trait `std::ops::SubAssign` for a new type.
104#[proc_macro_derive(SubAssign)]
105pub fn derive_sub_assign(input: TokenStream) -> TokenStream {
106    for_new_type(&parse(input).unwrap(), "SubAssign", |name, _| {
107        quote! {
108            impl std::ops::SubAssign for #name {
109                fn sub_assign(&mut self, rhs: Self) {
110                    self.0 -= rhs.0;
111                }
112            }
113        }
114        .into()
115    })
116}
117
118/// Derive macro generating an impl of the trait `std::ops::Mul` for a new type.
119#[proc_macro_derive(Mul)]
120pub fn derive_mul(input: TokenStream) -> TokenStream {
121    for_new_type(&parse(input).unwrap(), "Mul", |name, _| {
122        quote! {
123            impl std::ops::Mul for #name {
124                type Output = Self;
125                fn mul(self, rhs: Self) -> Self::Output {
126                    Self(self.0 * rhs.0)
127                }
128            }
129        }
130        .into()
131    })
132}
133
134/// Derive macro generating an impl of the trait `std::ops::MulAssign` for a new type.
135#[proc_macro_derive(MulAssign)]
136pub fn derive_mul_assign(input: TokenStream) -> TokenStream {
137    for_new_type(&parse(input).unwrap(), "MulAssign", |name, _| {
138        quote! {
139            impl std::ops::MulAssign for #name {
140                fn mul_assign(&mut self, rhs: Self) {
141                    self.0 *= rhs.0;
142                }
143            }
144        }
145        .into()
146    })
147}
148
149/// Derive macro generating an impl of the trait `std::ops::Mul` for a new type.
150#[proc_macro_derive(MulScalar)]
151pub fn derive_mul_scalar(input: TokenStream) -> TokenStream {
152    for_new_type(&parse(input).unwrap(), "Mul", |name, field| {
153        let ty = &field.ty;
154        quote! {
155            impl std::ops::Mul<#ty> for #name {
156                type Output = Self;
157                fn mul(self, rhs: #ty) -> Self::Output {
158                    Self(self.0 * rhs)
159                }
160            }
161        }
162        .into()
163    })
164}
165
166/// Derive macro generating an impl of the trait `std::ops::MulAssign` for a new type.
167#[proc_macro_derive(MulAssignScalar)]
168pub fn derive_mul_assign_scalar(input: TokenStream) -> TokenStream {
169    for_new_type(&parse(input).unwrap(), "MulAssign", |name, field| {
170        let ty = &field.ty;
171        quote! {
172            impl std::ops::MulAssign<#ty> for #name {
173                fn mul_assign(&mut self, rhs: #ty) {
174                    self.0 *= rhs;
175                }
176            }
177        }
178        .into()
179    })
180}
181
182/// Derive macro generating an impl of the trait `std::ops::Div` for a new type.
183#[proc_macro_derive(Div)]
184pub fn derive_div(input: TokenStream) -> TokenStream {
185    for_new_type(&parse(input).unwrap(), "Div", |name, _| {
186        quote! {
187            impl std::ops::Div for #name {
188                type Output = Self;
189                fn div(self, rhs: Self) -> Self::Output {
190                    Self(self.0 / rhs.0)
191                }
192            }
193        }
194        .into()
195    })
196}
197
198/// Derive macro generating an impl of the trait `std::ops::DivAssign` for a new type.
199#[proc_macro_derive(DivAssign)]
200pub fn derive_div_assign(input: TokenStream) -> TokenStream {
201    for_new_type(&parse(input).unwrap(), "DivAssign", |name, _| {
202        quote! {
203            impl std::ops::DivAssign for #name {
204                fn div_assign(&mut self, rhs: Self) {
205                    self.0 /= rhs.0;
206                }
207            }
208        }
209        .into()
210    })
211}
212
213/// Derive macro generating an impl of the trait `std::ops::Div` for a new type.
214#[proc_macro_derive(DivScalar)]
215pub fn derive_div_scalar(input: TokenStream) -> TokenStream {
216    for_new_type(&parse(input).unwrap(), "Div", |name, field| {
217        let ty = &field.ty;
218        quote! {
219            impl std::ops::Div<#ty> for #name {
220                type Output = Self;
221                fn div(self, rhs: #ty) -> Self::Output {
222                    Self(self.0 / rhs)
223                }
224            }
225        }
226        .into()
227    })
228}
229
230/// Derive macro generating an impl of the trait `std::ops::DivAssign` for a new type.
231#[proc_macro_derive(DivAssignScalar)]
232pub fn derive_div_assign_scalar(input: TokenStream) -> TokenStream {
233    for_new_type(&parse(input).unwrap(), "DivAssign", |name, field| {
234        let ty = &field.ty;
235        quote! {
236            impl std::ops::DivAssign<#ty> for #name {
237                fn div_assign(&mut self, rhs: #ty) {
238                    self.0 /= rhs;
239                }
240            }
241        }
242        .into()
243    })
244}
245
246/// Derive macro generating an impl of the trait `std::ops::Rem` for a new type.
247#[proc_macro_derive(Rem)]
248pub fn derive_rem(input: TokenStream) -> TokenStream {
249    for_new_type(&parse(input).unwrap(), "Rem", |name, _| {
250        quote! {
251            impl std::ops::Rem for #name {
252                type Output = Self;
253                fn rem(self, rhs: Self) -> Self::Output {
254                    Self(self.0 % rhs.0)
255                }
256            }
257        }
258        .into()
259    })
260}
261
262/// Derive macro generating an impl of the trait `std::ops::RemAssign` for a new type.
263#[proc_macro_derive(RemAssign)]
264pub fn derive_rem_assign(input: TokenStream) -> TokenStream {
265    for_new_type(&parse(input).unwrap(), "RemAssign", |name, _| {
266        quote! {
267            impl std::ops::RemAssign for #name {
268                fn rem_assign(&mut self, rhs: Self) {
269                    self.0 %= rhs.0;
270                }
271            }
272        }
273        .into()
274    })
275}
276
277/// Derive macro generating an impl of the trait `std::ops::Rem` for a new type.
278#[proc_macro_derive(RemScalar)]
279pub fn derive_rem_scalar(input: TokenStream) -> TokenStream {
280    for_new_type(&parse(input).unwrap(), "Rem", |name, field| {
281        let ty = &field.ty;
282        quote! {
283            impl std::ops::Rem<#ty> for #name {
284                type Output = Self;
285                fn rem(self, rhs: #ty) -> Self::Output {
286                    Self(self.0 % rhs)
287                }
288            }
289        }
290        .into()
291    })
292}
293
294/// Derive macro generating an impl of the trait `std::ops::RemAssign` for a new type.
295#[proc_macro_derive(RemAssignScalar)]
296pub fn derive_rem_assign_scalar(input: TokenStream) -> TokenStream {
297    for_new_type(&parse(input).unwrap(), "RemAssign", |name, field| {
298        let ty = &field.ty;
299        quote! {
300            impl std::ops::RemAssign<#ty> for #name {
301                fn rem_assign(&mut self, rhs: #ty) {
302                    self.0 %= rhs;
303                }
304            }
305        }
306        .into()
307    })
308}
309
310/// Invoke the given function
311fn for_new_type<F>(ast: &DeriveInput, t: &str, f: F) -> TokenStream
312where
313    F: Fn(&Ident, &Field) -> TokenStream,
314{
315    match &ast.data {
316        Data::Struct(DataStruct {
317            struct_token: _,
318            fields:
319                Fields::Unnamed(FieldsUnnamed {
320                    paren_token: _,
321                    unnamed,
322                }),
323            semi_token: _,
324        }) if unnamed.len() == 1 => {
325            let field = unnamed.first().unwrap();
326            f(&ast.ident, field)
327        }
328        _ => panic!("#[derive({t})] is only defined for newtypes (unary tuple structs)"),
329    }
330}