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}