ops_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{self, parse_macro_input, Data, DeriveInput, Ident};
4
5enum Ops {
6    Add,
7    Sub,
8    Mul,
9    Div,
10    AddAssign,
11    SubAssign,
12    MulAssign,
13    DivAssign,
14}
15
16fn parse(ast: DeriveInput) -> (Ident, Vec<Ident>) {
17    let name = ast.ident;
18    let struct_fields = if let Data::Struct(struct_data) = ast.data {
19        struct_data.fields
20    } else {
21        panic!("Expected a struct!")
22    };
23
24    let mut field_names = vec![];
25
26    for field in struct_fields {
27        field_names.push(field.ident.unwrap());
28    }
29
30    (name, field_names)
31}
32
33#[proc_macro_derive(AutoAdd)]
34pub fn derive_add(input: TokenStream) -> TokenStream {
35    complete(input, Ops::Add)
36}
37
38#[proc_macro_derive(AutoSub)]
39pub fn derive_sub(input: TokenStream) -> TokenStream {
40    complete(input, Ops::Sub)
41}
42
43#[proc_macro_derive(AutoMul)]
44pub fn derive_mul(input: TokenStream) -> TokenStream {
45    complete(input, Ops::Mul)
46}
47
48#[proc_macro_derive(AutoDiv)]
49pub fn derive_div(input: TokenStream) -> TokenStream {
50    complete(input, Ops::Div)
51}
52
53#[proc_macro_derive(AutoAddAssign)]
54pub fn derive_add_assign(input: TokenStream) -> TokenStream {
55    complete(input, Ops::AddAssign)
56}
57
58#[proc_macro_derive(AutoSubAssign)]
59pub fn derive_sub_assign(input: TokenStream) -> TokenStream {
60    complete(input, Ops::SubAssign)
61}
62
63#[proc_macro_derive(AutoMulAssign)]
64pub fn derive_mul_assign(input: TokenStream) -> TokenStream {
65    complete(input, Ops::MulAssign)
66}
67
68#[proc_macro_derive(AutoDivAssign)]
69pub fn derive_div_assign(input: TokenStream) -> TokenStream {
70    complete(input, Ops::DivAssign)
71}
72
73#[proc_macro_derive(AutoNeg)]
74pub fn derive_neg(input: TokenStream) -> TokenStream {
75    let (name, fields) = parse(parse_macro_input!(input as DeriveInput));
76
77    let ret = quote! {
78        impl ::std::ops::Neg for #name {
79            type Output = Self;
80
81            fn neg(self) -> Self::Output {
82                #name {
83                    #(
84                        #fields: -self.#fields,
85                    )*
86                }
87            }
88        }
89    };
90    ret.into()
91}
92
93#[proc_macro_derive(AutoAll)]
94pub fn derive_all(input: TokenStream) -> TokenStream {
95    let (name, fields) = parse(parse_macro_input!(input as DeriveInput));
96
97    let add: proc_macro2::TokenStream = complete_internal(&name, &fields, Ops::Add).into();
98    let sub: proc_macro2::TokenStream = complete_internal(&name, &fields, Ops::Sub).into();
99    let mul: proc_macro2::TokenStream = complete_internal(&name, &fields, Ops::Mul).into();
100    let div: proc_macro2::TokenStream = complete_internal(&name, &fields, Ops::Div).into();
101    let add_assign: proc_macro2::TokenStream =
102        complete_internal(&name, &fields, Ops::AddAssign).into();
103    let sub_assign: proc_macro2::TokenStream =
104        complete_internal(&name, &fields, Ops::SubAssign).into();
105    let mul_assign: proc_macro2::TokenStream =
106        complete_internal(&name, &fields, Ops::MulAssign).into();
107    let div_assign: proc_macro2::TokenStream =
108        complete_internal(&name, &fields, Ops::DivAssign).into();
109    let ret = quote! {
110        #add
111        #sub
112        #mul
113        #div
114        #add_assign
115        #sub_assign
116        #mul_assign
117        #div_assign
118
119        impl ::std::ops::Neg for #name {
120            type Output = Self;
121
122            fn neg(self) -> Self::Output {
123                #name {
124                    #(
125                        #fields: -self.#fields,
126                    )*
127                }
128            }
129        }
130    };
131    ret.into()
132}
133
134fn complete(input: TokenStream, trait_: Ops) -> TokenStream {
135    let (name, fields) = parse(parse_macro_input!(input as DeriveInput));
136    complete_internal(&name, &fields, trait_)
137}
138
139fn complete_internal(name: &Ident, fields: &[Ident], trait_: Ops) -> TokenStream {
140    let (trait_name, func_header, operation) = match trait_ {
141        Ops::Add => (
142            quote! {Add},
143            quote! {add(self, rhs: Self) -> Self::Output},
144            quote! {+},
145        ),
146        Ops::Sub => (
147            quote! {Sub},
148            quote! {sub(self, rhs: Self) -> Self::Output},
149            quote! {-},
150        ),
151        Ops::Mul => (
152            quote! {Mul},
153            quote! {mul(self, rhs: Self) -> Self::Output},
154            quote! {*},
155        ),
156        Ops::Div => (
157            quote! {Div},
158            quote! {div(self, rhs: Self) -> Self::Output},
159            quote! {/},
160        ),
161        Ops::AddAssign => (
162            quote! {AddAssign},
163            quote! {add_assign(&mut self, rhs: Self)},
164            quote! {+},
165        ),
166        Ops::SubAssign => (
167            quote! {SubAssign},
168            quote! {sub_assign(&mut self, rhs: Self)},
169            quote! {-},
170        ),
171        Ops::MulAssign => (
172            quote! {MulAssign},
173            quote! {mul_assign(&mut self, rhs: Self)},
174            quote! {*},
175        ),
176        Ops::DivAssign => (
177            quote! {DivAssign},
178            quote! {div_assign(&mut self, rhs: Self)},
179            quote! {/},
180        ),
181    };
182
183    let output = match trait_ {
184        Ops::Add | Ops::Sub | Ops::Mul | Ops::Div => quote! {type Output = Self;},
185        _ => quote! {},
186    };
187
188    let deref = match trait_ {
189        Ops::AddAssign | Ops::SubAssign | Ops::MulAssign | Ops::DivAssign => quote! {*self = },
190        _ => quote! {},
191    };
192
193    let ret = quote! {
194        impl ::std::ops::#trait_name for #name {
195            #output
196
197            fn #func_header {
198                #deref #name {
199                    #(
200                        #fields: self.#fields #operation rhs.#fields,
201                    )*
202                }
203            }
204        }
205    };
206    ret.into()
207}