shape/
case_enum.rs

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