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}