shape/
case_enum.rs

1pub(crate) mod all;
2pub(crate) mod one;
3
4use std::iter::empty;
5use std::iter::once;
6
7use indexmap::IndexMap;
8
9use self::all::All;
10use self::one::One;
11use super::Shape;
12use crate::name::Name;
13use crate::name::WeakScope;
14
15/// The [`ShapeCase`] enum attempts to capture all the common shapes of JSON
16/// values, not just the JSON types themselves, but also patterns of usage (such
17/// as using JSON arrays as either static tuples or dynamic lists).
18///
19/// A [`ShapeCase`] enum variant like [`ShapeCase::One`] may temporarily
20/// represent a structure that has not been fully simplified, but simplication
21/// is required to turn the [`ShapeCase`] into a `Shape`, using the
22/// `ShapeCase::simplify(&self) -> Shape` method.
23#[derive(Clone, Debug)]
24pub enum ShapeCase {
25    /// The `Bool`, `String`, and `Int` variants can either represent a general
26    /// shape using `None` for the `Option`, or a specific shape using `Some`.
27    /// For example, `ShapeCase::Bool(None)` is a shape satisfied by either true
28    /// or false, like the Rust bool type, while `ShapeCase::Bool(Some(true))`
29    /// is a shape satisfied only by `true` (and likewise for `false`).
30    Bool(Option<bool>),
31
32    /// Similar to [`ShapeCase::Bool`], [`ShapeCase::String`] represents a shape
33    /// matching either any string (in the `None` case) or some specific string
34    /// (in the `Some` case).
35    String(Option<String>),
36
37    /// Similar to [`ShapeCase::Bool`] and [`ShapeCase::String`],
38    /// [`ShapeCase::Int`] can represent either any integer or some specific
39    /// integer.
40    Int(Option<i64>),
41
42    /// [`ShapeCase::Float`] is a shape that captures the set of all floating
43    /// point numbers. We do not allow singleton floating point value shapes,
44    /// since floating point equality is unreliable.
45    Float,
46
47    /// [`ShapeCase::Null`] is a singleton shape whose only possible value is
48    /// `null`. Note that [`ShapeCase::Null`] is different from
49    /// [`ShapeCase::None`], which represents the absence of a value.
50    Null,
51
52    /// [`ShapeCase::Array`] represents a `prefix` of statically known shapes
53    /// (like a tuple type), followed by an optional `tail` shape for all other
54    /// (dynamic) elements. When only the `prefix` elements are defined, the
55    /// `tail` shape is [`ShapeCase::None`]. `ShapeCase::Array(vec![],
56    /// ShapeCase::None)` is the shape of an empty array.
57    Array { prefix: Vec<Shape>, tail: Shape },
58
59    /// [`ShapeCase::Object`] is a map of statically known field names to field
60    /// shapes, together with an optional type for all other string keys. When
61    /// dynamic string indexing is disabled, the rest shape will be
62    /// [`ShapeCase::None`]. Note that accessing the dynamic map always returns
63    /// `ShapeCase::One([rest_shape, ShapeCase::None])`, to reflect the
64    /// uncertainty of the shape of dynamic keys not present in the static
65    /// fields.
66    Object {
67        // TODO: track location of keys
68        fields: IndexMap<String, Shape>,
69        rest: Shape,
70    },
71
72    /// A union of shapes, satisfied by values that satisfy any of the shapes in
73    /// the set.
74    ///
75    /// Create using [`Shape::one`].
76    One(One),
77
78    /// An intersection of shapes, satisfied by values that satisfy all of the
79    /// shapes in the set. When applied to multiple [`ShapeCase::Object`]
80    /// shapes, [`ShapeCase::All`] represents merging the fields of the objects,
81    /// as reflected by the simplification logic.
82    ///
83    /// Crete using [`Shape::all`].
84    All(All),
85
86    /// [`ShapeCase::Name`] refers to a shape declared with the given [`Name`],
87    /// whose non-symbolic structure may not be known until later.
88    ///
89    /// When the [`ShapeCase::Name`] participates in a
90    /// [`crate::name::Namespace`], it may acquire a non-empty [`WeakScope`],
91    /// which can enable the symbolic [`Name`] to be resolved in the scope,
92    /// providing richer shape information than a [`Name`] alone.
93    ///
94    /// When shape processing needs to refer to the shape of some subproperty of
95    /// an unspecified named shape, it can append subproperty chains to the
96    /// immutable [`Name`] structure using methods like `field()` or `item()`
97    /// (among other methods) to obtain the nested name. When the named shape is
98    /// eventually declared, the nested structure can be used to resolve the
99    /// actual shape of the nested property path, using the [`WeakScope`].
100    Name(Name, WeakScope),
101
102    /// [`ShapeCase::Unknown`] is a shape that represents any possible JSON
103    /// value (including `ShapeCase::None`).
104    Unknown,
105
106    /// Represents the absence of a value, or the shape of a property not
107    /// present in an object. Used by the rest parameters of both
108    /// [`ShapeCase::Array`] and [`ShapeCase::Object`] to indicate no additional
109    /// dynamic elements are allowed, and with [`ShapeCase::One`] to represent
110    /// optionality of values, e.g. `One<Bool, None>`.
111    None,
112
113    /// Represents a local failure of shape processing.
114    Error(Error),
115}
116
117// Same as default #[derive(PartialEq)] except for ::Name resolution.
118impl Eq for ShapeCase {}
119impl PartialEq for ShapeCase {
120    fn eq(&self, other: &Self) -> bool {
121        match (self, other) {
122            (Self::Null, Self::Null)
123            | (Self::None, Self::None)
124            | (Self::Float, Self::Float)
125            | (Self::Unknown, Self::Unknown) => true,
126            (Self::Bool(a), Self::Bool(b)) => a == b,
127            (Self::String(a), Self::String(b)) => a == b,
128            (Self::Int(a), Self::Int(b)) => a == b,
129            (Self::One(self_shapes), Self::One(other_shapes)) => self_shapes == other_shapes,
130            (Self::All(self_shapes), Self::All(other_shapes)) => self_shapes == other_shapes,
131            (Self::Error(self_error), Self::Error(other_error)) => self_error == other_error,
132            (
133                Self::Array {
134                    prefix: self_prefix,
135                    tail: self_tail,
136                },
137                Self::Array {
138                    prefix: other_prefix,
139                    tail: other_tail,
140                },
141            ) => self_prefix == other_prefix && self_tail == other_tail,
142            (
143                Self::Object {
144                    fields: self_fields,
145                    rest: self_rest,
146                },
147                Self::Object {
148                    fields: other_fields,
149                    rest: other_rest,
150                },
151            ) => self_fields == other_fields && self_rest == other_rest,
152            // For two ShapeCase::Name values to be equal, their Names must be
153            // equal, and the Option<Shape>s they refer to in the WeakScope must
154            // be equal (or both None).
155            (Self::Name(self_name, self_weak), Self::Name(other_name, other_weak)) => {
156                self_name == other_name
157                    && self_weak.upgrade(self_name) == other_weak.upgrade(other_name)
158            }
159            _ => false,
160        }
161    }
162}
163
164impl ShapeCase {
165    #[must_use]
166    pub fn is_none(&self) -> bool {
167        matches!(self, Self::None)
168    }
169
170    #[must_use]
171    pub fn is_null(&self) -> bool {
172        matches!(self, Self::Null)
173    }
174
175    #[must_use]
176    pub fn error(message: String) -> Self {
177        Self::Error(Error {
178            message,
179            partial: None,
180        })
181    }
182
183    #[must_use]
184    pub fn error_with_partial(message: String, partial: Shape) -> Self {
185        Self::Error(Error {
186            message,
187            partial: Some(partial),
188        })
189    }
190
191    /// Iterate over all errors within this shape, recursively
192    pub fn errors(&self) -> Box<dyn Iterator<Item = &Error> + '_> {
193        match self {
194            Self::Error(error) => Box::new(once(error)),
195            Self::Array { prefix, tail } => {
196                Box::new(prefix.iter().flat_map(Shape::errors).chain(tail.errors()))
197            }
198            Self::Object { fields, rest } => {
199                Box::new(fields.values().flat_map(Shape::errors).chain(rest.errors()))
200            }
201            Self::One(shapes) => Box::new(shapes.iter().flat_map(Shape::errors)),
202            Self::All(shapes) => Box::new(shapes.iter().flat_map(Shape::errors)),
203            _ => Box::new(empty()),
204        }
205    }
206
207    pub(crate) fn nested_base_names(&self) -> impl Iterator<Item = &str> {
208        match self {
209            Self::Name(name, _weak) => vec![name.base_shape_name()].into_iter(),
210            Self::Array { prefix, tail } => {
211                let mut names = Vec::new();
212                for shape in prefix {
213                    names.extend(shape.nested_base_names());
214                }
215                names.extend(tail.nested_base_names());
216                names.into_iter()
217            }
218            Self::Object { fields, rest } => {
219                let mut names = Vec::new();
220                for shape in fields.values() {
221                    names.extend(shape.nested_base_names());
222                }
223                names.extend(rest.nested_base_names());
224                names.into_iter()
225            }
226            Self::One(shapes) => shapes
227                .iter()
228                .flat_map(Shape::nested_base_names)
229                .collect::<Vec<_>>()
230                .into_iter(),
231            Self::All(shapes) => shapes
232                .iter()
233                .flat_map(Shape::nested_base_names)
234                .collect::<Vec<_>>()
235                .into_iter(),
236            Self::Error(Error { partial, .. }) => {
237                let mut names = Vec::new();
238                if let Some(partial) = partial {
239                    names.extend(partial.nested_base_names());
240                }
241                names.into_iter()
242            }
243            _ => vec![].into_iter(),
244        }
245    }
246}
247
248impl From<bool> for ShapeCase {
249    fn from(value: bool) -> Self {
250        ShapeCase::Bool(Some(value))
251    }
252}
253
254impl From<String> for ShapeCase {
255    fn from(value: String) -> Self {
256        ShapeCase::String(Some(value))
257    }
258}
259
260impl From<&str> for ShapeCase {
261    fn from(value: &str) -> Self {
262        ShapeCase::String(Some(value.to_string()))
263    }
264}
265
266impl From<i64> for ShapeCase {
267    fn from(value: i64) -> Self {
268        ShapeCase::Int(Some(value))
269    }
270}
271
272impl PartialEq<&ShapeCase> for ShapeCase {
273    fn eq(&self, other: &&ShapeCase) -> bool {
274        self == *other
275    }
276}
277
278impl PartialEq<ShapeCase> for &ShapeCase {
279    fn eq(&self, other: &ShapeCase) -> bool {
280        *self == other
281    }
282}
283
284/// Represents a local failure of shape processing.
285#[derive(Clone, Debug, Eq, PartialEq)]
286pub struct Error {
287    pub message: String,
288    /// This `partial` shape can be another `ShapeCase::Error` shape, which
289    /// allows for chaining of multiple errors.
290    pub partial: Option<Shape>,
291}
292
293impl From<Error> for ShapeCase {
294    fn from(error: Error) -> Self {
295        ShapeCase::Error(error)
296    }
297}