bones_utils_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote, quote_spanned, spanned::Spanned};
3
4/// Helper macro to bail out of the macro with a compile error.
5macro_rules! throw {
6    ($hasSpan:expr, $err:literal) => {
7        let span = $hasSpan.__span();
8        return quote_spanned!(span =>
9            compile_error!($err);
10        ).into();
11    };
12}
13
14/// Returns whether or not the passed-in attribute is a simple attribute with no arguments with a
15/// name that matches `name`.
16fn is_simple_named_attr(attr: &venial::Attribute, name: &str) -> bool {
17    attr.get_single_path_segment() == Some(&format_ident!("{name}"))
18        && attr.get_value_tokens().is_empty()
19}
20
21/// Derive macro for deriving [`Deref`] on structs with one field.
22#[proc_macro_derive(Deref, attributes(deref))]
23pub fn derive_deref(input: TokenStream) -> TokenStream {
24    let input = venial::parse_declaration(input.into()).unwrap();
25
26    if let Some(s) = input.as_struct() {
27        let name = &s.name;
28        let params = &s.generic_params;
29
30        match &s.fields {
31            venial::StructFields::Tuple(tuple) => {
32                if tuple.fields.len() != 1 {
33                    throw!(tuple, "May only derive Deref for structs with one field.");
34                }
35
36                let deref_type = &tuple.fields[0].0.ty;
37
38                quote! {
39                    impl #params ::std::ops::Deref for #name #params {
40                        type Target = #deref_type;
41
42                        fn deref(&self) -> &Self::Target {
43                            &self.0
44                        }
45                    }
46                }
47                .into()
48            }
49            venial::StructFields::Named(named) => {
50                let (deref_type, field_name) = if named.fields.is_empty() {
51                    throw!(named, "May not derive Deref for struct without fields");
52                } else if named.fields.len() > 1 {
53                    let mut info = None;
54                    for (field, _) in named.fields.iter() {
55                        for attr in &field.attributes {
56                            if is_simple_named_attr(attr, "deref") {
57                                if info.is_some() {
58                                    throw!(attr, "Only one field may have the #[deref] attribute");
59                                } else {
60                                    info = Some((&field.ty, &field.name));
61                                }
62                            }
63                        }
64                    }
65
66                    if let Some(info) = info {
67                        info
68                    } else {
69                        throw!(
70                            named,
71                            "One field must be annotated with a #[deref] attribute"
72                        );
73                    }
74                } else {
75                    (&named.fields[0].0.ty, &named.fields[0].0.name)
76                };
77
78                quote! {
79                    impl #params ::std::ops::Deref for #name #params {
80                        type Target = #deref_type;
81
82                        fn deref(&self) -> &Self::Target {
83                            &self.#field_name
84                        }
85                    }
86                }
87                .into()
88            }
89            venial::StructFields::Unit => {
90                throw!(s, "Cannot derive Deref on anything but structs.");
91            }
92        }
93    } else {
94        throw!(input, "Cannot derive Deref on anything but structs.");
95    }
96}
97
98/// Derive macro for deriving [`DerefMut`] on structs with one field.
99#[proc_macro_derive(DerefMut, attributes(deref))]
100pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
101    let input = venial::parse_declaration(input.into()).unwrap();
102
103    if let Some(s) = input.as_struct() {
104        let name = &s.name;
105        let params = &s.generic_params;
106
107        match &s.fields {
108            venial::StructFields::Tuple(tuple) => {
109                if tuple.fields.len() != 1 {
110                    throw!(
111                        tuple,
112                        "May only derive DerefMut for structs with one field."
113                    );
114                }
115
116                quote! {
117                    impl #params std::ops::DerefMut for #name #params {
118                        fn deref_mut(&mut self) -> &mut Self::Target {
119                            &mut self.0
120                        }
121                    }
122                }
123                .into()
124            }
125            venial::StructFields::Named(named) => {
126                let field_name = if named.fields.is_empty() {
127                    throw!(named, "May not derive Deref for struct without fields");
128                } else if named.fields.len() > 1 {
129                    let mut info = None;
130                    for (field, _) in named.fields.iter() {
131                        for attr in &field.attributes {
132                            if is_simple_named_attr(attr, "deref") {
133                                if info.is_some() {
134                                    throw!(attr, "Only one field may have the #[deref] attribute");
135                                } else {
136                                    info = Some(&field.name);
137                                }
138                            }
139                        }
140                    }
141
142                    if let Some(name) = info {
143                        name
144                    } else {
145                        throw!(
146                            named,
147                            "One field must be annotated with a #[deref] attribute"
148                        );
149                    }
150                } else {
151                    &named.fields[0].0.name
152                };
153
154                quote! {
155                    impl #params std::ops::DerefMut for #name #params {
156                        fn deref_mut(&mut self) -> &mut Self::Target {
157                            &mut self.#field_name
158                        }
159                    }
160                }
161                .into()
162            }
163            venial::StructFields::Unit => {
164                throw!(s, "Cannot derive DerefMut on anything but structs.");
165            }
166        }
167    } else {
168        throw!(input, "Cannot derive DerefMut on anything but structs.");
169    }
170}