1use crate::ast::{Enum, Field, Struct, Variant};
2use crate::unraw::MemberUnraw;
3use proc_macro2::Span;
4use syn::Type;
5use syn::{
6 AngleBracketedGenericArguments, GenericArgument, Lifetime, PathArguments, TypeReference,
7};
8
9impl Struct<'_> {
10 pub(crate) fn from_field(&self) -> Option<&Field> {
11 from_field(&self.fields)
12 }
13
14 pub(crate) fn source_field(&self) -> Option<&Field> {
15 source_field(&self.fields)
16 }
17
18 pub(crate) fn backtrace_field(&self) -> Option<&Field> {
19 backtrace_field(&self.fields)
20 }
21
22 pub(crate) fn location_field(&self) -> Option<&Field> {
23 location_field(&self.fields)
24 }
25
26 pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> {
27 let backtrace_field = self.backtrace_field()?;
28 distinct_backtrace_field(backtrace_field, self.from_field())
29 }
30}
31
32impl Enum<'_> {
33 pub(crate) fn has_source(&self) -> bool {
34 self.variants
35 .iter()
36 .any(|variant| variant.source_field().is_some() || variant.attrs.transparent.is_some())
37 }
38
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.attrs.fmt.is_some()
49 || self
50 .variants
51 .iter()
52 .any(|variant| variant.attrs.display.is_some() || variant.attrs.fmt.is_some())
53 || self
54 .variants
55 .iter()
56 .all(|variant| variant.attrs.transparent.is_some())
57 }
58}
59
60impl Variant<'_> {
61 pub(crate) fn from_field(&self) -> Option<&Field> {
62 from_field(&self.fields)
63 }
64
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 location_field(&self) -> Option<&Field> {
74 location_field(&self.fields)
75 }
76
77 pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> {
78 let backtrace_field = self.backtrace_field()?;
79 distinct_backtrace_field(backtrace_field, self.from_field())
80 }
81}
82
83impl Field<'_> {
84 pub(crate) fn is_backtrace(&self) -> bool {
85 type_is_backtrace(self.ty)
86 }
87
88 pub(crate) fn is_location(&self) -> bool {
89 type_is_location(self.ty)
90 }
91
92 pub(crate) fn source_span(&self) -> Span {
93 if let Some(source_attr) = &self.attrs.source {
94 source_attr.span
95 } else if let Some(from_attr) = &self.attrs.from {
96 from_attr.span
97 } else {
98 self.member.span()
99 }
100 }
101}
102
103fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
104 for field in fields {
105 if field.attrs.from.is_some() {
106 return Some(field);
107 }
108 }
109 None
110}
111
112fn source_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
113 for field in fields {
114 if field.attrs.from.is_some() || field.attrs.source.is_some() {
115 return Some(field);
116 }
117 }
118 for field in fields {
119 match &field.member {
120 MemberUnraw::Named(ident) if ident == "source" => return Some(field),
121 _ => {}
122 }
123 }
124 None
125}
126
127fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
128 for field in fields {
129 if field.attrs.backtrace.is_some() {
130 return Some(field);
131 }
132 }
133 for field in fields {
134 if field.is_backtrace() {
135 return Some(field);
136 }
137 }
138 None
139}
140
141fn location_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
142 for field in fields {
143 if field.attrs.location.is_some() {
144 return Some(field);
145 }
146 }
147 for field in fields {
148 if field.is_location() {
149 return Some(field);
150 }
151 }
152 None
153}
154
155fn distinct_backtrace_field<'a, 'b>(
157 backtrace_field: &'a Field<'b>,
158 from_field: Option<&Field>,
159) -> Option<&'a Field<'b>> {
160 if from_field.map_or(false, |from_field| {
161 from_field.member == backtrace_field.member
162 }) {
163 None
164 } else {
165 Some(backtrace_field)
166 }
167}
168
169fn type_is_backtrace(ty: &Type) -> bool {
170 let path = match ty {
171 Type::Path(ty) => &ty.path,
172 _ => return false,
173 };
174
175 let last = path.segments.last().unwrap();
176 last.ident == "Backtrace" && last.arguments.is_empty()
177}
178
179fn type_is_location(ty: &Type) -> bool {
180 let path = match ty {
181 Type::Reference(TypeReference {
182 lifetime: Some(Lifetime { ident: ltident, .. }),
183 elem, ..
185 }) if ltident == "static" => match &**elem {
186 Type::Path(ty) => &ty.path,
187 _ => return false,
188 },
189 _ => return false,
190 };
191
192 let last = path.segments.last().unwrap();
193
194 last.ident == "Location"
195 && match &last.arguments {
196 PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) => {
197 match args.first() {
198 Some(GenericArgument::Lifetime(Lifetime { ident, .. })) => ident == "static",
199 _ => false,
200 }
201 }
202 _ => false,
203 }
204}