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