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