1use std::cmp::Ordering;
2use std::collections::{BTreeMap, HashSet};
3use std::fmt::{self, Display, Formatter};
4use std::hash::{Hash, Hasher};
5use std::str::FromStr;
6
7use geo::{LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon};
8use revision::revisioned;
9use rust_decimal::Decimal;
10use surrealdb_strand::Strand;
11use surrealdb_types::{SqlFormat, ToSql};
12
13use crate::expr::statements::info::InfoStructure;
14use crate::expr::{Expr, Literal, Part, Value};
15use crate::val::table_name_public::{IntoPublicTable, IntoTableName};
16use crate::val::{
17 Array, Bytes, Closure, Datetime, Duration, File, Geometry, Number, Range, RecordId, Regex, Set,
18 TableName, Uuid,
19};
20
21#[revisioned(revision = 1)]
22#[derive(Clone, Debug, Eq, PartialEq, Hash)]
23pub enum GeometryKind {
24 Point,
25 Line,
26 Polygon,
27 MultiPoint,
28 MultiLine,
29 MultiPolygon,
30 Collection,
31}
32
33impl ToSql for GeometryKind {
34 fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
35 match self {
36 GeometryKind::Point => f.push_str("point"),
37 GeometryKind::Line => f.push_str("line"),
38 GeometryKind::Polygon => f.push_str("polygon"),
39 GeometryKind::MultiPoint => f.push_str("multipoint"),
40 GeometryKind::MultiLine => f.push_str("multiline"),
41 GeometryKind::MultiPolygon => f.push_str("multipolygon"),
42 GeometryKind::Collection => f.push_str("collection"),
43 }
44 }
45}
46
47impl FromStr for GeometryKind {
48 type Err = anyhow::Error;
49
50 fn from_str(s: &str) -> Result<Self, Self::Err> {
51 match s {
52 "point" => Ok(GeometryKind::Point),
53 "line" => Ok(GeometryKind::Line),
54 "polygon" => Ok(GeometryKind::Polygon),
55 "multipoint" => Ok(GeometryKind::MultiPoint),
56 "multiline" => Ok(GeometryKind::MultiLine),
57 "multipolygon" => Ok(GeometryKind::MultiPolygon),
58 "collection" => Ok(GeometryKind::Collection),
59 _ => Err(anyhow::anyhow!("invalid geometry kind: {s}")),
60 }
61 }
62}
63
64impl From<GeometryKind> for crate::types::PublicGeometryKind {
65 fn from(k: GeometryKind) -> Self {
66 match k {
67 GeometryKind::Point => crate::types::PublicGeometryKind::Point,
68 GeometryKind::Line => crate::types::PublicGeometryKind::Line,
69 GeometryKind::Polygon => crate::types::PublicGeometryKind::Polygon,
70 GeometryKind::MultiPoint => crate::types::PublicGeometryKind::MultiPoint,
71 GeometryKind::MultiLine => crate::types::PublicGeometryKind::MultiLine,
72 GeometryKind::MultiPolygon => crate::types::PublicGeometryKind::MultiPolygon,
73 GeometryKind::Collection => crate::types::PublicGeometryKind::Collection,
74 }
75 }
76}
77
78impl From<crate::types::PublicGeometryKind> for GeometryKind {
79 fn from(k: crate::types::PublicGeometryKind) -> Self {
80 match k {
81 crate::types::PublicGeometryKind::Point => GeometryKind::Point,
82 crate::types::PublicGeometryKind::Line => GeometryKind::Line,
83 crate::types::PublicGeometryKind::Polygon => GeometryKind::Polygon,
84 crate::types::PublicGeometryKind::MultiPoint => GeometryKind::MultiPoint,
85 crate::types::PublicGeometryKind::MultiLine => GeometryKind::MultiLine,
86 crate::types::PublicGeometryKind::MultiPolygon => GeometryKind::MultiPolygon,
87 crate::types::PublicGeometryKind::Collection => GeometryKind::Collection,
88 }
89 }
90}
91
92#[revisioned(revision = 1)]
94#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
95pub enum Kind {
96 #[default]
98 Any,
99 None,
101 Null,
103 Bool,
105 Bytes,
107 Datetime,
109 Decimal,
111 Duration,
113 Float,
115 Int,
117 Number,
120 Object,
122 String,
124 Uuid,
126 Regex,
128 Table(Vec<TableName>),
130 Record(Vec<TableName>),
132 Geometry(Vec<GeometryKind>),
136 Either(Vec<Kind>),
139 Set(Box<Kind>, Option<u64>),
141 Array(Box<Kind>, Option<u64>),
143 Function(Option<Vec<Kind>>, Option<Box<Kind>>),
147 Range,
149 Literal(KindLiteral),
155 File(Vec<String>),
159}
160
161impl Kind {
162 pub fn of<T: HasKind>() -> Kind {
164 T::kind()
165 }
166
167 pub fn is_any(&self) -> bool {
169 matches!(self, Kind::Any)
170 }
171
172 pub fn is_record(&self) -> bool {
174 matches!(self, Kind::Record(_))
175 }
176
177 pub fn reference_can_target(&self, table: &TableName) -> bool {
196 match self {
197 Kind::Record(tables) => tables.is_empty() || tables.iter().any(|t| t == table),
198 Kind::Array(inner, _) | Kind::Set(inner, _) => inner.reference_can_target(table),
199 Kind::Either(kinds) => kinds.iter().any(|k| k.reference_can_target(table)),
200 Kind::Literal(KindLiteral::Array(kinds)) => {
201 kinds.iter().any(|k| k.reference_can_target(table))
202 }
203 Kind::Any => true,
204 _ => false,
205 }
206 }
207
208 pub fn collect_reference_target_tables(&self, out: &mut HashSet<TableName>) -> bool {
219 match self {
220 Kind::Record(tables) => {
221 if tables.is_empty() {
222 return true;
223 }
224 out.extend(tables.iter().cloned());
225 false
226 }
227 Kind::Array(inner, _) | Kind::Set(inner, _) => {
228 inner.collect_reference_target_tables(out)
229 }
230 Kind::Either(kinds) => {
231 let mut unbounded = false;
232 for k in kinds {
233 unbounded |= k.collect_reference_target_tables(out);
234 }
235 unbounded
236 }
237 Kind::Literal(KindLiteral::Array(kinds)) => {
238 let mut unbounded = false;
239 for k in kinds {
240 unbounded |= k.collect_reference_target_tables(out);
241 }
242 unbounded
243 }
244 Kind::Any => true,
245 _ => false,
246 }
247 }
248
249 pub fn can_be_none(&self) -> bool {
251 match self {
252 Kind::None | Kind::Any => true,
253 Kind::Either(x) => x.iter().any(|x| x.can_be_none()),
254 _ => false,
255 }
256 }
257
258 pub fn contains_literal(&self) -> bool {
260 match self {
261 Kind::Literal(_) => true,
262 Kind::Either(x) => x.iter().any(|x| x.contains_literal()),
263 _ => false,
264 }
265 }
266
267 pub fn allows_sub_fields(&self) -> bool {
272 match self {
273 Kind::Any | Kind::Object | Kind::Array(..) | Kind::Set(..) => true,
274 Kind::Literal(KindLiteral::Object(_) | KindLiteral::Array(_)) => true,
275 Kind::Either(kinds) => {
276 kinds.iter().all(|k| matches!(k, Kind::None) || k.allows_sub_fields())
277 }
278 _ => false,
279 }
280 }
281
282 pub fn inner_kind(&self) -> Option<Kind> {
287 match self {
288 Kind::Any
289 | Kind::None
290 | Kind::Null
291 | Kind::Bool
292 | Kind::Bytes
293 | Kind::Datetime
294 | Kind::Decimal
295 | Kind::Duration
296 | Kind::Float
297 | Kind::Int
298 | Kind::Number
299 | Kind::Object
300 | Kind::String
301 | Kind::Uuid
302 | Kind::Regex
303 | Kind::Table(_)
304 | Kind::Record(_)
305 | Kind::Geometry(_)
306 | Kind::Function(_, _)
307 | Kind::Range
308 | Kind::Literal(_)
309 | Kind::File(_) => None,
310 Kind::Array(x, _) | Kind::Set(x, _) => Some(x.as_ref().clone()),
311 Kind::Either(x) => {
312 let kinds: Vec<Kind> = x.iter().filter_map(Self::inner_kind).collect();
315 if kinds.is_empty() {
316 None
317 } else {
318 Some(Kind::Either(kinds))
319 }
320 }
321 }
322 }
323
324 pub fn allows_nested_kind(&self, path: &[Part], kind: &Kind) -> bool {
325 if self.is_any() || kind.is_any() {
327 return true;
328 }
329
330 if !path.is_empty() {
331 match self {
332 Kind::Object => return matches!(path.first(), Some(Part::Field(_) | Part::All)),
333 Kind::Either(kinds) => {
334 return kinds
335 .iter()
336 .all(|k| matches!(k, Kind::None) || k.allows_nested_kind(path, kind));
337 }
338 Kind::Array(inner, len) | Kind::Set(inner, len) => {
339 return match path.first() {
340 Some(Part::All) => inner.allows_nested_kind(&path[1..], kind),
341 Some(Part::Value(Expr::Literal(Literal::Integer(i)))) => {
342 if let Some(len) = len
343 && *i >= *len as i64
344 {
345 return false;
346 }
347
348 inner.allows_nested_kind(&path[1..], kind)
349 }
350 _ => false,
351 };
352 }
353 _ => (),
354 }
355 }
356
357 match self {
358 _ if path.is_empty() && self == kind => true,
360 Kind::Literal(lit) => lit.allows_nested_kind(path, kind),
362 Kind::Either(kinds) => {
364 kinds.iter().all(|k| matches!(k, Kind::None) || k.allows_nested_kind(path, kind))
365 }
366 _ => false,
367 }
368 }
369
370 pub fn flatten(self) -> Vec<Kind> {
371 match self {
372 Kind::Either(x) => x.into_iter().flat_map(|k| k.flatten()).collect(),
373 _ => vec![self],
374 }
375 }
376
377 pub fn either(kinds: Vec<Kind>) -> Kind {
378 let mut seen = HashSet::new();
379 let mut kinds = kinds
380 .into_iter()
381 .flat_map(|k| k.flatten())
382 .filter(|k| seen.insert(k.clone()))
383 .collect::<Vec<_>>();
384 match kinds.len() {
385 0 => Kind::None,
386 1 => kinds.remove(0),
387 _ => Kind::Either(kinds),
388 }
389 }
390
391 pub fn option(kind: Kind) -> Kind {
392 Kind::either(vec![Kind::None, kind])
393 }
394}
395
396pub trait HasKind {
404 fn kind() -> Kind;
405}
406
407impl<T: HasKind> HasKind for Option<T> {
408 fn kind() -> Kind {
409 Kind::option(T::kind())
410 }
411}
412
413impl<T: HasKind> HasKind for Vec<T> {
414 fn kind() -> Kind {
415 let kind = T::kind();
416 Kind::Array(Box::new(kind), None)
417 }
418}
419
420impl HasKind for Array {
421 fn kind() -> Kind {
422 Kind::Array(Box::new(Kind::Any), None)
423 }
424}
425
426impl HasKind for Set {
427 fn kind() -> Kind {
428 Kind::Set(Box::new(Kind::Any), None)
429 }
430}
431
432impl<T: HasKind, const SIZE: usize> HasKind for [T; SIZE] {
433 fn kind() -> Kind {
434 let kind = T::kind();
435 Kind::Array(Box::new(kind), Some(SIZE as u64))
436 }
437}
438
439impl HasKind for RecordId {
440 fn kind() -> Kind {
441 Kind::Record(Vec::new())
442 }
443}
444
445impl HasKind for Geometry {
446 fn kind() -> Kind {
447 Kind::Geometry(Vec::new())
448 }
449}
450
451impl HasKind for Closure {
452 fn kind() -> Kind {
453 Kind::Function(None, None)
455 }
456}
457
458impl HasKind for Regex {
459 fn kind() -> Kind {
460 Kind::Regex
461 }
462}
463
464impl HasKind for File {
465 fn kind() -> Kind {
466 Kind::File(Vec::new())
467 }
468}
469
470impl HasKind for TableName {
471 fn kind() -> Kind {
472 Kind::Table(Vec::new())
473 }
474}
475
476macro_rules! impl_basic_has_kind{
477 ($($name:ident => $kind:ident),*$(,)?) => {
478 $(
479 impl HasKind for $name{
480 fn kind() -> Kind{
481 Kind::$kind
482 }
483 }
484 )*
485 }
486}
487
488impl_basic_has_kind! {
489 bool => Bool,
490
491 i64 => Int,
492 f64 => Float,
493 Decimal => Decimal,
494
495 String => String,
496 Bytes => Bytes,
497 Number => Number,
498 Datetime => Datetime,
499 Duration => Duration,
500 Uuid => Uuid,
501 Range => Range,
502}
503
504impl HasKind for crate::val::Object {
505 fn kind() -> Kind {
506 Kind::Object
507 }
508}
509
510macro_rules! impl_geometry_has_kind{
511 ($($name:ty => $kind:expr),*$(,)?) => {
512 $(
513 impl HasKind for $name{
514 fn kind() -> Kind{
515 Kind::Geometry(vec![$kind])
516 }
517 }
518 )*
519 }
520}
521impl_geometry_has_kind! {
522 Point<f64> => GeometryKind::Point,
523 LineString<f64> => GeometryKind::Line,
524 MultiPoint<f64> => GeometryKind::MultiPoint,
525 Polygon<f64> => GeometryKind::Polygon,
526 MultiLineString<f64> => GeometryKind::MultiLine,
527 MultiPolygon<f64> => GeometryKind::MultiPolygon,
528}
529
530impl From<&Kind> for Box<Kind> {
531 #[inline]
532 fn from(v: &Kind) -> Self {
533 Box::new(v.clone())
534 }
535}
536
537impl ToSql for Kind {
538 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
539 let kind: crate::sql::Kind = self.clone().into();
540 kind.fmt_sql(f, fmt);
541 }
542}
543
544impl Display for Kind {
545 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
546 write!(f, "{}", self.to_sql())
547 }
548}
549
550impl InfoStructure for Kind {
551 fn structure(self) -> Value {
552 self.to_string().into()
553 }
554}
555
556impl From<crate::types::PublicKind> for Kind {
557 fn from(v: crate::types::PublicKind) -> Self {
558 match v {
559 crate::types::PublicKind::Any => Kind::Any,
560 crate::types::PublicKind::None => Kind::None,
561 crate::types::PublicKind::Null => Kind::Null,
562 crate::types::PublicKind::Bool => Kind::Bool,
563 crate::types::PublicKind::Bytes => Kind::Bytes,
564 crate::types::PublicKind::Datetime => Kind::Datetime,
565 crate::types::PublicKind::Decimal => Kind::Decimal,
566 crate::types::PublicKind::Duration => Kind::Duration,
567 crate::types::PublicKind::Float => Kind::Float,
568 crate::types::PublicKind::Int => Kind::Int,
569 crate::types::PublicKind::Number => Kind::Number,
570 crate::types::PublicKind::Object => Kind::Object,
571 crate::types::PublicKind::String => Kind::String,
572 crate::types::PublicKind::Uuid => Kind::Uuid,
573 crate::types::PublicKind::Regex => Kind::Regex,
574 crate::types::PublicKind::Range => Kind::Range,
575 crate::types::PublicKind::Table(table) => {
576 Kind::Table(table.into_iter().map(IntoTableName::into_table_name).collect())
577 }
578 crate::types::PublicKind::Record(tables) => {
579 Kind::Record(tables.into_iter().map(IntoTableName::into_table_name).collect())
580 }
581 crate::types::PublicKind::Geometry(kinds) => {
582 Kind::Geometry(kinds.into_iter().map(Into::into).collect())
583 }
584 crate::types::PublicKind::Either(kinds) => {
585 Kind::Either(kinds.into_iter().map(Kind::from).collect())
586 }
587 crate::types::PublicKind::Set(kind, size) => {
588 Kind::Set(Box::new(Kind::from(*kind)), size)
589 }
590 crate::types::PublicKind::Array(kind, size) => {
591 Kind::Array(Box::new(Kind::from(*kind)), size)
592 }
593 crate::types::PublicKind::Function(args, ret) => Kind::Function(
594 args.map(|a| a.into_iter().map(Kind::from).collect()),
595 ret.map(|r| Box::new(Kind::from(*r))),
596 ),
597 crate::types::PublicKind::File(bucket) => Kind::File(bucket),
598 crate::types::PublicKind::Literal(lit) => Kind::Literal(lit.into()),
599 }
600 }
601}
602
603impl From<Kind> for crate::types::PublicKind {
604 fn from(v: Kind) -> Self {
605 match v {
606 Kind::Any => crate::types::PublicKind::Any,
607 Kind::None => crate::types::PublicKind::None,
608 Kind::Null => crate::types::PublicKind::Null,
609 Kind::Bool => crate::types::PublicKind::Bool,
610 Kind::Bytes => crate::types::PublicKind::Bytes,
611 Kind::Datetime => crate::types::PublicKind::Datetime,
612 Kind::Decimal => crate::types::PublicKind::Decimal,
613 Kind::Duration => crate::types::PublicKind::Duration,
614 Kind::Float => crate::types::PublicKind::Float,
615 Kind::Int => crate::types::PublicKind::Int,
616 Kind::Number => crate::types::PublicKind::Number,
617 Kind::Object => crate::types::PublicKind::Object,
618 Kind::String => crate::types::PublicKind::String,
619 Kind::Uuid => crate::types::PublicKind::Uuid,
620 Kind::Regex => crate::types::PublicKind::Regex,
621 Kind::Range => crate::types::PublicKind::Range,
622 Kind::Table(tables) => crate::types::PublicKind::Table(
623 tables.into_iter().map(IntoPublicTable::into_public_table).collect(),
624 ),
625 Kind::Record(tables) => crate::types::PublicKind::Record(
626 tables.into_iter().map(IntoPublicTable::into_public_table).collect(),
627 ),
628 Kind::Geometry(kinds) => {
629 crate::types::PublicKind::Geometry(kinds.into_iter().map(Into::into).collect())
630 }
631 Kind::Either(kinds) => {
632 crate::types::PublicKind::Either(kinds.into_iter().map(Into::into).collect())
633 }
634 Kind::Set(kind, size) => crate::types::PublicKind::Set(Box::new((*kind).into()), size),
635 Kind::Array(kind, size) => {
636 crate::types::PublicKind::Array(Box::new((*kind).into()), size)
637 }
638 Kind::Function(args, ret) => crate::types::PublicKind::Function(
639 args.map(|a| a.into_iter().map(Into::into).collect()),
640 ret.map(|r| Box::new((*r).into())),
641 ),
642 Kind::File(bucket) => crate::types::PublicKind::File(bucket),
643 Kind::Literal(lit) => crate::types::PublicKind::Literal(lit.into()),
644 }
645 }
646}
647
648#[revisioned(revision = 1)]
649#[derive(Clone, Debug)]
650pub enum KindLiteral {
651 String(Strand),
652 Integer(i64),
653 Float(f64),
654 Decimal(Decimal),
655 Duration(Duration),
656 Array(Vec<Kind>),
657 Object(BTreeMap<Strand, Kind>),
658 Bool(bool),
662}
663
664impl From<crate::types::PublicKindLiteral> for KindLiteral {
665 fn from(v: crate::types::PublicKindLiteral) -> Self {
666 match v {
667 crate::types::PublicKindLiteral::String(s) => KindLiteral::String(s.into()),
668 crate::types::PublicKindLiteral::Integer(i) => KindLiteral::Integer(i),
669 crate::types::PublicKindLiteral::Float(f) => KindLiteral::Float(f),
670 crate::types::PublicKindLiteral::Decimal(d) => KindLiteral::Decimal(d),
671 crate::types::PublicKindLiteral::Duration(d) => {
672 KindLiteral::Duration(crate::val::Duration(*d))
673 }
674 crate::types::PublicKindLiteral::Array(kinds) => {
675 KindLiteral::Array(kinds.into_iter().map(Kind::from).collect())
676 }
677 crate::types::PublicKindLiteral::Object(obj) => KindLiteral::Object(
678 obj.into_iter().map(|(k, v)| (k.into(), Kind::from(v))).collect(),
679 ),
680 crate::types::PublicKindLiteral::Bool(b) => KindLiteral::Bool(b),
681 }
682 }
683}
684
685impl From<KindLiteral> for crate::types::PublicKindLiteral {
686 fn from(v: KindLiteral) -> Self {
687 match v {
688 KindLiteral::String(s) => crate::types::PublicKindLiteral::String(s.into_string()),
689 KindLiteral::Integer(i) => crate::types::PublicKindLiteral::Integer(i),
690 KindLiteral::Float(f) => crate::types::PublicKindLiteral::Float(f),
691 KindLiteral::Decimal(d) => crate::types::PublicKindLiteral::Decimal(d),
692 KindLiteral::Duration(d) => {
693 crate::types::PublicKindLiteral::Duration(crate::types::PublicDuration::from(d.0))
694 }
695 KindLiteral::Array(kinds) => {
696 crate::types::PublicKindLiteral::Array(kinds.into_iter().map(Into::into).collect())
697 }
698 KindLiteral::Object(obj) => crate::types::PublicKindLiteral::Object(
699 obj.into_iter().map(|(k, v)| (k.into_string(), v.into())).collect(),
700 ),
701 KindLiteral::Bool(b) => crate::types::PublicKindLiteral::Bool(b),
702 }
703 }
704}
705
706impl PartialEq for KindLiteral {
707 fn eq(&self, other: &Self) -> bool {
708 match self {
709 KindLiteral::String(strand) => {
710 if let KindLiteral::String(other) = other {
711 strand == other
712 } else {
713 false
714 }
715 }
716 KindLiteral::Integer(x) => {
717 if let KindLiteral::Integer(other) = other {
718 x == other
719 } else {
720 false
721 }
722 }
723 KindLiteral::Float(x) => {
724 if let KindLiteral::Float(other) = other {
725 x.to_bits() == other.to_bits()
726 } else {
727 false
728 }
729 }
730 KindLiteral::Decimal(decimal) => {
731 if let KindLiteral::Decimal(other) = other {
732 decimal == other
733 } else {
734 false
735 }
736 }
737 KindLiteral::Duration(duration) => {
738 if let KindLiteral::Duration(other) = other {
739 duration == other
740 } else {
741 false
742 }
743 }
744 KindLiteral::Array(kinds) => {
745 if let KindLiteral::Array(other) = other {
746 kinds == other
747 } else {
748 false
749 }
750 }
751 KindLiteral::Object(btree_map) => {
752 if let KindLiteral::Object(other) = other {
753 btree_map == other
754 } else {
755 false
756 }
757 }
758 KindLiteral::Bool(a) => {
768 if let KindLiteral::Bool(b) = other {
769 a == b
770 } else {
771 false
772 }
773 }
774 }
775 }
776}
777impl Eq for KindLiteral {}
778impl Hash for KindLiteral {
779 fn hash<H: Hasher>(&self, state: &mut H) {
780 std::mem::discriminant(self).hash(state);
781 match self {
782 KindLiteral::String(strand) => strand.hash(state),
783 KindLiteral::Integer(x) => x.hash(state),
784 KindLiteral::Float(x) => x.to_bits().hash(state),
785 KindLiteral::Decimal(decimal) => decimal.hash(state),
786 KindLiteral::Duration(duration) => duration.hash(state),
787 KindLiteral::Array(kinds) => kinds.hash(state),
788 KindLiteral::Object(btree_map) => btree_map.hash(state),
789 KindLiteral::Bool(x) => x.hash(state),
796 }
797 }
798}
799
800impl KindLiteral {
801 pub fn to_kind(&self) -> Kind {
802 match self {
803 Self::String(_) => Kind::String,
804 Self::Integer(_) | Self::Float(_) | Self::Decimal(_) => Kind::Number,
805 Self::Duration(_) => Kind::Duration,
806 Self::Array(a) => {
807 if let Some(inner) = a.first()
808 && a.iter().all(|x| x == inner)
809 {
810 return Kind::Array(Box::new(inner.to_owned()), Some(a.len() as u64));
811 }
812
813 Kind::Array(Box::new(Kind::Any), None)
814 }
815 Self::Object(_) => Kind::Object,
816 Self::Bool(_) => Kind::Bool,
818 }
819 }
820
821 pub fn validate_value(&self, value: &Value) -> bool {
822 match self {
823 Self::String(v) => match value {
824 Value::String(s) => s == v,
825 _ => false,
826 },
827 Self::Integer(v) => match value {
828 Value::Number(n) => *n == Number::Int(*v),
829 _ => false,
830 },
831 Self::Float(v) => match value {
832 Value::Number(n) => *n == Number::Float(*v),
833 _ => false,
834 },
835 Self::Decimal(v) => match value {
836 Value::Number(n) => *n == Number::Decimal(*v),
837 _ => false,
838 },
839 Self::Duration(v) => match value {
840 Value::Duration(n) => n == v,
841 _ => false,
842 },
843 Self::Bool(v) => match value {
844 Value::Bool(b) => b == v,
845 _ => false,
846 },
847 Self::Array(a) => match value {
848 Value::Array(x) => {
849 if a.len() != x.len() {
850 return false;
851 }
852
853 for (i, inner) in a.iter().enumerate() {
854 if let Some(value) = x.get(i) {
855 if !value.can_coerce_to_kind(inner) {
856 return false;
857 }
858 } else {
859 return false;
860 }
861 }
862
863 true
864 }
865 _ => false,
866 },
867 Self::Object(lit) => match value {
868 Value::Object(val) => {
869 let mut lit_iter = lit.iter();
870 let mut val_iter = val.iter();
871
872 let mut lit_next = lit_iter.next();
873 let mut val_next = val_iter.next();
874
875 while lit_next.is_some() || val_next.is_some() {
876 match (lit_next, val_next) {
877 (Some((lit_k, lit_kind)), Some((val_k, val_v))) => {
878 match lit_k.as_str().cmp(val_k.as_str()) {
879 Ordering::Less => {
880 if !lit_kind.can_be_none() {
882 return false;
883 }
884 lit_next = lit_iter.next();
885 }
886 Ordering::Equal => {
887 if !val_v.can_coerce_to_kind(lit_kind) {
889 return false;
890 }
891 lit_next = lit_iter.next();
892 val_next = val_iter.next();
893 }
894 Ordering::Greater => {
895 return false;
897 }
898 }
899 }
900 (Some((_, lit_kind)), None) => {
901 if !lit_kind.can_be_none() {
903 return false;
904 }
905 lit_next = lit_iter.next();
906 }
907 (None, Some(_)) => {
908 return false;
910 }
911 (None, None) => break,
912 }
913 }
914
915 true
916 }
917 _ => false,
918 },
919 }
951 }
952
953 pub fn allows_nested_kind(&self, path: &[Part], kind: &Kind) -> bool {
954 if kind.is_any() {
956 return true;
957 }
958
959 if path.is_empty() {
962 return match kind {
963 Kind::Literal(lit) => self == lit,
964 _ => &self.to_kind() == kind,
965 };
966 }
967
968 match self {
969 KindLiteral::Array(x) => match path.first() {
970 Some(Part::All) => x.iter().all(|y| y.allows_nested_kind(&path[1..], kind)),
971 Some(part) => part
972 .as_old_index()
973 .and_then(|idx| x.get(idx))
974 .map(|x| x.allows_nested_kind(&path[1..], kind))
975 .unwrap_or(false),
976 None => false,
977 },
978 KindLiteral::Object(x) => match path.first() {
979 Some(Part::All) => x.iter().all(|(_, y)| y.allows_nested_kind(&path[1..], kind)),
980 Some(Part::Field(k)) => {
981 if let Some(y) = x.get(k.as_str()) {
982 y.allows_nested_kind(&path[1..], kind)
983 } else {
984 false
985 }
986 }
987 _ => false,
988 },
989 _ => false,
1005 }
1006 }
1007}
1008
1009impl ToSql for KindLiteral {
1010 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
1011 let lit: crate::sql::kind::KindLiteral = self.clone().into();
1012 lit.fmt_sql(f, fmt)
1013 }
1014}
1015
1016#[cfg(test)]
1017mod reference_target_tests {
1018 use super::*;
1019
1020 fn tb(name: &str) -> TableName {
1021 name.into()
1022 }
1023
1024 #[test]
1025 fn typed_record_targets_only_its_table() {
1026 let k = Kind::Record(vec![tb("person")]);
1027 assert!(k.reference_can_target(&tb("person")));
1028 assert!(!k.reference_can_target(&tb("comment")));
1029 }
1030
1031 #[test]
1032 fn untyped_record_targets_any_table() {
1033 let k = Kind::Record(Vec::new());
1036 assert!(k.reference_can_target(&tb("person")));
1037 assert!(k.reference_can_target(&tb("anything")));
1038 }
1039
1040 #[test]
1041 fn multi_table_record_targets_each() {
1042 let k = Kind::Record(vec![tb("person"), tb("robot")]);
1043 assert!(k.reference_can_target(&tb("person")));
1044 assert!(k.reference_can_target(&tb("robot")));
1045 assert!(!k.reference_can_target(&tb("comment")));
1046 }
1047
1048 #[test]
1049 fn array_and_set_of_records() {
1050 let arr = Kind::Array(Box::new(Kind::Record(vec![tb("house")])), None);
1051 assert!(arr.reference_can_target(&tb("house")));
1052 assert!(!arr.reference_can_target(&tb("person")));
1053
1054 let set = Kind::Set(Box::new(Kind::Record(vec![tb("house")])), None);
1055 assert!(set.reference_can_target(&tb("house")));
1056 assert!(!set.reference_can_target(&tb("person")));
1057 }
1058
1059 #[test]
1060 fn option_record_is_either_none_record() {
1061 let k = Kind::Either(vec![Kind::None, Kind::Record(vec![tb("person")])]);
1063 assert!(k.reference_can_target(&tb("person")));
1064 assert!(!k.reference_can_target(&tb("comment")));
1065 }
1066
1067 #[test]
1068 fn either_of_records_targets_each() {
1069 let k =
1070 Kind::Either(vec![Kind::Record(vec![tb("person")]), Kind::Record(vec![tb("robot")])]);
1071 assert!(k.reference_can_target(&tb("person")));
1072 assert!(k.reference_can_target(&tb("robot")));
1073 assert!(!k.reference_can_target(&tb("comment")));
1074 }
1075
1076 #[test]
1077 fn literal_array_of_records() {
1078 let k = Kind::Literal(KindLiteral::Array(vec![Kind::Record(vec![tb("a")])]));
1079 assert!(k.reference_can_target(&tb("a")));
1080 assert!(!k.reference_can_target(&tb("b")));
1081 }
1082
1083 #[test]
1084 fn nested_option_array_record() {
1085 let k = Kind::Either(vec![
1087 Kind::None,
1088 Kind::Array(Box::new(Kind::Record(vec![tb("house")])), None),
1089 ]);
1090 assert!(k.reference_can_target(&tb("house")));
1091 assert!(!k.reference_can_target(&tb("person")));
1092 }
1093
1094 #[test]
1095 fn non_record_kinds_never_target() {
1096 assert!(!Kind::String.reference_can_target(&tb("person")));
1097 assert!(!Kind::Int.reference_can_target(&tb("person")));
1098 assert!(!Kind::None.reference_can_target(&tb("person")));
1099 assert!(!Kind::Object.reference_can_target(&tb("person")));
1100 }
1101
1102 #[test]
1103 fn any_kind_matches_defensively() {
1104 assert!(Kind::Any.reference_can_target(&tb("person")));
1107 }
1108
1109 fn collect(kind: &Kind) -> (bool, HashSet<TableName>) {
1110 let mut out = HashSet::new();
1111 let unbounded = kind.collect_reference_target_tables(&mut out);
1112 (unbounded, out)
1113 }
1114
1115 #[test]
1116 fn collect_typed_record() {
1117 let (unbounded, tables) = collect(&Kind::Record(vec![tb("person")]));
1118 assert!(!unbounded);
1119 assert_eq!(tables, HashSet::from([tb("person")]));
1120 }
1121
1122 #[test]
1123 fn collect_untyped_record_is_unbounded() {
1124 let (unbounded, _) = collect(&Kind::Record(Vec::new()));
1125 assert!(unbounded);
1126 }
1127
1128 #[test]
1129 fn collect_either_union_and_containers() {
1130 let k = Kind::Either(vec![
1132 Kind::Array(Box::new(Kind::Record(vec![tb("a")])), None),
1133 Kind::Record(vec![tb("b")]),
1134 ]);
1135 let (unbounded, tables) = collect(&k);
1136 assert!(!unbounded);
1137 assert_eq!(tables, HashSet::from([tb("a"), tb("b")]));
1138 }
1139
1140 #[test]
1141 fn collect_mixed_typed_and_untyped_is_unbounded() {
1142 let k = Kind::Either(vec![Kind::Record(vec![tb("a")]), Kind::Record(Vec::new())]);
1145 let (unbounded, tables) = collect(&k);
1146 assert!(unbounded);
1147 assert!(tables.contains(&tb("a")));
1148 }
1149
1150 #[test]
1151 fn collect_non_record_is_empty() {
1152 let (unbounded, tables) = collect(&Kind::String);
1153 assert!(!unbounded);
1154 assert!(tables.is_empty());
1155 }
1156}
1157
1158#[cfg(test)]
1166mod round_trip_tests {
1167 use rstest::rstest;
1168 use surrealdb_types::ToSql;
1169
1170 use super::*;
1171
1172 fn tb(name: &str) -> TableName {
1173 name.into()
1174 }
1175
1176 fn round_trip(k: &Kind) {
1177 let text = k.to_sql();
1178 let reparsed: Kind = crate::syn::kind_for_definition(&text)
1179 .unwrap_or_else(|e| panic!("failed to reparse kind text {text:?}: {e}"))
1180 .into();
1181 assert_eq!(&reparsed, k, "round trip mismatch via text {text:?}");
1182 }
1183
1184 #[rstest]
1185 #[case::any(Kind::Any)]
1186 #[case::none(Kind::None)]
1187 #[case::null(Kind::Null)]
1188 #[case::bool(Kind::Bool)]
1189 #[case::bytes(Kind::Bytes)]
1190 #[case::datetime(Kind::Datetime)]
1191 #[case::decimal(Kind::Decimal)]
1192 #[case::duration(Kind::Duration)]
1193 #[case::float(Kind::Float)]
1194 #[case::int(Kind::Int)]
1195 #[case::number(Kind::Number)]
1196 #[case::object(Kind::Object)]
1197 #[case::string(Kind::String)]
1198 #[case::uuid(Kind::Uuid)]
1199 #[case::regex(Kind::Regex)]
1200 #[case::range(Kind::Range)]
1201 #[case::table_untyped(Kind::Table(vec![]))]
1202 #[case::table_typed(Kind::Table(vec![tb("person"), tb("robot")]))]
1203 #[case::record_untyped(Kind::Record(vec![]))]
1204 #[case::record_typed(Kind::Record(vec![tb("person")]))]
1205 #[case::geometry_untyped(Kind::Geometry(vec![]))]
1206 #[case::geometry_typed(Kind::Geometry(vec![GeometryKind::Point, GeometryKind::MultiPolygon]))]
1207 #[case::either(Kind::Either(vec![Kind::String, Kind::Int, Kind::None]))]
1208 #[case::option_record(Kind::Either(vec![Kind::None, Kind::Record(vec![tb("person")])]))]
1209 #[case::set_unbounded(Kind::Set(Box::new(Kind::String), None))]
1210 #[case::set_bounded(Kind::Set(Box::new(Kind::Int), Some(10)))]
1211 #[case::array_unbounded(Kind::Array(Box::new(Kind::Any), None))]
1212 #[case::array_bounded(Kind::Array(Box::new(Kind::Record(vec![tb("a")])), Some(3)))]
1213 #[case::nested_array(Kind::Array(Box::new(Kind::Array(Box::new(Kind::Int), None)), None))]
1214 #[case::function_untyped(Kind::Function(None, None))]
1220 #[case::file_untyped(Kind::File(vec![]))]
1221 #[case::file_typed(Kind::File(vec!["bucket1".to_string(), "bucket2".to_string()]))]
1222 #[case::literal_string(Kind::Literal(KindLiteral::String(Strand::new_static("a"))))]
1223 #[case::literal_integer(Kind::Literal(KindLiteral::Integer(42)))]
1224 #[case::literal_float(Kind::Literal(KindLiteral::Float(1.5)))]
1225 #[case::literal_decimal(Kind::Literal(KindLiteral::Decimal(Decimal::new(125, 2))))]
1226 #[case::literal_duration(Kind::Literal(KindLiteral::Duration(Duration(
1227 std::time::Duration::from_secs(3600)
1228 ))))]
1229 #[case::literal_bool(Kind::Literal(KindLiteral::Bool(true)))]
1230 #[case::literal_array(Kind::Literal(KindLiteral::Array(vec![Kind::String, Kind::Int])))]
1231 #[case::literal_object(Kind::Literal(KindLiteral::Object(BTreeMap::from([(
1232 Strand::new_static("a"),
1233 Kind::String,
1234 )]))))]
1235 #[case::deeply_nested(Kind::Either(vec![
1236 Kind::Array(Box::new(Kind::Record(vec![tb("a"), tb("b")])), Some(5)),
1237 Kind::Set(Box::new(Kind::Literal(KindLiteral::Integer(1))), None),
1238 Kind::None,
1239 ]))]
1240 fn kind_round_trips_through_text(#[case] k: Kind) {
1241 round_trip(&k);
1242 }
1243}