Skip to main content

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            // Two ShapeCase::Name values are equal iff their symbolic Names are
146            // equal. We deliberately do not recursively compare the shapes they
147            // resolve to through their WeakScope: within a given Namespace, the
148            // invariant is that any two Name values sharing the same base name
149            // refer to the same underlying Shape (see Namespace::insert, which
150            // merges colliding names), so recursive comparison would be
151            // redundant. Comparing across Namespaces is not meaningful — names
152            // are symbolic — and attempting to recurse can diverge on
153            // self-referential shapes (e.g. `Foo = List<Foo>`).
154            (Self::Name(self_name, _), Self::Name(other_name, _)) => self_name == other_name,
155            _ => false,
156        }
157    }
158}
159
160impl ShapeCase {
161    #[must_use]
162    pub fn is_none(&self) -> bool {
163        matches!(self, Self::None)
164    }
165
166    #[must_use]
167    pub fn is_null(&self) -> bool {
168        matches!(self, Self::Null)
169    }
170
171    pub(crate) fn nested_base_names(&self) -> impl Iterator<Item = &str> {
172        match self {
173            Self::Name(name, _weak) => vec![name.base_shape_name()].into_iter(),
174            Self::Array { prefix, tail } => {
175                let mut names = Vec::new();
176                for shape in prefix {
177                    names.extend(shape.nested_base_names());
178                }
179                names.extend(tail.nested_base_names());
180                names.into_iter()
181            }
182            Self::Object { fields, rest } => {
183                let mut names = Vec::new();
184                for shape in fields.values() {
185                    names.extend(shape.nested_base_names());
186                }
187                names.extend(rest.nested_base_names());
188                names.into_iter()
189            }
190            Self::One(shapes) => shapes
191                .iter()
192                .flat_map(Shape::nested_base_names)
193                .collect::<Vec<_>>()
194                .into_iter(),
195            Self::All(shapes) => shapes
196                .iter()
197                .flat_map(Shape::nested_base_names)
198                .collect::<Vec<_>>()
199                .into_iter(),
200            _ => vec![].into_iter(),
201        }
202    }
203}
204
205impl From<bool> for ShapeCase {
206    fn from(value: bool) -> Self {
207        ShapeCase::Bool(Some(value))
208    }
209}
210
211impl From<String> for ShapeCase {
212    fn from(value: String) -> Self {
213        ShapeCase::String(Some(value))
214    }
215}
216
217impl From<&str> for ShapeCase {
218    fn from(value: &str) -> Self {
219        ShapeCase::String(Some(value.to_string()))
220    }
221}
222
223impl From<i64> for ShapeCase {
224    fn from(value: i64) -> Self {
225        ShapeCase::Int(Some(value))
226    }
227}
228
229impl PartialEq<&ShapeCase> for ShapeCase {
230    fn eq(&self, other: &&ShapeCase) -> bool {
231        self == *other
232    }
233}
234
235impl PartialEq<ShapeCase> for &ShapeCase {
236    fn eq(&self, other: &ShapeCase) -> bool {
237        *self == other
238    }
239}
240
241/// Represents a local failure of shape processing. Errors are stored as
242/// metadata in [`crate::meta::ShapeMeta`] rather than as a shape variant,
243/// allowing any shape to carry error information without changing its
244/// structural identity.
245#[derive(Clone, Debug, Eq, PartialEq, Hash)]
246pub struct Error {
247    pub message: String,
248}