surrealdb_core/sql/
kind.rs

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