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.
as_shape(&self) -> Shape8     fn as_shape(&self) -> Shape;
9 }
10 
11 impl<T> AsShape for ast::Fields<T> {
as_shape(&self) -> Shape12     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 {
as_shape(&self) -> Shape23     fn as_shape(&self) -> Shape {
24         match self {
25             syn::Fields::Named(fields) => fields.as_shape(),
26             syn::Fields::Unnamed(fields) => fields.as_shape(),
27             syn::Fields::Unit => Shape::Unit,
28         }
29     }
30 }
31 
32 impl AsShape for syn::FieldsNamed {
as_shape(&self) -> Shape33     fn as_shape(&self) -> Shape {
34         Shape::Named
35     }
36 }
37 
38 impl AsShape for syn::FieldsUnnamed {
as_shape(&self) -> Shape39     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 {
as_shape(&self) -> Shape49     fn as_shape(&self) -> Shape {
50         self.fields.as_shape()
51     }
52 }
53 
54 impl AsShape for syn::Variant {
as_shape(&self) -> Shape55     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 {
description(&self) -> &'static str74     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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result85     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86         write!(f, "{}", self.description())
87     }
88 }
89 
90 impl AsShape for Shape {
as_shape(&self) -> Shape91     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     /// ```
new(items: impl IntoIterator<Item = Shape>) -> Self124     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     /// ```
insert_all(&mut self)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
insert(&mut self, shape: 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.
is_empty(&self) -> bool157     pub fn is_empty(&self) -> bool {
158         !self.named && !self.newtype && !self.tuple && !self.unit
159     }
160 
contains_shape(&self, shape: Shape) -> bool161     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.
contains(&self, fields: &impl AsShape) -> bool171     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.
check(&self, fields: &impl AsShape) -> crate::Result<()>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 
to_vec(&self) -> Vec<Shape>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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result214     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215         let shapes = 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 {
from_iter<T: IntoIterator<Item = Shape>>(iter: T) -> Self228     fn from_iter<T: IntoIterator<Item = Shape>>(iter: T) -> Self {
229         let mut output = ShapeSet::default();
230         for 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]
any_accepts_anything()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]
tuple_accepts_newtype()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]
newtype_rejects_tuple()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