1mod builder;
4mod collection;
5mod comparison;
6mod conversion;
7mod crud;
8mod debug;
9
10pub mod merge;
11
12pub use crud::*;
13
14use std::collections::BTreeMap;
15
16pub use collection::{Collection, Field, Index, Unknown};
17
18use super::Value;
19
20#[derive(Debug, Clone, Eq, PartialOrd)]
25pub struct Kind {
26 bytes: Option<()>,
29 integer: Option<()>,
30 float: Option<()>,
31 boolean: Option<()>,
32 timestamp: Option<()>,
33 regex: Option<()>,
34 null: Option<()>,
35 undefined: Option<()>,
36 array: Option<Collection<Index>>,
37 object: Option<Collection<Field>>,
38}
39
40impl std::fmt::Display for Kind {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 if self.is_any() {
43 return f.write_str("any");
44 }
45
46 if self.is_exact() {
49 if let Some(object) = &self.object {
50 return object.fmt(f);
51 } else if let Some(array) = &self.array {
52 return array.fmt(f);
53 }
54 }
55
56 let mut kinds = vec![];
57
58 if self.contains_bytes() {
59 kinds.push("string");
60 }
61 if self.contains_integer() {
62 kinds.push("integer");
63 }
64 if self.contains_float() {
65 kinds.push("float");
66 }
67 if self.contains_boolean() {
68 kinds.push("boolean");
69 }
70 if self.contains_timestamp() {
71 kinds.push("timestamp");
72 }
73 if self.contains_regex() {
74 kinds.push("regex");
75 }
76 if self.contains_null() {
77 kinds.push("null");
78 }
79 if self.contains_undefined() {
80 kinds.push("undefined");
81 }
82 if self.contains_array() {
83 kinds.push("array");
84 }
85 if self.contains_object() {
86 kinds.push("object");
87 }
88
89 if kinds.is_empty() {
90 return f.write_str("never");
91 }
92
93 let len = kinds.len();
94 for (i, kind) in kinds.into_iter().enumerate() {
95 if i != 0 {
96 if i == len - 1 {
97 f.write_str(" or ")?;
98 } else {
99 f.write_str(", ")?;
100 }
101 }
102 kind.fmt(f)?;
103 }
104
105 Ok(())
106 }
107}
108
109impl PartialEq for Kind {
110 fn eq(&self, other: &Self) -> bool {
111 let a = self.canonicalize();
112 let b = other.canonicalize();
113
114 if a.bytes != b.bytes {
115 return false;
116 }
117 if a.integer != b.integer {
118 return false;
119 }
120 if a.float != b.float {
121 return false;
122 }
123 if a.boolean != b.boolean {
124 return false;
125 }
126 if a.timestamp != b.timestamp {
127 return false;
128 }
129 if a.regex != b.regex {
130 return false;
131 }
132 if a.null != b.null {
133 return false;
134 }
135 if a.undefined != b.undefined {
136 return false;
137 }
138 if a.array != b.array {
139 return false;
140 }
141 if a.object != b.object {
142 return false;
143 }
144 true
145 }
146}
147
148impl Kind {
149 #[must_use]
151 pub fn canonicalize(&self) -> Self {
152 let mut output = self.clone();
153
154 if let Some(object) = &mut output.object {
155 *object = object.canonicalize();
156 }
157 if let Some(array) = &mut output.array {
158 *array = array.canonicalize();
159 }
160 output
161 }
162}
163
164impl From<&Value> for Kind {
165 fn from(value: &Value) -> Self {
166 match value {
167 Value::Bytes(_) => Self::bytes(),
168 Value::Integer(_) => Self::integer(),
169 Value::Float(_) => Self::float(),
170 Value::Boolean(_) => Self::boolean(),
171 Value::Timestamp(_) => Self::timestamp(),
172 Value::Regex(_) => Self::regex(),
173 Value::Null => Self::null(),
174
175 Value::Object(object) => Self::object(
176 object
177 .iter()
178 .map(|(k, v)| (k.clone().into(), v.into()))
179 .collect::<BTreeMap<_, _>>(),
180 ),
181
182 Value::Array(array) => Self::array(
183 array
184 .iter()
185 .enumerate()
186 .map(|(i, v)| (i.into(), v.into()))
187 .collect::<BTreeMap<_, _>>(),
188 ),
189 }
190 }
191}
192
193impl From<Value> for Kind {
194 fn from(value: Value) -> Self {
195 (&value).into()
196 }
197}