1 | use std::{fmt, iter::FromIterator}; |
2 | |
3 | use crate::ast; |
4 | |
5 | /// Get the "shape" of a fields container, such as a struct or variant. |
6 | pub trait AsShape { |
7 | /// Get the "shape" of a fields container. |
8 | fn as_shape(&self) -> Shape; |
9 | } |
10 | |
11 | impl<T> AsShape for ast::Fields<T> { |
12 | fn as_shape(&self) -> Shape { |
13 | match self.style { |
14 | ast::Style::Tuple if self.fields.len() == 1 => Shape::Newtype, |
15 | ast::Style::Tuple => Shape::Tuple, |
16 | ast::Style::Struct => Shape::Named, |
17 | ast::Style::Unit => Shape::Unit, |
18 | } |
19 | } |
20 | } |
21 | |
22 | impl AsShape for syn::Fields { |
23 | fn as_shape(&self) -> Shape { |
24 | match self { |
25 | syn::Fields::Named(fields: &FieldsNamed) => fields.as_shape(), |
26 | syn::Fields::Unnamed(fields: &FieldsUnnamed) => fields.as_shape(), |
27 | syn::Fields::Unit => Shape::Unit, |
28 | } |
29 | } |
30 | } |
31 | |
32 | impl AsShape for syn::FieldsNamed { |
33 | fn as_shape(&self) -> Shape { |
34 | Shape::Named |
35 | } |
36 | } |
37 | |
38 | impl AsShape for syn::FieldsUnnamed { |
39 | fn as_shape(&self) -> Shape { |
40 | if self.unnamed.len() == 1 { |
41 | Shape::Newtype |
42 | } else { |
43 | Shape::Tuple |
44 | } |
45 | } |
46 | } |
47 | |
48 | impl AsShape for syn::DataStruct { |
49 | fn as_shape(&self) -> Shape { |
50 | self.fields.as_shape() |
51 | } |
52 | } |
53 | |
54 | impl AsShape for syn::Variant { |
55 | fn as_shape(&self) -> Shape { |
56 | self.fields.as_shape() |
57 | } |
58 | } |
59 | |
60 | /// Description of how fields in a struct or variant are syntactically laid out. |
61 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
62 | pub enum Shape { |
63 | /// A set of named fields, e.g. `{ field: String }`. |
64 | Named, |
65 | /// A list of unnamed fields, e.g. `(String, u64)`. |
66 | Tuple, |
67 | /// No fields, e.g. `struct Example;` |
68 | Unit, |
69 | /// A special case of [`Tuple`](Shape#variant.Tuple) with exactly one field, e.g. `(String)`. |
70 | Newtype, |
71 | } |
72 | |
73 | impl Shape { |
74 | pub fn description(&self) -> &'static str { |
75 | match self { |
76 | Shape::Named => "named fields" , |
77 | Shape::Tuple => "unnamed fields" , |
78 | Shape::Unit => "no fields" , |
79 | Shape::Newtype => "one unnamed field" , |
80 | } |
81 | } |
82 | } |
83 | |
84 | impl fmt::Display for Shape { |
85 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
86 | write!(f, " {}" , self.description()) |
87 | } |
88 | } |
89 | |
90 | impl AsShape for Shape { |
91 | fn as_shape(&self) -> Shape { |
92 | *self |
93 | } |
94 | } |
95 | |
96 | /// A set of [`Shape`] values, which correctly handles the relationship between |
97 | /// [newtype](Shape#variant.Newtype) and [tuple](Shape#variant.Tuple) shapes. |
98 | /// |
99 | /// # Example |
100 | /// ```rust |
101 | /// # use darling_core::util::{Shape, ShapeSet}; |
102 | /// let shape_set = ShapeSet::new(vec![Shape::Tuple]); |
103 | /// |
104 | /// // This is correct, because all newtypes are single-field tuples. |
105 | /// assert!(shape_set.contains(&Shape::Newtype)); |
106 | /// ``` |
107 | #[derive (Debug, Clone, Default)] |
108 | pub struct ShapeSet { |
109 | newtype: bool, |
110 | named: bool, |
111 | tuple: bool, |
112 | unit: bool, |
113 | } |
114 | |
115 | impl ShapeSet { |
116 | /// Create a new `ShapeSet` which includes the specified items. |
117 | /// |
118 | /// # Exampe |
119 | /// ```rust |
120 | /// # use darling_core::util::{Shape, ShapeSet}; |
121 | /// let shape_set = ShapeSet::new(vec![Shape::Named, Shape::Newtype]); |
122 | /// assert!(shape_set.contains(&Shape::Newtype)); |
123 | /// ``` |
124 | pub fn new(items: impl IntoIterator<Item = Shape>) -> Self { |
125 | items.into_iter().collect() |
126 | } |
127 | |
128 | /// Insert all possible shapes into the set. |
129 | /// |
130 | /// This is equivalent to calling [`insert`](ShapeSet#method.insert) with every value of [`Shape`]. |
131 | /// |
132 | /// # Example |
133 | /// ```rust |
134 | /// # use darling_core::util::{Shape, ShapeSet}; |
135 | /// let mut shape_set = ShapeSet::default(); |
136 | /// shape_set.insert_all(); |
137 | /// assert!(shape_set.contains(&Shape::Named)); |
138 | /// ``` |
139 | pub fn insert_all(&mut self) { |
140 | self.insert(Shape::Named); |
141 | self.insert(Shape::Newtype); |
142 | self.insert(Shape::Tuple); |
143 | self.insert(Shape::Unit); |
144 | } |
145 | |
146 | /// Insert a shape into the set, so that the set will match that shape |
147 | pub fn insert(&mut self, shape: Shape) { |
148 | match shape { |
149 | Shape::Named => self.named = true, |
150 | Shape::Tuple => self.tuple = true, |
151 | Shape::Unit => self.unit = true, |
152 | Shape::Newtype => self.newtype = true, |
153 | } |
154 | } |
155 | |
156 | /// Whether this set is empty. |
157 | pub fn is_empty(&self) -> bool { |
158 | !self.named && !self.newtype && !self.tuple && !self.unit |
159 | } |
160 | |
161 | fn contains_shape(&self, shape: Shape) -> bool { |
162 | match shape { |
163 | Shape::Named => self.named, |
164 | Shape::Tuple => self.tuple, |
165 | Shape::Unit => self.unit, |
166 | Shape::Newtype => self.newtype || self.tuple, |
167 | } |
168 | } |
169 | |
170 | /// Check if a fields container's shape is in this set. |
171 | pub fn contains(&self, fields: &impl AsShape) -> bool { |
172 | self.contains_shape(fields.as_shape()) |
173 | } |
174 | |
175 | /// Check if a field container's shape is in this set of shapes, and produce |
176 | /// an [`Error`](crate::Error) if it does not. |
177 | pub fn check(&self, fields: &impl AsShape) -> crate::Result<()> { |
178 | let shape = fields.as_shape(); |
179 | |
180 | if self.contains_shape(shape) { |
181 | Ok(()) |
182 | } else { |
183 | Err(crate::Error::unsupported_shape_with_expected( |
184 | shape.description(), |
185 | self, |
186 | )) |
187 | } |
188 | } |
189 | |
190 | fn to_vec(&self) -> Vec<Shape> { |
191 | let mut shapes = Vec::with_capacity(3); |
192 | |
193 | if self.named { |
194 | shapes.push(Shape::Named); |
195 | } |
196 | |
197 | if self.tuple || self.newtype { |
198 | shapes.push(if self.tuple { |
199 | Shape::Tuple |
200 | } else { |
201 | Shape::Newtype |
202 | }); |
203 | } |
204 | |
205 | if self.unit { |
206 | shapes.push(Shape::Unit) |
207 | } |
208 | |
209 | shapes |
210 | } |
211 | } |
212 | |
213 | impl fmt::Display for ShapeSet { |
214 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
215 | let shapes: Vec = self.to_vec(); |
216 | |
217 | match shapes.len() { |
218 | 0 => write!(f, "nothing" ), |
219 | 1 => write!(f, " {}" , shapes[0]), |
220 | 2 => write!(f, " {} or {}" , shapes[0], shapes[1]), |
221 | 3 => write!(f, " {}, {}, or {}" , shapes[0], shapes[1], shapes[2]), |
222 | _ => unreachable!(), |
223 | } |
224 | } |
225 | } |
226 | |
227 | impl FromIterator<Shape> for ShapeSet { |
228 | fn from_iter<T: IntoIterator<Item = Shape>>(iter: T) -> Self { |
229 | let mut output: ShapeSet = ShapeSet::default(); |
230 | for shape: Shape in iter.into_iter() { |
231 | output.insert(shape); |
232 | } |
233 | |
234 | output |
235 | } |
236 | } |
237 | |
238 | #[cfg (test)] |
239 | mod tests { |
240 | use syn::parse_quote; |
241 | |
242 | use super::*; |
243 | |
244 | #[test ] |
245 | fn any_accepts_anything() { |
246 | let mut filter = ShapeSet::default(); |
247 | filter.insert_all(); |
248 | let unit_struct: syn::DeriveInput = syn::parse_quote! { |
249 | struct Example; |
250 | }; |
251 | if let syn::Data::Struct(data) = unit_struct.data { |
252 | assert!(filter.contains(&data)); |
253 | } else { |
254 | panic!("Struct not parsed as struct" ); |
255 | }; |
256 | } |
257 | |
258 | #[test ] |
259 | fn tuple_accepts_newtype() { |
260 | let filter = ShapeSet::new(vec![Shape::Tuple]); |
261 | let newtype_struct: syn::DeriveInput = parse_quote! { |
262 | struct Example(String); |
263 | }; |
264 | |
265 | if let syn::Data::Struct(data) = newtype_struct.data { |
266 | assert!(filter.contains(&data)); |
267 | } else { |
268 | panic!("Struct not parsed as struct" ); |
269 | }; |
270 | } |
271 | |
272 | #[test ] |
273 | fn newtype_rejects_tuple() { |
274 | let filter = ShapeSet::new(vec![Shape::Newtype]); |
275 | let tuple_struct: syn::DeriveInput = parse_quote! { |
276 | struct Example(String, u64); |
277 | }; |
278 | |
279 | if let syn::Data::Struct(data) = tuple_struct.data { |
280 | assert!(!filter.contains(&data)); |
281 | } else { |
282 | panic!("Struct not parsed as struct" ); |
283 | }; |
284 | } |
285 | } |
286 | |