shape/case_enum.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
use indexmap::IndexMap;
use indexmap::IndexSet;
use super::child_shape::NamedShapePathKey;
use super::helpers::OffsetRange;
use super::Shape;
/// The [`ShapeCase`] enum attempts to capture all the common shapes of JSON
/// values, not just the JSON types themselves, but also patterns of usage (such
/// as using JSON arrays as either static tuples or dynamic lists).
///
/// A [`ShapeCase`] enum variant like [`ShapeCase::One`] may temporarily
/// represent a structure that has not been fully simplified, but simplication
/// is required to turn the [`ShapeCase`] into a `Shape`, using the
/// `ShapeCase::simplify(&self) -> Shape` method.
#[derive(Clone, PartialEq, Eq)]
pub enum ShapeCase {
/// The `Bool`, `String`, and `Int` variants can either represent a general
/// shape using `None` for the `Option`, or a specific shape using `Some`.
/// For example, `ShapeCase::Bool(None)` is a shape satisfied by either true
/// or false, like the Rust bool type, while `ShapeCase::Bool(Some(true))`
/// is a shape satisfied only by `true` (and likewise for `false`).
Bool(Option<bool>),
/// Similar to [`ShapeCase::Bool`], [`ShapeCase::String`] represents a shape
/// matching either any string (in the `None` case) or some specific string
/// (in the `Some` case).
String(Option<String>),
/// Similar to [`ShapeCase::Bool`] and [`ShapeCase::String`],
/// [`ShapeCase::Int`] can represent either any integer or some specific
/// integer.
Int(Option<i64>),
/// [`ShapeCase::Float`] is a shape that captures the set of all floating
/// point numbers. We do not allow singleton floating point value shapes,
/// since floating point equality is unreliable.
Float,
/// [`ShapeCase::Null`] is a singleton shape whose only possible value is
/// `null`. Note that [`ShapeCase::Null`] is different from
/// [`ShapeCase::None`], which represents the absence of a value.
Null,
/// [`ShapeCase::Array`] represents a `prefix` of statically known shapes
/// (like a tuple type), followed by an optional `tail` shape for all other
/// (dynamic) elements. When only the `prefix` elements are defined, the
/// `tail` shape is [`ShapeCase::None`]. `ShapeCase::Array(vec![],
/// ShapeCase::None)` is the shape of an empty array.
Array { prefix: Vec<Shape>, tail: Shape },
/// [`ShapeCase::Object`] is a map of statically known field names to field
/// shapes, together with an optional type for all other string keys. When
/// dynamic string indexing is disabled, the rest shape will be
/// [`ShapeCase::None`]. Note that accessing the dynamic map always returns
/// `ShapeCase::One([rest_shape, ShapeCase::None])`, to reflect the
/// uncertainty of the shape of dynamic keys not present in the static
/// fields.
Object {
fields: IndexMap<String, Shape>,
rest: Shape,
},
/// A union of shapes, satisfied by values that satisfy any of the shapes in
/// the set.
One(IndexSet<Shape>),
/// An intersection of shapes, satisfied by values that satisfy all of the
/// shapes in the set. When applied to multiple [`ShapeCase::Object`]
/// shapes, [`ShapeCase::All`] represents merging the fields of the objects,
/// as reflected by the simplification logic.
All(IndexSet<Shape>),
/// [`ShapeCase::Name`] refers to a shape declared with the given name,
/// possibly in the future. When shape processing needs to refer to the
/// shape of some subproperty of a named shape, it uses the
/// `Vec<NamedShapePathKey>` subpath to represent the nested shape. When the
/// named shape is eventually declared, the subpath can be used to resolve
/// the actual shape of the nested property path.
//
/// As of now, the name `String` is expected to be either an identifier or a
/// variable name like `$root` or `$this` or `$args`, allowing
/// [`ShapeCase::Name`] to represent types of subproperties of variables as
/// well as named types from some schema.
Name(String, Vec<NamedShapePathKey>),
/// Represents the absence of a value, or the shape of a property not
/// present in an object. Used by the rest parameters of both
/// [`ShapeCase::Array`] and [`ShapeCase::Object`] to indicate no additional
/// dynamic elements are allowed, and with [`ShapeCase::One`] to represent
/// optionality of values, e.g. `One<Bool, None>`.
None,
/// Represents a local failure of shape processing.
Error {
message: String,
range: OffsetRange,
/// This `partial` shape can be another `ShapeCase::Error` shape, which
/// allows for chaining of multiple errors.
partial: Option<Shape>,
},
}
impl ShapeCase {
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn error(message: &str) -> Self {
Self::Error {
message: message.to_string(),
range: None,
partial: None,
}
}
pub fn error_with_range(message: &str, range: OffsetRange) -> Self {
Self::Error {
message: message.to_string(),
range,
partial: None,
}
}
pub fn error_with_partial(message: &str, partial: Shape) -> Self {
Self::Error {
message: message.to_string(),
range: None,
partial: Some(partial),
}
}
pub fn error_with_range_and_partial(message: &str, range: OffsetRange, partial: Shape) -> Self {
Self::Error {
message: message.to_string(),
range,
partial: Some(partial),
}
}
}
impl From<bool> for ShapeCase {
fn from(value: bool) -> Self {
ShapeCase::Bool(Some(value))
}
}
impl From<String> for ShapeCase {
fn from(value: String) -> Self {
ShapeCase::String(Some(value))
}
}
impl From<&str> for ShapeCase {
fn from(value: &str) -> Self {
ShapeCase::String(Some(value.to_string()))
}
}
impl From<i64> for ShapeCase {
fn from(value: i64) -> Self {
ShapeCase::Int(Some(value))
}
}