surrealdb_core/sql/
kind.rs

1use super::escape::EscapeKey;
2use super::{Duration, Idiom, Number, Part, Strand};
3use crate::sql::statements::info::InfoStructure;
4use crate::sql::{
5	fmt::{is_pretty, pretty_indent, Fmt, Pretty},
6	Table, Value,
7};
8use revision::revisioned;
9use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11use std::fmt::{self, Display, Formatter, Write};
12
13#[revisioned(revision = 2)]
14#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
15#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
16#[non_exhaustive]
17#[derive(Default)]
18pub enum Kind {
19	#[default]
20	Any,
21	Null,
22	Bool,
23	Bytes,
24	Datetime,
25	Decimal,
26	Duration,
27	Float,
28	Int,
29	Number,
30	Object,
31	Point,
32	String,
33	Uuid,
34	#[revision(start = 2)]
35	Regex,
36	Record(Vec<Table>),
37	Geometry(Vec<String>),
38	Option(Box<Kind>),
39	Either(Vec<Kind>),
40	Set(Box<Kind>, Option<u64>),
41	Array(Box<Kind>, Option<u64>),
42	Function(Option<Vec<Kind>>, Option<Box<Kind>>),
43	Range,
44	Literal(Literal),
45	References(Option<Table>, Option<Idiom>),
46}
47
48impl Kind {
49	/// Returns true if this type is an `any`
50	pub(crate) fn is_any(&self) -> bool {
51		matches!(self, Kind::Any)
52	}
53
54	/// Returns true if this type is a record
55	pub(crate) fn is_record(&self) -> bool {
56		matches!(self, Kind::Record(_))
57	}
58
59	/// Returns true if this type is optional
60	pub(crate) fn can_be_none(&self) -> bool {
61		matches!(self, Kind::Option(_) | Kind::Any)
62	}
63
64	/// Returns the kind in case of a literal, otherwise returns the kind itself
65	fn to_kind(&self) -> Self {
66		match self {
67			Kind::Literal(l) => l.to_kind(),
68			k => k.to_owned(),
69		}
70	}
71
72	/// Returns true if this type is a literal, or contains a literal
73	pub(crate) fn is_literal_nested(&self) -> bool {
74		if matches!(self, Kind::Literal(_)) {
75			return true;
76		}
77
78		if let Kind::Option(x) = self {
79			return x.is_literal_nested();
80		}
81
82		if let Kind::Either(x) = self {
83			return x.iter().any(|x| x.is_literal_nested());
84		}
85
86		false
87	}
88
89	/// Returns Some if this type can be converted into a discriminated object, None otherwise
90	pub(crate) fn to_discriminated(&self) -> Option<Kind> {
91		match self {
92			Kind::Either(nested) => {
93				if let Some(nested) = nested
94					.iter()
95					.map(|k| match k {
96						Kind::Literal(Literal::Object(o)) => Some(o),
97						_ => None,
98					})
99					.collect::<Option<Vec<&BTreeMap<String, Kind>>>>()
100				{
101					if let Some(first) = nested.first() {
102						let mut key: Option<String> = None;
103
104						'key: for (k, v) in first.iter() {
105							let mut kinds: Vec<Kind> = vec![v.to_owned()];
106							for item in nested[1..].iter() {
107								if let Some(kind) = item.get(k) {
108									match kind {
109										Kind::Literal(l)
110											if kinds.contains(&l.to_kind())
111												|| kinds.contains(&Kind::Literal(l.to_owned())) =>
112										{
113											continue 'key;
114										}
115										kind if kinds.iter().any(|k| *kind == k.to_kind()) => {
116											continue 'key;
117										}
118										kind => {
119											kinds.push(kind.to_owned());
120										}
121									}
122								} else {
123									continue 'key;
124								}
125							}
126
127							key = Some(k.clone());
128							break;
129						}
130
131						if let Some(key) = key {
132							return Some(Kind::Literal(Literal::DiscriminatedObject(
133								key.clone(),
134								nested.into_iter().map(|o| o.to_owned()).collect(),
135							)));
136						}
137					}
138				}
139
140				None
141			}
142			_ => None,
143		}
144	}
145
146	// Return the kind of the contained value.
147	//
148	// For example: for `array<number>` or `set<number>` this returns `number`.
149	// For `array<number> | set<float>` this returns `number | float`.
150	pub(crate) fn inner_kind(&self) -> Option<Kind> {
151		let mut this = self;
152		loop {
153			match &this {
154				Kind::Any
155				| Kind::Null
156				| Kind::Bool
157				| Kind::Bytes
158				| Kind::Datetime
159				| Kind::Decimal
160				| Kind::Duration
161				| Kind::Float
162				| Kind::Int
163				| Kind::Number
164				| Kind::Object
165				| Kind::Point
166				| Kind::String
167				| Kind::Uuid
168				| Kind::Regex
169				| Kind::Record(_)
170				| Kind::Geometry(_)
171				| Kind::Function(_, _)
172				| Kind::Range
173				| Kind::Literal(_)
174				| Kind::References(_, _) => return None,
175				Kind::Option(x) => {
176					this = x;
177				}
178				Kind::Array(x, _) | Kind::Set(x, _) => return Some(x.as_ref().clone()),
179				Kind::Either(x) => {
180					// a either shouldn't be able to contain a either itself so recursing here
181					// should be fine.
182					let kinds: Vec<Kind> = x.iter().filter_map(Self::inner_kind).collect();
183					if kinds.is_empty() {
184						return None;
185					}
186					return Some(Kind::Either(kinds));
187				}
188			}
189		}
190	}
191
192	pub(crate) fn non_optional(&self) -> &Kind {
193		match self {
194			Kind::Option(k) => k.as_ref().non_optional(),
195			_ => self,
196		}
197	}
198
199	pub(crate) fn allows_nested_kind(&self, path: &[Part], kind: &Kind) -> bool {
200		// ANY type won't cause a mismatch
201		if self.is_any() || kind.is_any() {
202			return true;
203		}
204
205		if !path.is_empty() {
206			match self {
207				Kind::Object => return matches!(path.first(), Some(Part::Field(_) | Part::All)),
208				Kind::Either(kinds) => {
209					return kinds.iter().all(|k| k.allows_nested_kind(path, kind))
210				}
211				Kind::Array(inner, len) | Kind::Set(inner, len) => {
212					return match path.first() {
213						Some(Part::All) => inner.allows_nested_kind(&path[1..], kind),
214						Some(Part::Index(i)) => {
215							if let Some(len) = len {
216								if i.as_usize() >= *len as usize {
217									return false;
218								}
219							}
220
221							inner.allows_nested_kind(&path[1..], kind)
222						}
223						_ => false,
224					}
225				}
226				_ => (),
227			}
228		}
229
230		match self {
231			Kind::Literal(lit) => lit.allows_nested_kind(path, kind),
232			Kind::Option(inner) => inner.allows_nested_kind(path, kind),
233			_ if path.is_empty() => self == kind,
234			_ => false,
235		}
236	}
237}
238
239impl From<&Kind> for Box<Kind> {
240	#[inline]
241	fn from(v: &Kind) -> Self {
242		Box::new(v.clone())
243	}
244}
245
246impl Display for Kind {
247	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
248		match self {
249			Kind::Any => f.write_str("any"),
250			Kind::Null => f.write_str("null"),
251			Kind::Bool => f.write_str("bool"),
252			Kind::Bytes => f.write_str("bytes"),
253			Kind::Datetime => f.write_str("datetime"),
254			Kind::Decimal => f.write_str("decimal"),
255			Kind::Duration => f.write_str("duration"),
256			Kind::Float => f.write_str("float"),
257			Kind::Int => f.write_str("int"),
258			Kind::Number => f.write_str("number"),
259			Kind::Object => f.write_str("object"),
260			Kind::Point => f.write_str("point"),
261			Kind::String => f.write_str("string"),
262			Kind::Uuid => f.write_str("uuid"),
263			Kind::Regex => f.write_str("regex"),
264			Kind::Function(_, _) => f.write_str("function"),
265			Kind::Option(k) => write!(f, "option<{}>", k),
266			Kind::Record(k) => match k {
267				k if k.is_empty() => write!(f, "record"),
268				k => write!(f, "record<{}>", Fmt::verbar_separated(k)),
269			},
270			Kind::Geometry(k) => match k {
271				k if k.is_empty() => write!(f, "geometry"),
272				k => write!(f, "geometry<{}>", Fmt::verbar_separated(k)),
273			},
274			Kind::Set(k, l) => match (k, l) {
275				(k, None) if k.is_any() => write!(f, "set"),
276				(k, None) => write!(f, "set<{k}>"),
277				(k, Some(l)) => write!(f, "set<{k}, {l}>"),
278			},
279			Kind::Array(k, l) => match (k, l) {
280				(k, None) if k.is_any() => write!(f, "array"),
281				(k, None) => write!(f, "array<{k}>"),
282				(k, Some(l)) => write!(f, "array<{k}, {l}>"),
283			},
284			Kind::Either(k) => write!(f, "{}", Fmt::verbar_separated(k)),
285			Kind::Range => f.write_str("range"),
286			Kind::Literal(l) => write!(f, "{}", l),
287			Kind::References(t, i) => match (t, i) {
288				(Some(t), None) => write!(f, "references<{}>", t),
289				(Some(t), Some(i)) => write!(f, "references<{}, {}>", t, i),
290				(None, _) => f.write_str("references"),
291			},
292		}
293	}
294}
295
296impl InfoStructure for Kind {
297	fn structure(self) -> Value {
298		self.to_string().into()
299	}
300}
301
302#[revisioned(revision = 1)]
303#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
304#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
305#[non_exhaustive]
306pub enum Literal {
307	String(Strand),
308	Number(Number),
309	Duration(Duration),
310	Array(Vec<Kind>),
311	Object(BTreeMap<String, Kind>),
312	DiscriminatedObject(String, Vec<BTreeMap<String, Kind>>),
313	Bool(bool),
314}
315
316impl Literal {
317	pub fn to_kind(&self) -> Kind {
318		match self {
319			Self::String(_) => Kind::String,
320			Self::Number(_) => Kind::Number,
321			Self::Duration(_) => Kind::Duration,
322			Self::Array(a) => {
323				if let Some(inner) = a.first() {
324					if a.iter().all(|x| x == inner) {
325						return Kind::Array(Box::new(inner.to_owned()), Some(a.len() as u64));
326					}
327				}
328
329				Kind::Array(Box::new(Kind::Any), None)
330			}
331			Self::Object(_) => Kind::Object,
332			Self::DiscriminatedObject(_, _) => Kind::Object,
333			Self::Bool(_) => Kind::Bool,
334		}
335	}
336
337	pub fn validate_value(&self, value: &Value) -> bool {
338		match self {
339			Self::String(v) => match value {
340				Value::Strand(s) => s == v,
341				_ => false,
342			},
343			Self::Number(v) => match value {
344				Value::Number(n) => n == v,
345				_ => false,
346			},
347			Self::Duration(v) => match value {
348				Value::Duration(n) => n == v,
349				_ => false,
350			},
351			Self::Bool(v) => match value {
352				Value::Bool(b) => b == v,
353				_ => false,
354			},
355			Self::Array(a) => match value {
356				Value::Array(x) => {
357					if a.len() != x.len() {
358						return false;
359					}
360
361					for (i, inner) in a.iter().enumerate() {
362						if let Some(value) = x.get(i) {
363							if value.to_owned().coerce_to(inner).is_err() {
364								return false;
365							}
366						} else {
367							return false;
368						}
369					}
370
371					true
372				}
373				_ => false,
374			},
375			Self::Object(o) => match value {
376				Value::Object(x) => {
377					if o.len() < x.len() {
378						return false;
379					}
380
381					for (k, v) in o.iter() {
382						if let Some(value) = x.get(k) {
383							if value.to_owned().coerce_to(v).is_err() {
384								return false;
385							}
386						} else if !v.can_be_none() {
387							return false;
388						}
389					}
390
391					true
392				}
393				_ => false,
394			},
395			Self::DiscriminatedObject(key, discriminants) => match value {
396				Value::Object(x) => {
397					let value = x.get(key).unwrap_or(&Value::None);
398					if let Some(o) = discriminants
399						.iter()
400						.find(|o| value.to_owned().coerce_to(o.get(key).unwrap()).is_ok())
401					{
402						if o.len() < x.len() {
403							return false;
404						}
405
406						for (k, v) in o.iter() {
407							if let Some(value) = x.get(k) {
408								if value.to_owned().coerce_to(v).is_err() {
409									return false;
410								}
411							} else if !v.can_be_none() {
412								return false;
413							}
414						}
415
416						true
417					} else {
418						false
419					}
420				}
421				_ => false,
422			},
423		}
424	}
425
426	pub(crate) fn allows_nested_kind(&self, path: &[Part], kind: &Kind) -> bool {
427		// ANY type won't cause a mismatch
428		if kind.is_any() {
429			return true;
430		}
431
432		// We reached the end of the path
433		// Check if the literal is equal to the kind
434		if path.is_empty() {
435			return match kind {
436				Kind::Literal(lit) => self == lit,
437				_ => &self.to_kind() == kind,
438			};
439		}
440
441		match self {
442			Literal::Array(x) => match path.first() {
443				Some(Part::All) => x.iter().all(|y| y.allows_nested_kind(&path[1..], kind)),
444				Some(Part::Index(i)) => {
445					if let Some(y) = x.get(i.as_usize()) {
446						y.allows_nested_kind(&path[1..], kind)
447					} else {
448						false
449					}
450				}
451				_ => false,
452			},
453			Literal::Object(x) => match path.first() {
454				Some(Part::All) => x.iter().all(|(_, y)| y.allows_nested_kind(&path[1..], kind)),
455				Some(Part::Field(k)) => {
456					if let Some(y) = x.get(&k.0) {
457						y.allows_nested_kind(&path[1..], kind)
458					} else {
459						false
460					}
461				}
462				_ => false,
463			},
464			Literal::DiscriminatedObject(_, discriminants) => match path.first() {
465				Some(Part::All) => discriminants
466					.iter()
467					.all(|o| o.iter().all(|(_, y)| y.allows_nested_kind(&path[1..], kind))),
468				Some(Part::Field(k)) => discriminants.iter().all(|o| {
469					if let Some(y) = o.get(&k.0) {
470						y.allows_nested_kind(&path[1..], kind)
471					} else {
472						false
473					}
474				}),
475				_ => false,
476			},
477			_ => false,
478		}
479	}
480}
481
482impl Display for Literal {
483	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
484		match self {
485			Literal::String(s) => write!(f, "{}", s),
486			Literal::Number(n) => write!(f, "{}", n),
487			Literal::Duration(d) => write!(f, "{}", d),
488			Literal::Bool(b) => write!(f, "{}", b),
489			Literal::Array(a) => {
490				let mut f = Pretty::from(f);
491				f.write_char('[')?;
492				if !a.is_empty() {
493					let indent = pretty_indent();
494					write!(f, "{}", Fmt::pretty_comma_separated(a.as_slice()))?;
495					drop(indent);
496				}
497				f.write_char(']')
498			}
499			Literal::Object(o) => {
500				let mut f = Pretty::from(f);
501				if is_pretty() {
502					f.write_char('{')?;
503				} else {
504					f.write_str("{ ")?;
505				}
506				if !o.is_empty() {
507					let indent = pretty_indent();
508					write!(
509						f,
510						"{}",
511						Fmt::pretty_comma_separated(o.iter().map(|args| Fmt::new(
512							args,
513							|(k, v), f| write!(f, "{}: {}", EscapeKey(k), v)
514						)),)
515					)?;
516					drop(indent);
517				}
518				if is_pretty() {
519					f.write_char('}')
520				} else {
521					f.write_str(" }")
522				}
523			}
524			Literal::DiscriminatedObject(_, discriminants) => {
525				let mut f = Pretty::from(f);
526
527				for (i, o) in discriminants.iter().enumerate() {
528					if i > 0 {
529						f.write_str(" | ")?;
530					}
531
532					if is_pretty() {
533						f.write_char('{')?;
534					} else {
535						f.write_str("{ ")?;
536					}
537					if !o.is_empty() {
538						let indent = pretty_indent();
539						write!(
540							f,
541							"{}",
542							Fmt::pretty_comma_separated(o.iter().map(|args| Fmt::new(
543								args,
544								|(k, v), f| write!(f, "{}: {}", EscapeKey(k), v)
545							)),)
546						)?;
547						drop(indent);
548					}
549					if is_pretty() {
550						f.write_char('}')?;
551					} else {
552						f.write_str(" }")?;
553					}
554				}
555
556				Ok(())
557			}
558		}
559	}
560}