/home/runner/work/bare_err_tree/bare_err_tree/bare_err_tree_proc/src/errtype.rs
Line | Count | Source |
1 | | /* |
2 | | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at https://mozilla.org/MPL/2.0/. |
5 | | */ |
6 | | |
7 | | use quote::{quote, quote_spanned}; |
8 | | use syn::{spanned::Spanned, DataEnum, DataStruct, Field, Ident, Type}; |
9 | | |
10 | | #[derive(Debug)] |
11 | | pub enum ErrType { |
12 | | /// &dyn ErrTree, not in a collection |
13 | | Dyn, |
14 | | /// Known ErrTree, not in a collection |
15 | | Tree, |
16 | | /// &dyn ErrTree, in a collection |
17 | | DynIter, |
18 | | /// Known ErrTree, in a collection |
19 | | TreeIter, |
20 | | } |
21 | | |
22 | | #[derive(Debug)] |
23 | | pub struct TreeErr { |
24 | | ident: Ident, |
25 | | span: proc_macro2::Span, |
26 | | var: ErrType, |
27 | | } |
28 | | |
29 | | impl TreeErr { |
30 | 6 | pub fn new(ident: Ident, span: proc_macro2::Span, var: ErrType) -> Self { |
31 | 6 | Self { ident, span, var } |
32 | 6 | } |
33 | | } |
34 | | |
35 | | /// Generate the `with_pkg` call on all notated sources in a struct. |
36 | 3 | pub fn gen_sources_struct(errs: &[TreeErr], foreign: bool) -> proc_macro2::TokenStream { |
37 | | // Trivial name change covers both foreign and direct impl |
38 | 3 | let parent = if foreign { |
39 | 0 | quote! { self.inner } |
40 | | } else { |
41 | 3 | quote! { self } |
42 | | }; |
43 | | |
44 | 3 | let conv = |x, span| { |
45 | 0 | quote_spanned! { |
46 | 0 | span=> let #x = & self.#x as &dyn ::bare_err_tree::AsErrTree; |
47 | 0 | let #x = core::iter::once(#x); |
48 | 0 | } |
49 | 0 | }; |
50 | | |
51 | 3 | let conv_dyn = |x, span| { |
52 | 2 | quote_spanned! { |
53 | 2 | span=> let #x = ::bare_err_tree::WrapErr::tree(& self.#x); |
54 | 2 | let #x = core::iter::once(#x); |
55 | 2 | } |
56 | 2 | }; |
57 | | |
58 | 3 | let conv_dyn_iter = |x, span| { |
59 | 2 | quote_spanned! { |
60 | 2 | span=> let #x = #parent.#x.iter() |
61 | 2 | .map(::bare_err_tree::WrapErr::tree); |
62 | 2 | } |
63 | 2 | }; |
64 | | |
65 | 3 | let conv_iter = |x, span| { |
66 | 0 | quote_spanned! { |
67 | 0 | span=> let #x = #parent.#x.iter().map(|x| x as &dyn ::bare_err_tree::AsErrTree); |
68 | 0 | } |
69 | 0 | }; |
70 | | |
71 | 4 | let gen_vars = errs.iter().map(3 |err| match err.var { |
72 | 2 | ErrType::Dyn => conv_dyn(&err.ident, err.span), |
73 | 0 | ErrType::Tree => conv(&err.ident, err.span), |
74 | 2 | ErrType::DynIter => conv_dyn_iter(&err.ident, err.span), |
75 | 0 | ErrType::TreeIter => conv_iter(&err.ident, err.span), |
76 | 4 | }); |
77 | 4 | let ids = errs.iter().map(|err| &err.ident); |
78 | 3 | |
79 | 3 | quote! { |
80 | 3 | #(#gen_vars)* |
81 | 3 | let mut sources = &mut core::iter::empty()#(.chain(#ids))*; |
82 | 3 | |
83 | 3 | (func)(::bare_err_tree::ErrTree::with_pkg(self, sources, _err_tree_pkg)) |
84 | 3 | } |
85 | 3 | } |
86 | | |
87 | | /// Generate the `with_pkg` call on all notated sources in a enum. |
88 | 1 | pub fn gen_sources_enum(errs: &[TreeErr], ident: &Ident) -> proc_macro2::TokenStream { |
89 | 1 | let conv = |x, span| { |
90 | 0 | quote_spanned! { |
91 | 0 | span=> #ident :: #x (x) => { |
92 | 0 | let x = x as &dyn ::bare_err_tree::AsErrTree; |
93 | 0 | let x = &mut core::iter::once(x); |
94 | 0 | (func)(::bare_err_tree::ErrTree::with_pkg(self, x, _err_tree_pkg)) |
95 | 0 | }, |
96 | 0 | } |
97 | 0 | }; |
98 | | |
99 | 1 | let conv_dyn = |x, span| { |
100 | 1 | quote_spanned! { |
101 | 1 | span=> #ident :: #x (x) => { |
102 | 1 | let x = ::bare_err_tree::WrapErr::tree(x); |
103 | 1 | let x = &mut core::iter::once(x); |
104 | 1 | (func)(::bare_err_tree::ErrTree::with_pkg(self, x, _err_tree_pkg)) |
105 | 1 | }, |
106 | 1 | } |
107 | 1 | }; |
108 | | |
109 | 1 | let conv_iter = |x, span| { |
110 | 0 | quote_spanned! { |
111 | 0 | span=> #ident :: #x (x) => { |
112 | 0 | let x = &mut x.iter().map(|z| z as &dyn AsErrTree); |
113 | 0 | (func)(::bare_err_tree::ErrTree::with_pkg(self, x, _err_tree_pkg)) |
114 | 0 | } |
115 | 0 | } |
116 | 0 | }; |
117 | | |
118 | 1 | let conv_iter_dyn = |x, span| { |
119 | 1 | quote_spanned! { |
120 | 1 | span=> #ident :: #x (x) => { |
121 | 1 | let x = &mut x.iter().map(::bare_err_tree::WrapErr::tree); |
122 | 1 | (func)(::bare_err_tree::ErrTree::with_pkg(self, x, _err_tree_pkg)) |
123 | 1 | } |
124 | 1 | } |
125 | 1 | }; |
126 | | |
127 | 2 | let gen_arms = errs.iter().map(1 |err| match err.var { |
128 | 1 | ErrType::Dyn => conv_dyn(&err.ident, err.span), |
129 | 0 | ErrType::Tree => conv(&err.ident, err.span), |
130 | 1 | ErrType::DynIter => conv_iter_dyn(&err.ident, err.span), |
131 | 0 | ErrType::TreeIter => conv_iter(&err.ident, err.span), |
132 | 2 | }); |
133 | 1 | |
134 | 1 | quote! { |
135 | 1 | let sources = match &self.inner { |
136 | 1 | #(#gen_arms)* |
137 | 1 | _ => { |
138 | 1 | (func)(::bare_err_tree::ErrTree::with_pkg(self, &mut core::iter::empty(), _err_tree_pkg)) |
139 | 1 | } |
140 | 1 | }; |
141 | 1 | } |
142 | 1 | } |
143 | | |
144 | | /// Parse iterator types. |
145 | | /// |
146 | | /// Distinguishes between sized and unsized arrays to generate the |
147 | | /// correct identity name and sizing types. |
148 | 3 | fn iter_parse(f: &Field, ident: Ident, var: ErrType) -> TreeErr { |
149 | 3 | let mut ty = f.ty.clone(); |
150 | 3 | while let Type::Reference(ty_ref0 ) = ty { |
151 | 0 | ty = *ty_ref.elem; |
152 | 0 | } |
153 | | |
154 | 3 | TreeErr::new(ident, f.span(), var) |
155 | 3 | } |
156 | | |
157 | | /// Finds all child error annotations on a struct. |
158 | 3 | pub fn get_struct_macros(data: &DataStruct) -> impl Iterator<Item = TreeErr> + use<'_> { |
159 | 5 | data.fields.iter().flat_map(|f| { |
160 | 5 | f.attrs.iter().filter_map(|x| { |
161 | 4 | x.meta.require_path_only().ok().and_then(|y| { |
162 | 4 | y.segments |
163 | 4 | .iter() |
164 | 4 | .find_map(|seg| match seg.ident.to_string().as_str() { |
165 | 4 | "dyn_err" => Some(TreeErr::new( |
166 | 2 | f.ident.clone().unwrap(), |
167 | 2 | f.span(), |
168 | 2 | ErrType::Dyn, |
169 | 2 | )), |
170 | 2 | "tree_err" => Some(TreeErr::new( |
171 | 0 | f.ident.clone().unwrap(), |
172 | 0 | f.span(), |
173 | 0 | ErrType::Tree, |
174 | 0 | )), |
175 | 2 | "dyn_iter_err" => { |
176 | 2 | Some(iter_parse(f, f.ident.clone().unwrap(), ErrType::DynIter)) |
177 | | } |
178 | 0 | "tree_iter_err" => { |
179 | 0 | Some(iter_parse(f, f.ident.clone().unwrap(), ErrType::TreeIter)) |
180 | | } |
181 | 0 | _ => None, |
182 | 4 | }) |
183 | 4 | }) |
184 | 5 | }) |
185 | 5 | }) |
186 | 3 | } |
187 | | |
188 | | /// Finds all child error annotations on an enum. |
189 | 1 | pub fn get_enum_macros(data: &DataEnum) -> impl Iterator<Item = TreeErr> + use<'_> { |
190 | 3 | data.variants.iter().flat_map(|f| { |
191 | 3 | f.attrs.iter().filter_map(|x| { |
192 | 2 | x.meta.require_path_only().ok().and_then(|y| { |
193 | 2 | y.segments |
194 | 2 | .iter() |
195 | 2 | .find_map(|seg| match seg.ident.to_string().as_str() { |
196 | 2 | "dyn_err" => Some(TreeErr::new(f.ident.clone(), f.span(), ErrType::Dyn))1 , |
197 | 1 | "tree_err" => Some(TreeErr::new(f.ident.clone(), f.span(), ErrType::Tree))0 , |
198 | 1 | "dyn_iter_err" => { |
199 | 1 | if f.fields.len() == 1 { |
200 | 1 | let field = |
201 | 1 | f.fields.iter().next().expect("Previously checked length"); |
202 | 1 | Some(iter_parse(field, f.ident.clone(), ErrType::DynIter)) |
203 | | } else { |
204 | 0 | Some(TreeErr::new(f.ident.clone(), f.span(), ErrType::DynIter)) |
205 | | } |
206 | | } |
207 | 0 | "tree_iter_err" => { |
208 | 0 | if f.fields.len() == 1 { |
209 | 0 | let field = |
210 | 0 | f.fields.iter().next().expect("Previously checked length"); |
211 | 0 | Some(iter_parse(field, f.ident.clone(), ErrType::TreeIter)) |
212 | | } else { |
213 | 0 | Some(TreeErr::new(f.ident.clone(), f.span(), ErrType::TreeIter)) |
214 | | } |
215 | | } |
216 | 0 | _ => None, |
217 | 2 | }) |
218 | 2 | }) |
219 | 3 | }) |
220 | 3 | }) |
221 | 1 | } |
222 | | |
223 | | /// Remove this library's annotation, as they aren't actually valid macros. |
224 | 3 | pub fn clean_struct_macros(data: &mut DataStruct) { |
225 | 5 | data.fields.iter_mut().for_each(|f| { |
226 | 5 | f.attrs = f |
227 | 5 | .attrs |
228 | 5 | .clone() |
229 | 5 | .into_iter() |
230 | 5 | .filter(|x| { |
231 | 4 | x.meta |
232 | 4 | .require_path_only() |
233 | 4 | .ok() |
234 | 4 | .and_then(|y| { |
235 | 4 | y.segments |
236 | 4 | .iter() |
237 | 4 | .any(|seg| { |
238 | 4 | ["dyn_err", "tree_err", "dyn_iter_err", "tree_iter_err"] |
239 | 4 | .contains(&seg.ident.to_string().as_str()) |
240 | 4 | }) |
241 | 4 | .then_some(()) |
242 | 4 | }) |
243 | 4 | .is_none() |
244 | 5 | }) |
245 | 5 | .collect(); |
246 | 5 | }); |
247 | 3 | } |
248 | | |
249 | | /// Remove this library's annotation, as they aren't actually valid macros. |
250 | 1 | pub fn clean_enum_macros(data: &mut DataEnum) { |
251 | 3 | data.variants.iter_mut().for_each(|f| { |
252 | 3 | f.attrs = f |
253 | 3 | .attrs |
254 | 3 | .clone() |
255 | 3 | .into_iter() |
256 | 3 | .filter(|x| { |
257 | 2 | x.meta |
258 | 2 | .require_path_only() |
259 | 2 | .ok() |
260 | 2 | .and_then(|y| { |
261 | 2 | y.segments |
262 | 2 | .iter() |
263 | 2 | .any(|seg| { |
264 | 2 | ["dyn_err", "tree_err", "dyn_iter_err", "tree_iter_err"] |
265 | 2 | .contains(&seg.ident.to_string().as_str()) |
266 | 2 | }) |
267 | 2 | .then_some(()) |
268 | 2 | }) |
269 | 2 | .is_none() |
270 | 3 | }) |
271 | 3 | .collect(); |
272 | 3 | }); |
273 | 1 | } |