thiserror_nostd_notrait_impl/
prop.rs

1use crate::ast::{Enum, Field, Struct, Variant};
2#[cfg(feature = "std")]
3use crate::span::MemberSpan;
4#[cfg(feature = "std")]
5use proc_macro2::Span;
6#[cfg(feature = "std")]
7use syn::Member;
8use syn::Type;
9
10impl Struct<'_> {
11    pub(crate) fn from_field(&self) -> Option<&Field> {
12        from_field(&self.fields)
13    }
14
15    #[cfg(feature = "std")]
16    pub(crate) fn source_field(&self) -> Option<&Field> {
17        source_field(&self.fields)
18    }
19
20    pub(crate) fn backtrace_field(&self) -> Option<&Field> {
21        backtrace_field(&self.fields)
22    }
23
24    pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> {
25        let backtrace_field = self.backtrace_field()?;
26        distinct_backtrace_field(backtrace_field, self.from_field())
27    }
28}
29
30impl Enum<'_> {
31    #[cfg(feature = "std")]
32    pub(crate) fn has_source(&self) -> bool {
33        self.variants
34            .iter()
35            .any(|variant| variant.source_field().is_some() || variant.attrs.transparent.is_some())
36    }
37
38    #[cfg(feature = "std")]
39    pub(crate) fn has_backtrace(&self) -> bool {
40        self.variants
41            .iter()
42            .any(|variant| variant.backtrace_field().is_some())
43    }
44
45    pub(crate) fn has_display(&self) -> bool {
46        self.attrs.display.is_some()
47            || self.attrs.transparent.is_some()
48            || self
49                .variants
50                .iter()
51                .any(|variant| variant.attrs.display.is_some())
52            || self
53                .variants
54                .iter()
55                .all(|variant| variant.attrs.transparent.is_some())
56    }
57}
58
59impl Variant<'_> {
60    pub(crate) fn from_field(&self) -> Option<&Field> {
61        from_field(&self.fields)
62    }
63
64    #[cfg(feature = "std")]
65    pub(crate) fn source_field(&self) -> Option<&Field> {
66        source_field(&self.fields)
67    }
68
69    pub(crate) fn backtrace_field(&self) -> Option<&Field> {
70        backtrace_field(&self.fields)
71    }
72
73    pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> {
74        let backtrace_field = self.backtrace_field()?;
75        distinct_backtrace_field(backtrace_field, self.from_field())
76    }
77}
78
79impl Field<'_> {
80    pub(crate) fn is_backtrace(&self) -> bool {
81        type_is_backtrace(self.ty)
82    }
83
84    #[cfg(feature = "std")]
85    pub(crate) fn source_span(&self) -> Span {
86        if let Some(source_attr) = &self.attrs.source {
87            source_attr.path().get_ident().unwrap().span()
88        } else if let Some(from_attr) = &self.attrs.from {
89            from_attr.path().get_ident().unwrap().span()
90        } else {
91            self.member.member_span()
92        }
93    }
94}
95
96fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
97    for field in fields {
98        if field.attrs.from.is_some() {
99            return Some(field);
100        }
101    }
102    None
103}
104
105#[cfg(feature = "std")]
106fn source_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
107    for field in fields {
108        if field.attrs.from.is_some() || field.attrs.source.is_some() {
109            return Some(field);
110        }
111    }
112    for field in fields {
113        match &field.member {
114            Member::Named(ident) if ident == "source" => return Some(field),
115            _ => {}
116        }
117    }
118    None
119}
120
121fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
122    for field in fields {
123        if field.attrs.backtrace.is_some() {
124            return Some(field);
125        }
126    }
127    for field in fields {
128        if field.is_backtrace() {
129            return Some(field);
130        }
131    }
132    None
133}
134
135// The #[backtrace] field, if it is not the same as the #[from] field.
136fn distinct_backtrace_field<'a, 'b>(
137    backtrace_field: &'a Field<'b>,
138    from_field: Option<&Field>,
139) -> Option<&'a Field<'b>> {
140    if from_field.map_or(false, |from_field| {
141        from_field.member == backtrace_field.member
142    }) {
143        None
144    } else {
145        Some(backtrace_field)
146    }
147}
148
149fn type_is_backtrace(ty: &Type) -> bool {
150    let path = match ty {
151        Type::Path(ty) => &ty.path,
152        _ => return false,
153    };
154
155    let last = path.segments.last().unwrap();
156    last.ident == "Backtrace" && last.arguments.is_empty()
157}