1use crate::ast::{Enum, Field, Struct, Variant};
2use crate::unraw::MemberUnraw;
3use proc_macro2::Span;
4use syn::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 = 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.attrs.fmt.is_some()
42 || self
43 .variants
44 .iter()
45 .any(|variant| variant.attrs.display.is_some() || variant.attrs.fmt.is_some())
46 || self
47 .variants
48 .iter()
49 .all(|variant| variant.attrs.transparent.is_some())
50 }
51}
52
53impl Variant<'_> {
54 pub(crate) fn from_field(&self) -> Option<&Field> {
55 from_field(&self.fields)
56 }
57
58 pub(crate) fn source_field(&self) -> Option<&Field> {
59 source_field(&self.fields)
60 }
61
62 pub(crate) fn backtrace_field(&self) -> Option<&Field> {
63 backtrace_field(&self.fields)
64 }
65
66 pub(crate) fn distinct_backtrace_field(&self) -> Option<&Field> {
67 let backtrace_field = self.backtrace_field()?;
68 distinct_backtrace_field(backtrace_field, self.from_field())
69 }
70}
71
72impl Field<'_> {
73 pub(crate) fn is_backtrace(&self) -> bool {
74 type_is_backtrace(self.ty)
75 }
76
77 pub(crate) fn source_span(&self) -> Span {
78 if let Some(source_attr) = &self.attrs.source {
79 source_attr.span
80 } else if let Some(from_attr) = &self.attrs.from {
81 from_attr.span
82 } else {
83 self.member.span()
84 }
85 }
86}
87
88fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
89 for field in fields {
90 if field.attrs.from.is_some() {
91 return Some(field);
92 }
93 }
94 None
95}
96
97fn source_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
98 for field in fields {
99 if field.attrs.from.is_some() || field.attrs.source.is_some() {
100 return Some(field);
101 }
102 }
103 for field in fields {
104 match &field.member {
105 MemberUnraw::Named(ident) if ident == "source" => return Some(field),
106 _ => {}
107 }
108 }
109 None
110}
111
112fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
113 for field in fields {
114 if field.attrs.backtrace.is_some() {
115 return Some(field);
116 }
117 }
118 for field in fields {
119 if field.is_backtrace() {
120 return Some(field);
121 }
122 }
123 None
124}
125
126fn distinct_backtrace_field<'a, 'b>(
128 backtrace_field: &'a Field<'b>,
129 from_field: Option<&Field>,
130) -> Option<&'a Field<'b>> {
131 if from_field.map_or(false, |from_field| {
132 from_field.member == backtrace_field.member
133 }) {
134 None
135 } else {
136 Some(backtrace_field)
137 }
138}
139
140fn type_is_backtrace(ty: &Type) -> bool {
141 let path = match ty {
142 Type::Path(ty) => &ty.path,
143 _ => return false,
144 };
145
146 let last = path.segments.last().unwrap();
147 last.ident == "Backtrace" && last.arguments.is_empty()
148}