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 {
273 match self {
274 Kind::Any | Kind::Object | Kind::Array(..) | Kind::Set(..) => true,
275 Kind::Literal(KindLiteral::Object(_) | KindLiteral::Array(_)) => true,
276 Kind::Either(kinds) => {
277 kinds.iter().any(|k| k.allows_sub_fields())
278 && kinds
279 .iter()
280 .all(|k| matches!(k, Kind::None | Kind::Null) || k.allows_sub_fields())
281 }
282 _ => false,
283 }
284 }
285
286 pub fn inner_kind(&self) -> Option<Kind> {
291 match self {
292 Kind::Any
293 | Kind::None
294 | Kind::Null
295 | Kind::Bool
296 | Kind::Bytes
297 | Kind::Datetime
298 | Kind::Decimal
299 | Kind::Duration
300 | Kind::Float
301 | Kind::Int
302 | Kind::Number
303 | Kind::Object
304 | Kind::String
305 | Kind::Uuid
306 | Kind::Regex
307 | Kind::Table(_)
308 | Kind::Record(_)
309 | Kind::Geometry(_)
310 | Kind::Function(_, _)
311 | Kind::Range
312 | Kind::Literal(_)
313 | Kind::File(_) => None,
314 Kind::Array(x, _) | Kind::Set(x, _) => Some(x.as_ref().clone()),
315 Kind::Either(x) => {
316 let kinds: Vec<Kind> = x.iter().filter_map(Self::inner_kind).collect();
319 if kinds.is_empty() {
320 None
321 } else {
322 Some(Kind::Either(kinds))
323 }
324 }
325 }
326 }
327
328 pub fn allows_nested_kind(&self, path: &[Part], kind: &Kind) -> bool {
329 if self.is_any() || kind.is_any() {
331 return true;
332 }
333
334 if !path.is_empty() {
335 match self {
336 Kind::Object => return matches!(path.first(), Some(Part::Field(_) | Part::All)),
337 Kind::Either(kinds) => {
338 return kinds
339 .iter()
340 .all(|k| matches!(k, Kind::None) || k.allows_nested_kind(path, kind));
341 }
342 Kind::Array(inner, len) | Kind::Set(inner, len) => {
343 return match path.first() {
344 Some(Part::All) => inner.allows_nested_kind(&path[1..], kind),
345 Some(Part::Value(Expr::Literal(Literal::Integer(i)))) => {
346 if let Some(len) = len
347 && *i >= *len as i64
348 {
349 return false;
350 }
351
352 inner.allows_nested_kind(&path[1..], kind)
353 }
354 _ => false,
355 };
356 }
357 _ => (),
358 }
359 }
360
361 match self {
362 _ if path.is_empty() && self == kind => true,
364 Kind::Literal(lit) => lit.allows_nested_kind(path, kind),
366 Kind::Either(kinds) => {
368 kinds.iter().all(|k| matches!(k, Kind::None) || k.allows_nested_kind(path, kind))
369 }
370 _ => false,
371 }
372 }
373
374 pub fn flatten(self) -> Vec<Kind> {
375 match self {
376 Kind::Either(x) => x.into_iter().flat_map(|k| k.flatten()).collect(),
377 _ => vec![self],
378 }
379 }
380
381 pub fn either(kinds: Vec<Kind>) -> Kind {
382 let mut seen = HashSet::new();
383 let mut kinds = kinds
384 .into_iter()
385 .flat_map(|k| k.flatten())
386 .filter(|k| seen.insert(k.clone()))
387 .collect::<Vec<_>>();
388 match kinds.len() {
389 0 => Kind::None,
390 1 => kinds.remove(0),
391 _ => Kind::Either(kinds),
392 }
393 }
394
395 pub fn option(kind: Kind) -> Kind {
396 Kind::either(vec![Kind::None, kind])
397 }
398}
399
400pub trait HasKind {
408 fn kind() -> Kind;
409}
410
411impl<T: HasKind> HasKind for Option<T> {
412 fn kind() -> Kind {
413 Kind::option(T::kind())
414 }
415}
416
417impl<T: HasKind> HasKind for Vec<T> {
418 fn kind() -> Kind {
419 let kind = T::kind();
420 Kind::Array(Box::new(kind), None)
421 }
422}
423
424impl HasKind for Array {
425 fn kind() -> Kind {
426 Kind::Array(Box::new(Kind::Any), None)
427 }
428}
429
430impl HasKind for Set {
431 fn kind() -> Kind {
432 Kind::Set(Box::new(Kind::Any), None)
433 }
434}
435
436impl<T: HasKind, const SIZE: usize> HasKind for [T; SIZE] {
437 fn kind() -> Kind {
438 let kind = T::kind();
439 Kind::Array(Box::new(kind), Some(SIZE as u64))
440 }
441}
442
443impl HasKind for RecordId {
444 fn kind() -> Kind {
445 Kind::Record(Vec::new())
446 }
447}
448
449impl HasKind for Geometry {
450 fn kind() -> Kind {
451 Kind::Geometry(Vec::new())
452 }
453}
454
455impl HasKind for Closure {
456 fn kind() -> Kind {
457 Kind::Function(None, None)
459 }
460}
461
462impl HasKind for Regex {
463 fn kind() -> Kind {
464 Kind::Regex
465 }
466}
467
468impl HasKind for File {
469 fn kind() -> Kind {
470 Kind::File(Vec::new())
471 }
472}
473
474impl HasKind for TableName {
475 fn kind() -> Kind {
476 Kind::Table(Vec::new())
477 }
478}
479
480macro_rules! impl_basic_has_kind{
481 ($($name:ident => $kind:ident),*$(,)?) => {
482 $(
483 impl HasKind for $name{
484 fn kind() -> Kind{
485 Kind::$kind
486 }
487 }
488 )*
489 }
490}
491
492impl_basic_has_kind! {
493 bool => Bool,
494
495 i64 => Int,
496 f64 => Float,
497 Decimal => Decimal,
498
499 String => String,
500 Bytes => Bytes,
501 Number => Number,
502 Datetime => Datetime,
503 Duration => Duration,
504 Uuid => Uuid,
505 Range => Range,
506}
507
508impl HasKind for crate::val::Object {
509 fn kind() -> Kind {
510 Kind::Object
511 }
512}
513
514macro_rules! impl_geometry_has_kind{
515 ($($name:ty => $kind:expr),*$(,)?) => {
516 $(
517 impl HasKind for $name{
518 fn kind() -> Kind{
519 Kind::Geometry(vec![$kind])
520 }
521 }
522 )*
523 }
524}
525impl_geometry_has_kind! {
526 Point<f64> => GeometryKind::Point,
527 LineString<f64> => GeometryKind::Line,
528 MultiPoint<f64> => GeometryKind::MultiPoint,
529 Polygon<f64> => GeometryKind::Polygon,
530 MultiLineString<f64> => GeometryKind::MultiLine,
531 MultiPolygon<f64> => GeometryKind::MultiPolygon,
532}
533
534impl From<&Kind> for Box<Kind> {
535 #[inline]
536 fn from(v: &Kind) -> Self {
537 Box::new(v.clone())
538 }
539}
540
541impl ToSql for Kind {
542 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
543 let kind: crate::sql::Kind = self.clone().into();
544 kind.fmt_sql(f, fmt);
545 }
546}
547
548impl Display for Kind {
549 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
550 write!(f, "{}", self.to_sql())
551 }
552}
553
554impl InfoStructure for Kind {
555 fn structure(self) -> Value {
556 self.to_string().into()
557 }
558}
559
560impl From<crate::types::PublicKind> for Kind {
561 fn from(v: crate::types::PublicKind) -> Self {
562 match v {
563 crate::types::PublicKind::Any => Kind::Any,
564 crate::types::PublicKind::None => Kind::None,
565 crate::types::PublicKind::Null => Kind::Null,
566 crate::types::PublicKind::Bool => Kind::Bool,
567 crate::types::PublicKind::Bytes => Kind::Bytes,
568 crate::types::PublicKind::Datetime => Kind::Datetime,
569 crate::types::PublicKind::Decimal => Kind::Decimal,
570 crate::types::PublicKind::Duration => Kind::Duration,
571 crate::types::PublicKind::Float => Kind::Float,
572 crate::types::PublicKind::Int => Kind::Int,
573 crate::types::PublicKind::Number => Kind::Number,
574 crate::types::PublicKind::Object => Kind::Object,
575 crate::types::PublicKind::String => Kind::String,
576 crate::types::PublicKind::Uuid => Kind::Uuid,
577 crate::types::PublicKind::Regex => Kind::Regex,
578 crate::types::PublicKind::Range => Kind::Range,
579 crate::types::PublicKind::Table(table) => {
580 Kind::Table(table.into_iter().map(IntoTableName::into_table_name).collect())
581 }
582 crate::types::PublicKind::Record(tables) => {
583 Kind::Record(tables.into_iter().map(IntoTableName::into_table_name).collect())
584 }
585 crate::types::PublicKind::Geometry(kinds) => {
586 Kind::Geometry(kinds.into_iter().map(Into::into).collect())
587 }
588 crate::types::PublicKind::Either(kinds) => {
589 Kind::Either(kinds.into_iter().map(Kind::from).collect())
590 }
591 crate::types::PublicKind::Set(kind, size) => {
592 Kind::Set(Box::new(Kind::from(*kind)), size)
593 }
594 crate::types::PublicKind::Array(kind, size) => {
595 Kind::Array(Box::new(Kind::from(*kind)), size)
596 }
597 crate::types::PublicKind::Function(args, ret) => Kind::Function(
598 args.map(|a| a.into_iter().map(Kind::from).collect()),
599 ret.map(|r| Box::new(Kind::from(*r))),
600 ),
601 crate::types::PublicKind::File(bucket) => Kind::File(bucket),
602 crate::types::PublicKind::Literal(lit) => Kind::Literal(lit.into()),
603 }
604 }
605}
606
607impl From<Kind> for crate::types::PublicKind {
608 fn from(v: Kind) -> Self {
609 match v {
610 Kind::Any => crate::types::PublicKind::Any,
611 Kind::None => crate::types::PublicKind::None,
612 Kind::Null => crate::types::PublicKind::Null,
613 Kind::Bool => crate::types::PublicKind::Bool,
614 Kind::Bytes => crate::types::PublicKind::Bytes,
615 Kind::Datetime => crate::types::PublicKind::Datetime,
616 Kind::Decimal => crate::types::PublicKind::Decimal,
617 Kind::Duration => crate::types::PublicKind::Duration,
618 Kind::Float => crate::types::PublicKind::Float,
619 Kind::Int => crate::types::PublicKind::Int,
620 Kind::Number => crate::types::PublicKind::Number,
621 Kind::Object => crate::types::PublicKind::Object,
622 Kind::String => crate::types::PublicKind::String,
623 Kind::Uuid => crate::types::PublicKind::Uuid,
624 Kind::Regex => crate::types::PublicKind::Regex,
625 Kind::Range => crate::types::PublicKind::Range,
626 Kind::Table(tables) => crate::types::PublicKind::Table(
627 tables.into_iter().map(IntoPublicTable::into_public_table).collect(),
628 ),
629 Kind::Record(tables) => crate::types::PublicKind::Record(
630 tables.into_iter().map(IntoPublicTable::into_public_table).collect(),
631 ),
632 Kind::Geometry(kinds) => {
633 crate::types::PublicKind::Geometry(kinds.into_iter().map(Into::into).collect())
634 }
635 Kind::Either(kinds) => {
636 crate::types::PublicKind::Either(kinds.into_iter().map(Into::into).collect())
637 }
638 Kind::Set(kind, size) => crate::types::PublicKind::Set(Box::new((*kind).into()), size),
639 Kind::Array(kind, size) => {
640 crate::types::PublicKind::Array(Box::new((*kind).into()), size)
641 }
642 Kind::Function(args, ret) => crate::types::PublicKind::Function(
643 args.map(|a| a.into_iter().map(Into::into).collect()),
644 ret.map(|r| Box::new((*r).into())),
645 ),
646 Kind::File(bucket) => crate::types::PublicKind::File(bucket),
647 Kind::Literal(lit) => crate::types::PublicKind::Literal(lit.into()),
648 }
649 }
650}
651
652#[revisioned(revision = 1)]
653#[derive(Clone, Debug)]
654pub enum KindLiteral {
655 String(Strand),
656 Integer(i64),
657 Float(f64),
658 Decimal(Decimal),
659 Duration(Duration),
660 Array(Vec<Kind>),
661 Object(BTreeMap<Strand, Kind>),
662 Bool(bool),
666}
667
668impl From<crate::types::PublicKindLiteral> for KindLiteral {
669 fn from(v: crate::types::PublicKindLiteral) -> Self {
670 match v {
671 crate::types::PublicKindLiteral::String(s) => KindLiteral::String(s.into()),
672 crate::types::PublicKindLiteral::Integer(i) => KindLiteral::Integer(i),
673 crate::types::PublicKindLiteral::Float(f) => KindLiteral::Float(f),
674 crate::types::PublicKindLiteral::Decimal(d) => KindLiteral::Decimal(d),
675 crate::types::PublicKindLiteral::Duration(d) => {
676 KindLiteral::Duration(crate::val::Duration(*d))
677 }
678 crate::types::PublicKindLiteral::Array(kinds) => {
679 KindLiteral::Array(kinds.into_iter().map(Kind::from).collect())
680 }
681 crate::types::PublicKindLiteral::Object(obj) => KindLiteral::Object(
682 obj.into_iter().map(|(k, v)| (k.into(), Kind::from(v))).collect(),
683 ),
684 crate::types::PublicKindLiteral::Bool(b) => KindLiteral::Bool(b),
685 }
686 }
687}
688
689impl From<KindLiteral> for crate::types::PublicKindLiteral {
690 fn from(v: KindLiteral) -> Self {
691 match v {
692 KindLiteral::String(s) => crate::types::PublicKindLiteral::String(s.into_string()),
693 KindLiteral::Integer(i) => crate::types::PublicKindLiteral::Integer(i),
694 KindLiteral::Float(f) => crate::types::PublicKindLiteral::Float(f),
695 KindLiteral::Decimal(d) => crate::types::PublicKindLiteral::Decimal(d),
696 KindLiteral::Duration(d) => {
697 crate::types::PublicKindLiteral::Duration(crate::types::PublicDuration::from(d.0))
698 }
699 KindLiteral::Array(kinds) => {
700 crate::types::PublicKindLiteral::Array(kinds.into_iter().map(Into::into).collect())
701 }
702 KindLiteral::Object(obj) => crate::types::PublicKindLiteral::Object(
703 obj.into_iter().map(|(k, v)| (k.into_string(), v.into())).collect(),
704 ),
705 KindLiteral::Bool(b) => crate::types::PublicKindLiteral::Bool(b),
706 }
707 }
708}
709
710impl PartialEq for KindLiteral {
711 fn eq(&self, other: &Self) -> bool {
712 match self {
713 KindLiteral::String(strand) => {
714 if let KindLiteral::String(other) = other {
715 strand == other
716 } else {
717 false
718 }
719 }
720 KindLiteral::Integer(x) => {
721 if let KindLiteral::Integer(other) = other {
722 x == other
723 } else {
724 false
725 }
726 }
727 KindLiteral::Float(x) => {
728 if let KindLiteral::Float(other) = other {
729 x.to_bits() == other.to_bits()
730 } else {
731 false
732 }
733 }
734 KindLiteral::Decimal(decimal) => {
735 if let KindLiteral::Decimal(other) = other {
736 decimal == other
737 } else {
738 false
739 }
740 }
741 KindLiteral::Duration(duration) => {
742 if let KindLiteral::Duration(other) = other {
743 duration == other
744 } else {
745 false
746 }
747 }
748 KindLiteral::Array(kinds) => {
749 if let KindLiteral::Array(other) = other {
750 kinds == other
751 } else {
752 false
753 }
754 }
755 KindLiteral::Object(btree_map) => {
756 if let KindLiteral::Object(other) = other {
757 btree_map == other
758 } else {
759 false
760 }
761 }
762 KindLiteral::Bool(a) => {
772 if let KindLiteral::Bool(b) = other {
773 a == b
774 } else {
775 false
776 }
777 }
778 }
779 }
780}
781impl Eq for KindLiteral {}
782impl Hash for KindLiteral {
783 fn hash<H: Hasher>(&self, state: &mut H) {
784 std::mem::discriminant(self).hash(state);
785 match self {
786 KindLiteral::String(strand) => strand.hash(state),
787 KindLiteral::Integer(x) => x.hash(state),
788 KindLiteral::Float(x) => x.to_bits().hash(state),
789 KindLiteral::Decimal(decimal) => decimal.hash(state),
790 KindLiteral::Duration(duration) => duration.hash(state),
791 KindLiteral::Array(kinds) => kinds.hash(state),
792 KindLiteral::Object(btree_map) => btree_map.hash(state),
793 KindLiteral::Bool(x) => x.hash(state),
800 }
801 }
802}
803
804impl KindLiteral {
805 pub fn to_kind(&self) -> Kind {
806 match self {
807 Self::String(_) => Kind::String,
808 Self::Integer(_) | Self::Float(_) | Self::Decimal(_) => Kind::Number,
809 Self::Duration(_) => Kind::Duration,
810 Self::Array(a) => {
811 if let Some(inner) = a.first()
812 && a.iter().all(|x| x == inner)
813 {
814 return Kind::Array(Box::new(inner.to_owned()), Some(a.len() as u64));
815 }
816
817 Kind::Array(Box::new(Kind::Any), None)
818 }
819 Self::Object(_) => Kind::Object,
820 Self::Bool(_) => Kind::Bool,
822 }
823 }
824
825 pub fn validate_value(&self, value: &Value) -> bool {
826 match self {
827 Self::String(v) => match value {
828 Value::String(s) => s == v,
829 _ => false,
830 },
831 Self::Integer(v) => match value {
832 Value::Number(n) => *n == Number::Int(*v),
833 _ => false,
834 },
835 Self::Float(v) => match value {
836 Value::Number(n) => *n == Number::Float(*v),
837 _ => false,
838 },
839 Self::Decimal(v) => match value {
840 Value::Number(n) => *n == Number::Decimal(*v),
841 _ => false,
842 },
843 Self::Duration(v) => match value {
844 Value::Duration(n) => n == v,
845 _ => false,
846 },
847 Self::Bool(v) => match value {
848 Value::Bool(b) => b == v,
849 _ => false,
850 },
851 Self::Array(a) => match value {
852 Value::Array(x) => {
853 if a.len() != x.len() {
854 return false;
855 }
856
857 for (i, inner) in a.iter().enumerate() {
858 if let Some(value) = x.get(i) {
859 if !value.can_coerce_to_kind(inner) {
860 return false;
861 }
862 } else {
863 return false;
864 }
865 }
866
867 true
868 }
869 _ => false,
870 },
871 Self::Object(lit) => match value {
872 Value::Object(val) => {
873 let mut lit_iter = lit.iter();
874 let mut val_iter = val.iter();
875
876 let mut lit_next = lit_iter.next();
877 let mut val_next = val_iter.next();
878
879 while lit_next.is_some() || val_next.is_some() {
880 match (lit_next, val_next) {
881 (Some((lit_k, lit_kind)), Some((val_k, val_v))) => {
882 match lit_k.as_str().cmp(val_k.as_str()) {
883 Ordering::Less => {
884 if !lit_kind.can_be_none() {
886 return false;
887 }
888 lit_next = lit_iter.next();
889 }
890 Ordering::Equal => {
891 if !val_v.can_coerce_to_kind(lit_kind) {
893 return false;
894 }
895 lit_next = lit_iter.next();
896 val_next = val_iter.next();
897 }
898 Ordering::Greater => {
899 return false;
901 }
902 }
903 }
904 (Some((_, lit_kind)), None) => {
905 if !lit_kind.can_be_none() {
907 return false;
908 }
909 lit_next = lit_iter.next();
910 }
911 (None, Some(_)) => {
912 return false;
914 }
915 (None, None) => break,
916 }
917 }
918
919 true
920 }
921 _ => false,
922 },
923 }
955 }
956
957 pub fn allows_nested_kind(&self, path: &[Part], kind: &Kind) -> bool {
958 if kind.is_any() {
960 return true;
961 }
962
963 if path.is_empty() {
966 return match kind {
967 Kind::Literal(lit) => self == lit,
968 _ => &self.to_kind() == kind,
969 };
970 }
971
972 match self {
973 KindLiteral::Array(x) => match path.first() {
974 Some(Part::All) => x.iter().all(|y| y.allows_nested_kind(&path[1..], kind)),
975 Some(part) => part
976 .as_old_index()
977 .and_then(|idx| x.get(idx))
978 .map(|x| x.allows_nested_kind(&path[1..], kind))
979 .unwrap_or(false),
980 None => false,
981 },
982 KindLiteral::Object(x) => match path.first() {
983 Some(Part::All) => x.iter().all(|(_, y)| y.allows_nested_kind(&path[1..], kind)),
984 Some(Part::Field(k)) => {
985 if let Some(y) = x.get(k.as_str()) {
986 y.allows_nested_kind(&path[1..], kind)
987 } else {
988 false
989 }
990 }
991 _ => false,
992 },
993 _ => false,
1009 }
1010 }
1011}
1012
1013impl ToSql for KindLiteral {
1014 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
1015 let lit: crate::sql::kind::KindLiteral = self.clone().into();
1016 lit.fmt_sql(f, fmt)
1017 }
1018}
1019
1020#[cfg(test)]
1021mod reference_target_tests {
1022 use super::*;
1023
1024 fn tb(name: &str) -> TableName {
1025 name.into()
1026 }
1027
1028 #[test]
1029 fn typed_record_targets_only_its_table() {
1030 let k = Kind::Record(vec![tb("person")]);
1031 assert!(k.reference_can_target(&tb("person")));
1032 assert!(!k.reference_can_target(&tb("comment")));
1033 }
1034
1035 #[test]
1036 fn untyped_record_targets_any_table() {
1037 let k = Kind::Record(Vec::new());
1040 assert!(k.reference_can_target(&tb("person")));
1041 assert!(k.reference_can_target(&tb("anything")));
1042 }
1043
1044 #[test]
1045 fn multi_table_record_targets_each() {
1046 let k = Kind::Record(vec![tb("person"), tb("robot")]);
1047 assert!(k.reference_can_target(&tb("person")));
1048 assert!(k.reference_can_target(&tb("robot")));
1049 assert!(!k.reference_can_target(&tb("comment")));
1050 }
1051
1052 #[test]
1053 fn array_and_set_of_records() {
1054 let arr = Kind::Array(Box::new(Kind::Record(vec![tb("house")])), None);
1055 assert!(arr.reference_can_target(&tb("house")));
1056 assert!(!arr.reference_can_target(&tb("person")));
1057
1058 let set = Kind::Set(Box::new(Kind::Record(vec![tb("house")])), None);
1059 assert!(set.reference_can_target(&tb("house")));
1060 assert!(!set.reference_can_target(&tb("person")));
1061 }
1062
1063 #[test]
1064 fn option_record_is_either_none_record() {
1065 let k = Kind::Either(vec![Kind::None, Kind::Record(vec![tb("person")])]);
1067 assert!(k.reference_can_target(&tb("person")));
1068 assert!(!k.reference_can_target(&tb("comment")));
1069 }
1070
1071 #[test]
1072 fn either_of_records_targets_each() {
1073 let k =
1074 Kind::Either(vec![Kind::Record(vec![tb("person")]), Kind::Record(vec![tb("robot")])]);
1075 assert!(k.reference_can_target(&tb("person")));
1076 assert!(k.reference_can_target(&tb("robot")));
1077 assert!(!k.reference_can_target(&tb("comment")));
1078 }
1079
1080 #[test]
1081 fn literal_array_of_records() {
1082 let k = Kind::Literal(KindLiteral::Array(vec![Kind::Record(vec![tb("a")])]));
1083 assert!(k.reference_can_target(&tb("a")));
1084 assert!(!k.reference_can_target(&tb("b")));
1085 }
1086
1087 #[test]
1088 fn nested_option_array_record() {
1089 let k = Kind::Either(vec![
1091 Kind::None,
1092 Kind::Array(Box::new(Kind::Record(vec![tb("house")])), None),
1093 ]);
1094 assert!(k.reference_can_target(&tb("house")));
1095 assert!(!k.reference_can_target(&tb("person")));
1096 }
1097
1098 #[test]
1099 fn non_record_kinds_never_target() {
1100 assert!(!Kind::String.reference_can_target(&tb("person")));
1101 assert!(!Kind::Int.reference_can_target(&tb("person")));
1102 assert!(!Kind::None.reference_can_target(&tb("person")));
1103 assert!(!Kind::Object.reference_can_target(&tb("person")));
1104 }
1105
1106 #[test]
1107 fn any_kind_matches_defensively() {
1108 assert!(Kind::Any.reference_can_target(&tb("person")));
1111 }
1112
1113 fn collect(kind: &Kind) -> (bool, HashSet<TableName>) {
1114 let mut out = HashSet::new();
1115 let unbounded = kind.collect_reference_target_tables(&mut out);
1116 (unbounded, out)
1117 }
1118
1119 #[test]
1120 fn collect_typed_record() {
1121 let (unbounded, tables) = collect(&Kind::Record(vec![tb("person")]));
1122 assert!(!unbounded);
1123 assert_eq!(tables, HashSet::from([tb("person")]));
1124 }
1125
1126 #[test]
1127 fn collect_untyped_record_is_unbounded() {
1128 let (unbounded, _) = collect(&Kind::Record(Vec::new()));
1129 assert!(unbounded);
1130 }
1131
1132 #[test]
1133 fn collect_either_union_and_containers() {
1134 let k = Kind::Either(vec![
1136 Kind::Array(Box::new(Kind::Record(vec![tb("a")])), None),
1137 Kind::Record(vec![tb("b")]),
1138 ]);
1139 let (unbounded, tables) = collect(&k);
1140 assert!(!unbounded);
1141 assert_eq!(tables, HashSet::from([tb("a"), tb("b")]));
1142 }
1143
1144 #[test]
1145 fn collect_mixed_typed_and_untyped_is_unbounded() {
1146 let k = Kind::Either(vec![Kind::Record(vec![tb("a")]), Kind::Record(Vec::new())]);
1149 let (unbounded, tables) = collect(&k);
1150 assert!(unbounded);
1151 assert!(tables.contains(&tb("a")));
1152 }
1153
1154 #[test]
1155 fn collect_non_record_is_empty() {
1156 let (unbounded, tables) = collect(&Kind::String);
1157 assert!(!unbounded);
1158 assert!(tables.is_empty());
1159 }
1160}
1161
1162#[cfg(test)]
1170mod round_trip_tests {
1171 use rstest::rstest;
1172 use surrealdb_types::ToSql;
1173
1174 use super::*;
1175
1176 fn tb(name: &str) -> TableName {
1177 name.into()
1178 }
1179
1180 fn round_trip(k: &Kind) {
1181 let text = k.to_sql();
1182 let reparsed: Kind = crate::syn::kind_for_definition(&text)
1183 .unwrap_or_else(|e| panic!("failed to reparse kind text {text:?}: {e}"))
1184 .into();
1185 assert_eq!(&reparsed, k, "round trip mismatch via text {text:?}");
1186 }
1187
1188 #[rstest]
1189 #[case::any(Kind::Any)]
1190 #[case::none(Kind::None)]
1191 #[case::null(Kind::Null)]
1192 #[case::bool(Kind::Bool)]
1193 #[case::bytes(Kind::Bytes)]
1194 #[case::datetime(Kind::Datetime)]
1195 #[case::decimal(Kind::Decimal)]
1196 #[case::duration(Kind::Duration)]
1197 #[case::float(Kind::Float)]
1198 #[case::int(Kind::Int)]
1199 #[case::number(Kind::Number)]
1200 #[case::object(Kind::Object)]
1201 #[case::string(Kind::String)]
1202 #[case::uuid(Kind::Uuid)]
1203 #[case::regex(Kind::Regex)]
1204 #[case::range(Kind::Range)]
1205 #[case::table_untyped(Kind::Table(vec![]))]
1206 #[case::table_typed(Kind::Table(vec![tb("person"), tb("robot")]))]
1207 #[case::record_untyped(Kind::Record(vec![]))]
1208 #[case::record_typed(Kind::Record(vec![tb("person")]))]
1209 #[case::geometry_untyped(Kind::Geometry(vec![]))]
1210 #[case::geometry_typed(Kind::Geometry(vec![GeometryKind::Point, GeometryKind::MultiPolygon]))]
1211 #[case::either(Kind::Either(vec![Kind::String, Kind::Int, Kind::None]))]
1212 #[case::option_record(Kind::Either(vec![Kind::None, Kind::Record(vec![tb("person")])]))]
1213 #[case::set_unbounded(Kind::Set(Box::new(Kind::String), None))]
1214 #[case::set_bounded(Kind::Set(Box::new(Kind::Int), Some(10)))]
1215 #[case::array_unbounded(Kind::Array(Box::new(Kind::Any), None))]
1216 #[case::array_bounded(Kind::Array(Box::new(Kind::Record(vec![tb("a")])), Some(3)))]
1217 #[case::nested_array(Kind::Array(Box::new(Kind::Array(Box::new(Kind::Int), None)), None))]
1218 #[case::function_untyped(Kind::Function(None, None))]
1224 #[case::file_untyped(Kind::File(vec![]))]
1225 #[case::file_typed(Kind::File(vec!["bucket1".to_string(), "bucket2".to_string()]))]
1226 #[case::literal_string(Kind::Literal(KindLiteral::String(Strand::new_static("a"))))]
1227 #[case::literal_integer(Kind::Literal(KindLiteral::Integer(42)))]
1228 #[case::literal_float(Kind::Literal(KindLiteral::Float(1.5)))]
1229 #[case::literal_decimal(Kind::Literal(KindLiteral::Decimal(Decimal::new(125, 2))))]
1230 #[case::literal_duration(Kind::Literal(KindLiteral::Duration(Duration(
1231 std::time::Duration::from_secs(3600)
1232 ))))]
1233 #[case::literal_bool(Kind::Literal(KindLiteral::Bool(true)))]
1234 #[case::literal_array(Kind::Literal(KindLiteral::Array(vec![Kind::String, Kind::Int])))]
1235 #[case::literal_object(Kind::Literal(KindLiteral::Object(BTreeMap::from([(
1236 Strand::new_static("a"),
1237 Kind::String,
1238 )]))))]
1239 #[case::deeply_nested(Kind::Either(vec![
1240 Kind::Array(Box::new(Kind::Record(vec![tb("a"), tb("b")])), Some(5)),
1241 Kind::Set(Box::new(Kind::Literal(KindLiteral::Integer(1))), None),
1242 Kind::None,
1243 ]))]
1244 fn kind_round_trips_through_text(#[case] k: Kind) {
1245 round_trip(&k);
1246 }
1247}