1 | use crate::ast::{Enum, Field, Struct, Variant}; |
2 | use syn::{Member, Type}; |
3 | |
4 | impl 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 | |
23 | impl 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 | |
50 | impl 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 | |
69 | impl Field<'_> { |
70 | pub(crate) fn is_backtrace(&self) -> bool { |
71 | type_is_backtrace(self.ty) |
72 | } |
73 | } |
74 | |
75 | fn 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 | |
84 | fn 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 | |
99 | fn 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. |
114 | fn 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 | |
127 | fn 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 | |