Coverage Report

Created: 2025-02-07 03:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}