Skip to main content

surrealdb_expr/val/value/convert/
cast.rs

1use std::fmt;
2use std::ops::Bound;
3use std::str::FromStr as _;
4
5use common::decimal::DecimalExt;
6use geo::Point;
7use rust_decimal::Decimal;
8use surrealdb_cnf::GENERATION_ALLOCATION_LIMIT;
9use surrealdb_types::ToSql;
10
11use super::coerce::ElementPosition;
12use crate::expr::Kind;
13use crate::expr::kind::{GeometryKind, HasKind, KindLiteral};
14use crate::syn;
15use crate::val::range::IntegerRangeExt;
16use crate::val::{
17	Array, Bytes, Closure, Datetime, Duration, File, Geometry, Null, Number, Object, Range,
18	RecordId, Regex, Set, SqlNone, TableName, Uuid, Value,
19};
20
21#[derive(Clone, Debug)]
22pub enum CastError {
23	// Coercion error at the end.
24	InvalidKind {
25		from: Value,
26		into: String,
27	},
28	InvalidLength {
29		len: usize,
30		into: String,
31	},
32	/// Cast failed because element of type didn't match.
33	ElementOf {
34		inner: Box<CastError>,
35		into: String,
36		position: Option<ElementPosition>,
37	},
38	// Annoying error which doesn't fit in with the rest of the errors and breaks the trait
39	// pattern.
40	//
41	// Remove once the move to anyhow is complete.
42	RangeSizeLimit {
43		value: Box<Range>,
44	},
45}
46impl std::error::Error for CastError {}
47impl fmt::Display for CastError {
48	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49		match self {
50			CastError::InvalidKind {
51				from,
52				into,
53			} => {
54				write!(f, "Could not cast into `{into}` using input `{from}`", from = from.to_sql())
55			}
56			CastError::ElementOf {
57				inner,
58				into,
59				position,
60			} => {
61				inner.fmt(f)?;
62				match position {
63					Some(ElementPosition::Index(i)) => {
64						write!(f, " when casting element at index {i} of `{into}`")
65					}
66					Some(ElementPosition::Key(k)) => {
67						write!(f, " when casting value for key '{k}' of `{into}`")
68					}
69					None => write!(f, " when casting an element of `{into}`"),
70				}
71			}
72			CastError::InvalidLength {
73				len,
74				into,
75			} => {
76				write!(f, "Expected `{into}` but found a collection of length `{len}`")
77			}
78			CastError::RangeSizeLimit {
79				value,
80			} => {
81				write!(
82					f,
83					"Casting range `{value}` to an array would create an array larger then the max allocation limit.",
84					value = value.to_sql()
85				)
86			}
87		}
88	}
89}
90
91pub trait CastErrorExt {
92	fn with_element_of_at_index<F>(self, index: usize, f: F) -> Self
93	where
94		F: Fn() -> String;
95}
96
97impl<T> CastErrorExt for Result<T, CastError> {
98	fn with_element_of_at_index<F>(self, index: usize, f: F) -> Self
99	where
100		F: Fn() -> String,
101	{
102		match self {
103			Ok(x) => Ok(x),
104			Err(e) => Err(CastError::ElementOf {
105				inner: Box::new(e),
106				into: f(),
107				position: Some(ElementPosition::Index(index)),
108			}),
109		}
110	}
111}
112
113/// Trait for converting the value using casting rules, calling the functions on
114/// this trait results in similar behavior as casting does in surrealql like
115/// `<string> 1`.
116///
117/// Casting rules are more loose then coercing rules.
118pub trait Cast: Sized {
119	/// Returns true if calling cast on the value will succeed.
120	///
121	/// If `T::can_cast(&v)` returns `true` then `T::cast(v) should not return
122	/// an error.
123	fn can_cast(v: &Value) -> bool;
124
125	/// Cast a value to the self type.
126	fn cast(v: Value) -> Result<Self, CastError>;
127}
128
129impl Cast for Value {
130	fn can_cast(_: &Value) -> bool {
131		true
132	}
133
134	fn cast(v: Value) -> Result<Self, CastError> {
135		Ok(v)
136	}
137}
138
139impl Cast for SqlNone {
140	fn can_cast(v: &Value) -> bool {
141		matches!(v, Value::None)
142	}
143
144	fn cast(v: Value) -> Result<Self, CastError> {
145		match v {
146			Value::None => Ok(SqlNone),
147			x => Err(CastError::InvalidKind {
148				from: x,
149				into: "none".to_string(),
150			}),
151		}
152	}
153}
154
155impl Cast for Null {
156	fn can_cast(v: &Value) -> bool {
157		matches!(v, Value::Null)
158	}
159
160	fn cast(v: Value) -> Result<Self, CastError> {
161		match v {
162			Value::Null => Ok(Null),
163			x => Err(CastError::InvalidKind {
164				from: x,
165				into: "null".to_string(),
166			}),
167		}
168	}
169}
170
171impl Cast for bool {
172	fn can_cast(v: &Value) -> bool {
173		match v {
174			Value::Bool(_) => true,
175			Value::String(x) => matches!(x.as_str(), "true" | "false"),
176			_ => false,
177		}
178	}
179
180	fn cast(v: Value) -> Result<Self, CastError> {
181		match v {
182			Value::Bool(b) => Ok(b),
183			Value::String(x) => match x.as_str() {
184				"true" => Ok(true),
185				"false" => Ok(false),
186				_ => Err(CastError::InvalidKind {
187					from: Value::String(x),
188					into: "bool".to_string(),
189				}),
190			},
191			x => Err(CastError::InvalidKind {
192				from: x,
193				into: "bool".to_string(),
194			}),
195		}
196	}
197}
198
199impl Cast for i64 {
200	fn can_cast(v: &Value) -> bool {
201		match v {
202			Value::Number(Number::Int(_)) => true,
203			Value::Number(Number::Float(v)) => v.fract() == 0.0,
204			Value::Number(Number::Decimal(v)) => v.is_integer() || i64::try_from(*v).is_ok(),
205			Value::String(v) => v.parse::<i64>().is_ok(),
206			_ => false,
207		}
208	}
209
210	fn cast(v: Value) -> Result<Self, CastError> {
211		match v {
212			// Allow any int number
213			Value::Number(Number::Int(x)) => Ok(x),
214			// Attempt to convert an float number
215			Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(v as i64),
216			// Attempt to convert a decimal number
217			Value::Number(Number::Decimal(d)) if d.is_integer() => match d.try_into() {
218				Ok(v) => Ok(v),
219				_ => Err(CastError::InvalidKind {
220					from: v,
221					into: "int".into(),
222				}),
223			},
224			// Attempt to convert a string value
225			Value::String(ref s) => match s.parse::<i64>() {
226				Ok(v) => Ok(v),
227				_ => Err(CastError::InvalidKind {
228					from: v,
229					into: "int".into(),
230				}),
231			},
232			_ => Err(CastError::InvalidKind {
233				from: v,
234				into: "int".into(),
235			}),
236		}
237	}
238}
239
240impl Cast for f64 {
241	fn can_cast(v: &Value) -> bool {
242		match v {
243			Value::Number(Number::Int(_) | Number::Float(_)) => true,
244			Value::Number(Number::Decimal(v)) => v.is_integer() || i64::try_from(*v).is_ok(),
245			Value::String(v) => v.parse::<f64>().is_ok(),
246			_ => false,
247		}
248	}
249
250	fn cast(v: Value) -> Result<Self, CastError> {
251		match v {
252			Value::Number(Number::Float(i)) => Ok(i),
253			Value::Number(Number::Int(f)) => Ok(f as f64),
254			Value::Number(Number::Decimal(d)) => match d.try_into() {
255				// The Decimal can be parsed as a Float
256				Ok(v) => Ok(v),
257				// The Decimal loses precision
258				_ => Err(CastError::InvalidKind {
259					from: v,
260					into: "float".into(),
261				}),
262			},
263			// Attempt to convert a string value
264			Value::String(ref s) => match s.parse::<f64>() {
265				// The string can be parsed as a Float
266				Ok(v) => Ok(v),
267				// This string is not a float
268				_ => Err(CastError::InvalidKind {
269					from: v,
270					into: "float".into(),
271				}),
272			},
273			// Anything else raises an error
274			_ => Err(CastError::InvalidKind {
275				from: v,
276				into: "float".into(),
277			}),
278		}
279	}
280}
281
282impl Cast for Decimal {
283	fn can_cast(v: &Value) -> bool {
284		match v {
285			Value::Number(_) => true,
286			Value::String(v) => v.parse::<f64>().is_ok(),
287			_ => false,
288		}
289	}
290
291	fn cast(v: Value) -> Result<Self, CastError> {
292		match v {
293			Value::Number(Number::Decimal(d)) => Ok(d),
294			// Attempt to convert an int number
295			Value::Number(Number::Int(ref i)) => Ok(Decimal::from(*i)),
296			// Attempt to convert an float number
297			Value::Number(Number::Float(ref f)) => match Decimal::try_from(*f) {
298				// The Float can be represented as a Decimal
299				Ok(d) => Ok(d),
300				// This Float does not convert to a Decimal
301				_ => Err(CastError::InvalidKind {
302					from: v,
303					into: "decimal".into(),
304				}),
305			},
306			// Attempt to convert a string value
307			Value::String(ref s) => match Decimal::from_str_normalized(s) {
308				// The string can be parsed as a Decimal
309				Ok(v) => Ok(v),
310				// This string is not a Decimal
311				_ => Err(CastError::InvalidKind {
312					from: v,
313					into: "decimal".into(),
314				}),
315			},
316			// Anything else raises an error
317			_ => Err(CastError::InvalidKind {
318				from: v,
319				into: "decimal".into(),
320			}),
321		}
322	}
323}
324
325impl Cast for Number {
326	fn can_cast(v: &Value) -> bool {
327		match v {
328			Value::Number(_) => true,
329			Value::String(s) => Number::from_str(s).is_ok(),
330			_ => false,
331		}
332	}
333
334	fn cast(v: Value) -> Result<Self, CastError> {
335		match v {
336			Value::Number(v) => Ok(v),
337			Value::String(ref s) => Number::from_str(s).map_err(|_| CastError::InvalidKind {
338				from: v,
339				into: "number".into(),
340			}),
341			// Anything else raises an error
342			_ => Err(CastError::InvalidKind {
343				from: v,
344				into: "number".into(),
345			}),
346		}
347	}
348}
349
350impl Cast for String {
351	fn can_cast(v: &Value) -> bool {
352		match v {
353			Value::None | Value::Null => false,
354			Value::Bytes(b) => std::str::from_utf8(b).is_ok(),
355			_ => true,
356		}
357	}
358
359	fn cast(v: Value) -> Result<Self, CastError> {
360		match v {
361			Value::Bytes(b) => match String::from_utf8(b.0.to_vec()) {
362				Ok(x) => Ok(x),
363				Err(e) => Err(CastError::InvalidKind {
364					from: Value::Bytes(Bytes::from(e.into_bytes())),
365					into: "string".to_owned(),
366				}),
367			},
368
369			Value::Null => Ok("NULL".into()),
370			Value::None => Ok("NONE".into()),
371			Value::String(x) => Ok(x.into_string()),
372			Value::Uuid(x) => Ok(x.to_string()),
373			Value::Datetime(x) => Ok(x.to_string()),
374			Value::Number(Number::Decimal(x)) => Ok(x.to_string()),
375			x => Ok(x.to_sql()),
376		}
377	}
378}
379
380impl Cast for Uuid {
381	fn can_cast(v: &Value) -> bool {
382		match v {
383			Value::Uuid(_) => true,
384			Value::String(s) => Uuid::from_str(s).is_ok(),
385			_ => false,
386		}
387	}
388
389	fn cast(v: Value) -> Result<Self, CastError> {
390		match v {
391			Value::Uuid(u) => Ok(u),
392			Value::String(ref s) => Uuid::from_str(s).map_err(|_| CastError::InvalidKind {
393				from: v,
394				into: "uuid".into(),
395			}),
396			_ => Err(CastError::InvalidKind {
397				from: v,
398				into: "uuid".into(),
399			}),
400		}
401	}
402}
403
404impl Cast for Datetime {
405	fn can_cast(v: &Value) -> bool {
406		match v {
407			Value::Datetime(_) => true,
408			Value::String(s) => Datetime::from_str(s).is_ok(),
409			_ => false,
410		}
411	}
412
413	fn cast(v: Value) -> Result<Self, CastError> {
414		match v {
415			// Datetimes are allowed
416			Value::Datetime(v) => Ok(v),
417			// Attempt to parse a string
418			Value::String(ref s) => Datetime::from_str(s).map_err(|_| CastError::InvalidKind {
419				from: v,
420				into: "datetime".into(),
421			}),
422			// Anything else raises an error
423			_ => Err(CastError::InvalidKind {
424				from: v,
425				into: "datetime".into(),
426			}),
427		}
428	}
429}
430
431impl Cast for Duration {
432	fn can_cast(v: &Value) -> bool {
433		match v {
434			Value::Duration(_) => true,
435			Value::String(s) => Duration::from_str(s).is_ok(),
436			_ => false,
437		}
438	}
439
440	fn cast(v: Value) -> Result<Self, CastError> {
441		match v {
442			// Datetimes are allowed
443			Value::Duration(v) => Ok(v),
444			// Attempt to parse a string
445			Value::String(ref s) => Duration::from_str(s).map_err(|_| CastError::InvalidKind {
446				from: v,
447				into: "duration".into(),
448			}),
449			// Anything else raises an error
450			_ => Err(CastError::InvalidKind {
451				from: v,
452				into: "duration".into(),
453			}),
454		}
455	}
456}
457
458impl Cast for Bytes {
459	fn can_cast(v: &Value) -> bool {
460		match v {
461			Value::Bytes(_) | Value::String(_) => true,
462			Value::Array(x) => x.iter().all(|v| value_to_byte(v).is_some()),
463			_ => false,
464		}
465	}
466
467	fn cast(v: Value) -> Result<Self, CastError> {
468		match v {
469			Value::Bytes(b) => Ok(b),
470			Value::String(s) => Ok(Bytes::from(s.into_string().into_bytes())),
471			Value::Array(x) => match x.0.iter().map(value_to_byte).collect::<Option<Vec<u8>>>() {
472				Some(bytes) => Ok(Bytes::from(bytes)),
473				None => Err(CastError::InvalidKind {
474					from: Value::Array(x),
475					into: "bytes".to_owned(),
476				}),
477			},
478			_ => Err(CastError::InvalidKind {
479				from: v,
480				into: "bytes".into(),
481			}),
482		}
483	}
484}
485
486// `TryFrom<Number> for u8` is intentionally not reused: it truncates fractional
487// floats (via `num_traits::ToPrimitive::to_u8`) instead of rejecting them, which
488// would reintroduce the truncation bug this helper is here to prevent.
489fn value_to_byte(v: &Value) -> Option<u8> {
490	match v {
491		Value::Number(Number::Int(x)) => u8::try_from(*x).ok(),
492		Value::Number(Number::Float(f)) if f.fract() == 0.0 => u8::try_from(*f as i64).ok(),
493		Value::Number(Number::Decimal(d)) if d.is_integer() => {
494			u8::try_from(i64::try_from(*d).ok()?).ok()
495		}
496		Value::String(s) => s.parse::<u8>().ok(),
497		_ => None,
498	}
499}
500
501impl Cast for Array {
502	fn can_cast(v: &Value) -> bool {
503		match v {
504			Value::Array(_) | Value::Bytes(_) | Value::Set(_) => true,
505			Value::Range(r) => r.can_coerce_to_typed::<i64>(),
506			_ => false,
507		}
508	}
509
510	fn cast(v: Value) -> Result<Self, CastError> {
511		match v {
512			Value::Array(x) => Ok(x),
513			Value::Set(s) => Ok(s.into_iter().collect()),
514			Value::Range(range) => {
515				if !range.can_coerce_to_typed::<i64>() {
516					return Err(CastError::InvalidKind {
517						from: Value::Range(range),
518						into: "array".to_string(),
519					});
520				}
521				// checked above
522				let range = range.coerce_to_typed::<i64>().expect("range type checked above");
523				if range.len().is_none_or(|n| n > *GENERATION_ALLOCATION_LIMIT) {
524					return Err(CastError::RangeSizeLimit {
525						value: Box::new(Range::from(range)),
526					});
527				}
528
529				Ok(range.cast_to_array())
530			}
531
532			Value::Bytes(x) => Ok(Array(x.0.into_iter().map(|x| Value::from(x as i64)).collect())),
533			_ => Err(CastError::InvalidKind {
534				from: v,
535				into: "array".into(),
536			}),
537		}
538	}
539}
540
541impl Cast for Set {
542	fn can_cast(v: &Value) -> bool {
543		matches!(v, Value::Set(_) | Value::Array(_))
544	}
545
546	fn cast(v: Value) -> Result<Self, CastError> {
547		match v {
548			Value::Set(x) => Ok(x),
549			Value::Array(x) => {
550				// Convert array to set, automatically deduplicating
551				Ok(Set::from(x.0))
552			}
553			_ => Err(CastError::InvalidKind {
554				from: v,
555				into: "set".into(),
556			}),
557		}
558	}
559}
560
561impl Cast for Regex {
562	fn can_cast(v: &Value) -> bool {
563		match v {
564			Value::Regex(_) => true,
565			Value::String(x) => Regex::from_str(x).is_ok(),
566			_ => false,
567		}
568	}
569
570	fn cast(v: Value) -> Result<Self, CastError> {
571		match v {
572			Value::Regex(x) => Ok(x),
573			Value::String(x) => match Regex::from_str(&x) {
574				Ok(x) => Ok(x),
575				Err(_) => Err(CastError::InvalidKind {
576					from: Value::String(x),
577					into: "regex".to_string(),
578				}),
579			},
580			x => Err(CastError::InvalidKind {
581				from: x,
582				into: "regex".to_string(),
583			}),
584		}
585	}
586}
587
588impl Cast for Box<Range> {
589	fn can_cast(v: &Value) -> bool {
590		match v {
591			Value::Range(_) => true,
592			Value::Array(x) => x.len() == 2,
593			_ => false,
594		}
595	}
596
597	fn cast(v: Value) -> Result<Self, CastError> {
598		match v {
599			Value::Range(x) => Ok(x),
600			Value::Array(x) => {
601				if x.len() != 2 {
602					return Err(CastError::InvalidKind {
603						from: Value::Array(x),
604						into: "range".to_string(),
605					});
606				}
607
608				let mut iter = x.into_iter();
609				// checked above
610				let beg = iter.next().expect("array length checked above");
611				// checked above
612				let end = iter.next().expect("array length checked above");
613
614				Ok(Box::new(Range {
615					start: Bound::Included(beg),
616					end: Bound::Excluded(end),
617				}))
618			}
619			_ => Err(CastError::InvalidKind {
620				from: v,
621				into: "range".into(),
622			}),
623		}
624	}
625}
626
627impl Cast for Point<f64> {
628	fn can_cast(v: &Value) -> bool {
629		match v {
630			Value::Geometry(Geometry::Point(_)) => true,
631			Value::Array(x) => x.len() == 2,
632			_ => false,
633		}
634	}
635
636	fn cast(v: Value) -> Result<Self, CastError> {
637		match v {
638			Value::Geometry(Geometry::Point(v)) => Ok(v),
639			Value::Array(x) => {
640				if x.len() != 2 {
641					return Err(CastError::InvalidKind {
642						from: Value::Array(x),
643						into: "point".to_string(),
644					});
645				}
646
647				if !x[0].can_coerce_to::<f64>() || !x[1].can_coerce_to::<f64>() {
648					return Err(CastError::InvalidKind {
649						from: Value::Array(x),
650						into: "point".to_string(),
651					});
652				}
653
654				let mut iter = x.into_iter();
655				// checked above
656				let x = iter
657					.next()
658					.expect("array length checked above")
659					.cast_to::<f64>()
660					.expect("value type checked above");
661				// checked above
662				let y = iter
663					.next()
664					.expect("array length checked above")
665					.cast_to::<f64>()
666					.expect("value type checked above");
667
668				Ok(Point::new(x, y))
669			}
670			_ => Err(CastError::InvalidKind {
671				from: v,
672				into: "point".into(),
673			}),
674		}
675	}
676}
677
678impl Cast for RecordId {
679	fn can_cast(v: &Value) -> bool {
680		match v {
681			Value::RecordId(_) => true,
682			Value::String(x) => syn::record_id(x).is_ok(),
683			_ => false,
684		}
685	}
686
687	fn cast(v: Value) -> Result<Self, CastError> {
688		match v {
689			Value::RecordId(x) => Ok(x),
690			Value::String(x) => match syn::record_id(&x) {
691				Ok(x) => Ok(x.into()),
692				Err(_) => Err(CastError::InvalidKind {
693					from: Value::String(x),
694					into: "record".to_string(),
695				}),
696			},
697			from => Err(CastError::InvalidKind {
698				from,
699				into: "record".to_string(),
700			}),
701		}
702	}
703}
704
705impl Cast for surrealdb_strand::TableName {
706	fn can_cast(v: &Value) -> bool {
707		matches!(v, Value::Table(_) | Value::String(_))
708	}
709
710	fn cast(v: Value) -> Result<Self, CastError> {
711		match v {
712			Value::Table(x) => Ok(x),
713			Value::String(x) => Ok(surrealdb_strand::TableName::new(x)),
714			from => Err(CastError::InvalidKind {
715				from,
716				into: "table".to_string(),
717			}),
718		}
719	}
720}
721
722impl<T: Cast> Cast for Option<T> {
723	fn can_cast(v: &Value) -> bool {
724		if let Value::None = v {
725			return true;
726		}
727		T::can_cast(v)
728	}
729
730	fn cast(v: Value) -> Result<Self, CastError> {
731		match v {
732			Value::None => Ok(None),
733			x => T::cast(x).map(Some),
734		}
735	}
736}
737
738macro_rules! impl_direct {
739	($($name:ident => $inner:ty $(= $kind:ident)?),*$(,)?) => {
740		$(
741		impl Cast for $inner {
742			fn can_cast(v: &Value) -> bool{
743				matches!(v, Value::$name(_))
744			}
745
746			fn cast(v: Value) -> Result<Self, CastError> {
747				if let Value::$name(x) = v {
748					return Ok(x);
749				} else {
750					return Err(CastError::InvalidKind{
751						from: v,
752						into: impl_direct!(@kindof $inner $(= $kind)?),
753					});
754				}
755			}
756		}
757		)*
758	};
759
760	(@kindof $inner:ty = $kind:ident) => {
761		<$kind as HasKind>::kind().to_sql()
762	};
763
764	(@kindof $inner:ty) => {
765		<$inner as HasKind>::kind().to_sql()
766	};
767}
768
769// Types which directly match one enum variant or fail
770impl_direct! {
771	Closure => Box<Closure> = Closure,
772	Object => Object,
773	Geometry => Geometry,
774	File => File,
775}
776
777impl Value {
778	pub fn can_cast_to<T: Cast>(&self) -> bool {
779		T::can_cast(self)
780	}
781
782	pub fn can_cast_to_kind(&self, kind: &Kind) -> bool {
783		match kind {
784			Kind::Any => true,
785			Kind::None => self.can_cast_to::<SqlNone>(),
786			Kind::Null => self.can_cast_to::<Null>(),
787			Kind::Bool => self.can_cast_to::<bool>(),
788			Kind::Int => self.can_cast_to::<i64>(),
789			Kind::Float => self.can_cast_to::<f64>(),
790			Kind::Decimal => self.can_cast_to::<Decimal>(),
791			Kind::Number => self.can_cast_to::<Number>(),
792			Kind::String => self.can_cast_to::<String>(),
793			Kind::Datetime => self.can_cast_to::<Datetime>(),
794			Kind::Duration => self.can_cast_to::<Duration>(),
795			Kind::Object => self.can_cast_to::<crate::val::Object>(),
796			Kind::Bytes => self.can_cast_to::<Bytes>(),
797			Kind::Uuid => self.can_cast_to::<Uuid>(),
798			Kind::Regex => self.can_cast_to::<Regex>(),
799			Kind::Range => self.can_cast_to::<Box<Range>>(),
800			Kind::Function(_, _) => self.can_cast_to::<Box<Closure>>(),
801			Kind::Set(t, l) => match l {
802				Some(l) => self.can_cast_to_set_len(t, *l),
803				None => self.can_cast_to_set(t),
804			},
805			Kind::Array(t, l) => match l {
806				Some(l) => self.can_cast_to_array_len(t, *l),
807				None => self.can_cast_to_array(t),
808			},
809			Kind::Table(t) => {
810				if t.is_empty() {
811					self.can_cast_to::<String>()
812				} else {
813					self.can_cast_to_table(t)
814				}
815			}
816			Kind::Record(t) => {
817				if t.is_empty() {
818					self.can_cast_to::<RecordId>()
819				} else {
820					self.can_cast_to_record(t)
821				}
822			}
823			Kind::Geometry(t) => {
824				if t.is_empty() {
825					self.can_cast_to::<Geometry>()
826				} else {
827					self.can_cast_to_geometry(t)
828				}
829			}
830			Kind::Either(k) => k.iter().any(|x| self.can_cast_to_kind(x)),
831			Kind::Literal(lit) => self.can_cast_to_literal(lit),
832			Kind::File(buckets) => {
833				if buckets.is_empty() {
834					self.can_cast_to::<File>()
835				} else {
836					self.can_cast_to_file_buckets(buckets)
837				}
838			}
839		}
840	}
841
842	fn can_cast_to_array_len(&self, kind: &Kind, len: u64) -> bool {
843		match self {
844			Value::Array(a) => a.len() as u64 == len && a.iter().all(|x| x.can_cast_to_kind(kind)),
845			_ => false,
846		}
847	}
848
849	fn can_cast_to_array(&self, kind: &Kind) -> bool {
850		match self {
851			Value::Array(a) => a.iter().all(|x| x.can_cast_to_kind(kind)),
852			_ => false,
853		}
854	}
855
856	fn can_cast_to_set_len(&self, kind: &Kind, len: u64) -> bool {
857		match self {
858			Value::Set(s) => s.len() as u64 == len && s.iter().all(|x| x.can_cast_to_kind(kind)),
859			_ => false,
860		}
861	}
862
863	fn can_cast_to_set(&self, kind: &Kind) -> bool {
864		match self {
865			Value::Set(s) => s.iter().all(|x| x.can_cast_to_kind(kind)),
866			_ => false,
867		}
868	}
869
870	fn can_cast_to_table(&self, val: &[TableName]) -> bool {
871		match self {
872			Value::Table(t) => t.is_table_type(val),
873			Value::String(_) => true, // Strings can be cast to tables
874			_ => false,
875		}
876	}
877
878	fn can_cast_to_record(&self, val: &[TableName]) -> bool {
879		match self {
880			Value::RecordId(t) => t.is_table_type(val),
881			_ => false,
882		}
883	}
884
885	fn can_cast_to_geometry(&self, val: &[GeometryKind]) -> bool {
886		self.is_geometry_type(val)
887	}
888
889	fn can_cast_to_literal(&self, val: &KindLiteral) -> bool {
890		val.validate_value(self)
891	}
892
893	fn can_cast_to_file_buckets(&self, buckets: &[String]) -> bool {
894		matches!(self, Value::File(f) if f.is_bucket_type(buckets))
895	}
896
897	pub fn cast_to<T: Cast>(self) -> Result<T, CastError> {
898		T::cast(self)
899	}
900
901	/// Try to convert this value to the specified `Kind`
902	pub fn cast_to_kind(self, kind: &Kind) -> Result<Value, CastError> {
903		// Attempt to convert to the desired type
904		match kind {
905			Kind::Any => Ok(self),
906			Kind::None => self.cast_to::<SqlNone>().map(|_| Value::None),
907			Kind::Null => self.cast_to::<Null>().map(|_| Value::Null),
908			Kind::Bool => self.cast_to::<bool>().map(Value::from),
909			Kind::Int => self.cast_to::<i64>().map(Value::from),
910			Kind::Float => self.cast_to::<f64>().map(Value::from),
911			Kind::Decimal => self.cast_to::<Decimal>().map(Value::from),
912			Kind::Number => self.cast_to::<Number>().map(Value::from),
913			Kind::String => self.cast_to::<String>().map(Value::from),
914			Kind::Datetime => self.cast_to::<Datetime>().map(Value::from),
915			Kind::Duration => self.cast_to::<Duration>().map(Value::from),
916			Kind::Object => self.cast_to::<crate::val::Object>().map(Value::from),
917			Kind::Bytes => self.cast_to::<Bytes>().map(Value::from),
918			Kind::Uuid => self.cast_to::<Uuid>().map(Value::from),
919			Kind::Regex => self.cast_to::<Regex>().map(Value::from),
920			Kind::Range => self.cast_to::<Box<Range>>().map(Value::from),
921			Kind::Function(_, _) => self.cast_to::<Box<Closure>>().map(Value::from),
922			Kind::Set(t, l) => match l {
923				Some(l) => self.cast_to_set_type_len(t, *l).map(Value::from),
924				None => self.cast_to_set_type(t).map(Value::from),
925			},
926			Kind::Array(t, l) => match l {
927				Some(l) => self.cast_to_array_len(t, *l).map(Value::from),
928				None => self.cast_to_array(t).map(Value::from),
929			},
930			Kind::Table(t) => match t.is_empty() {
931				true => self
932					.cast_to::<String>()
933					.map(|s| Value::Table(surrealdb_strand::TableName::new(s))),
934				false => self.cast_to_table(t).map(Value::from),
935			},
936			Kind::Record(t) => match t.is_empty() {
937				true => self.cast_to::<RecordId>().map(Value::from),
938				false => self.cast_to_record(t).map(Value::from),
939			},
940			Kind::Geometry(t) => match t.is_empty() {
941				true => self.cast_to::<Geometry>().map(Value::from),
942				false => self.cast_to_geometry(t).map(Value::from),
943			},
944			Kind::Either(k) => {
945				let Some(k) = k.iter().find(|x| self.can_cast_to_kind(x)) else {
946					return Err(CastError::InvalidKind {
947						from: self,
948						into: kind.to_sql(),
949					});
950				};
951
952				Ok(self.cast_to_kind(k).expect(
953					"If can_coerce_to_kind returns true then coerce_to_kind must not error",
954				))
955			}
956			Kind::Literal(lit) => self.cast_to_literal(lit),
957			Kind::File(buckets) => {
958				if buckets.is_empty() {
959					self.cast_to::<File>().map(Value::from)
960				} else {
961					self.cast_to_file_buckets(buckets).map(Value::from)
962				}
963			}
964		}
965	}
966
967	/// Try to convert this value to a Literal, returns a `Value` with the
968	/// coerced value
969	pub fn cast_to_literal(self, literal: &KindLiteral) -> Result<Value, CastError> {
970		if literal.validate_value(&self) {
971			Ok(self)
972		} else {
973			Err(CastError::InvalidKind {
974				from: self,
975				into: literal.to_sql(),
976			})
977		}
978	}
979
980	/// Try to convert this value to a Table of a certain type
981	fn cast_to_table(self, val: &[TableName]) -> Result<surrealdb_strand::TableName, CastError> {
982		match self {
983			Value::Table(v) if v.is_table_type(val) => Ok(v),
984			Value::String(v) => {
985				// Check if the string is a valid table name and matches the allowed types
986				let table = surrealdb_strand::TableName::new(v.clone());
987				if table.is_table_type(val) {
988					Ok(table)
989				} else {
990					let mut kind = "table<".to_string();
991					for (idx, t) in val.iter().enumerate() {
992						if idx != 0 {
993							kind.push('|');
994						}
995						kind.push_str(t.as_str())
996					}
997					kind.push('>');
998
999					Err(CastError::InvalidKind {
1000						from: Value::String(v),
1001						into: kind,
1002					})
1003				}
1004			}
1005			x => {
1006				let mut kind = "table<".to_string();
1007				for (idx, t) in val.iter().enumerate() {
1008					if idx != 0 {
1009						kind.push('|');
1010					}
1011					kind.push_str(t.as_str())
1012				}
1013				kind.push('>');
1014
1015				Err(CastError::InvalidKind {
1016					from: x,
1017					into: kind,
1018				})
1019			}
1020		}
1021	}
1022
1023	/// Try to convert this value to a Record of a certain type
1024	fn cast_to_record(self, val: &[TableName]) -> Result<RecordId, CastError> {
1025		let expected_kind = || -> String {
1026			let mut kind = "record<".to_string();
1027			for (idx, t) in val.iter().enumerate() {
1028				if idx != 0 {
1029					kind.push('|');
1030				}
1031				kind.push_str(t.as_str())
1032			}
1033			kind.push('>');
1034			kind
1035		};
1036
1037		match self {
1038			Value::RecordId(v) if v.is_table_type(val) => Ok(v),
1039			Value::String(v) => {
1040				let record_id = match syn::record_id(v.as_str()) {
1041					Ok(x) => RecordId::from(x),
1042					Err(_) => {
1043						return Err(CastError::InvalidKind {
1044							from: Value::String(v),
1045							into: expected_kind(),
1046						});
1047					}
1048				};
1049
1050				if !record_id.is_table_type(val) {
1051					return Err(CastError::InvalidKind {
1052						from: Value::String(v),
1053						into: expected_kind(),
1054					});
1055				}
1056
1057				Ok(record_id)
1058			}
1059			x => Err(CastError::InvalidKind {
1060				from: x,
1061				into: expected_kind(),
1062			}),
1063		}
1064	}
1065
1066	/// Try to convert this value to a `Geometry` of a certain type
1067	fn cast_to_geometry(self, val: &[GeometryKind]) -> Result<Geometry, CastError> {
1068		match self {
1069			// Geometries are allowed if correct type
1070			Value::Geometry(v) if self.is_geometry_type(val) => Ok(v),
1071			Value::Array(_) => {
1072				// An array of two numbers can be cast into a point
1073				if val.contains(&GeometryKind::Point)
1074					&& let Some(p) = Geometry::array_to_point(&self)
1075				{
1076					Ok(Geometry::Point(p))
1077				} else {
1078					Err(CastError::InvalidKind {
1079						from: self,
1080						into: "geometry".into(),
1081					})
1082				}
1083			}
1084			// Anything else raises an error
1085			_ => Err(CastError::InvalidKind {
1086				from: self,
1087				into: "geometry".into(),
1088			}),
1089		}
1090	}
1091
1092	/// Try to convert this value to ab `Array` of a certain type
1093	fn cast_to_array(self, kind: &Kind) -> Result<Array, CastError> {
1094		self.cast_to::<Array>()?
1095			.into_iter()
1096			.enumerate()
1097			.map(|(i, value)| {
1098				value
1099					.cast_to_kind(kind)
1100					.with_element_of_at_index(i, || format!("array<{}>", kind.to_sql()))
1101			})
1102			.collect::<Result<Array, CastError>>()
1103	}
1104
1105	/// Try to convert this value to ab `Array` of a certain type and length
1106	fn cast_to_array_len(self, kind: &Kind, len: u64) -> Result<Array, CastError> {
1107		let array = self.cast_to::<Array>()?;
1108
1109		if (array.len() as u64) != len {
1110			return Err(CastError::InvalidLength {
1111				len: array.len(),
1112				into: format!("array<{},{}>", kind.to_sql(), len),
1113			});
1114		}
1115
1116		array
1117			.into_iter()
1118			.enumerate()
1119			.map(|(i, value)| {
1120				value
1121					.cast_to_kind(kind)
1122					.with_element_of_at_index(i, || format!("array<{}>", kind.to_sql()))
1123			})
1124			.collect::<Result<Array, CastError>>()
1125	}
1126
1127	/// Try to convert this value to a `Set` of a certain type
1128	pub fn cast_to_set_type(self, kind: &Kind) -> Result<Set, CastError> {
1129		self.cast_to::<Array>()?
1130			.into_iter()
1131			.enumerate()
1132			.map(|(i, value)| {
1133				value
1134					.cast_to_kind(kind)
1135					.with_element_of_at_index(i, || format!("set<{}>", kind.to_sql()))
1136			})
1137			.collect::<Result<Set, CastError>>()
1138	}
1139
1140	/// Try to convert this value to a `Set` of a certain type and length
1141	pub fn cast_to_set_type_len(self, kind: &Kind, len: u64) -> Result<Set, CastError> {
1142		let set = self
1143			.cast_to::<Array>()?
1144			.into_iter()
1145			.enumerate()
1146			.map(|(i, value)| {
1147				value
1148					.cast_to_kind(kind)
1149					.with_element_of_at_index(i, || format!("set<{}>", kind.to_sql()))
1150			})
1151			.collect::<Result<Set, CastError>>()?;
1152
1153		if (set.len() as u64) != len {
1154			return Err(CastError::InvalidLength {
1155				len: set.len(),
1156				into: format!("set<{},{}>", kind.to_sql(), len),
1157			});
1158		}
1159
1160		Ok(set)
1161	}
1162
1163	pub fn cast_to_file_buckets(self, buckets: &[String]) -> Result<File, CastError> {
1164		let v = self.cast_to::<File>()?;
1165
1166		if v.is_bucket_type(buckets) {
1167			return Ok(v);
1168		}
1169
1170		let mut kind = "file<".to_owned();
1171		for (idx, t) in buckets.iter().enumerate() {
1172			if idx != 0 {
1173				kind.push('|');
1174			}
1175			kind.push_str(t.as_str())
1176		}
1177		kind.push('>');
1178		Err(CastError::InvalidKind {
1179			from: v.into(),
1180			into: kind,
1181		})
1182	}
1183}
1184
1185#[cfg(test)]
1186mod tests {
1187	use surrealdb_strand::Strand;
1188
1189	use super::*;
1190
1191	#[test]
1192	fn test_cast_to_table_generic() {
1193		// Test casting string to generic table type
1194		let value = Value::String(Strand::new_static("users"));
1195		let kind = Kind::Table(vec![]);
1196		let result = value.cast_to_kind(&kind);
1197		assert!(result.is_ok());
1198		if let Ok(Value::Table(table)) = result {
1199			assert_eq!(table.as_str(), "users");
1200		} else {
1201			panic!("Expected Value::Table");
1202		}
1203	}
1204
1205	#[test]
1206	fn test_cast_to_table_specific() {
1207		// Test casting string to specific table type (matching)
1208		let value = Value::String(Strand::new_static("users"));
1209		let kind = Kind::Table(vec!["users".into()]);
1210		let result = value.cast_to_kind(&kind);
1211		assert!(result.is_ok());
1212		if let Ok(Value::Table(table)) = result {
1213			assert_eq!(table.as_str(), "users");
1214		}
1215
1216		// Test casting string to specific table type (not matching)
1217		let value = Value::String(Strand::new_static("posts"));
1218		let kind = Kind::Table(vec!["users".into()]);
1219		let result = value.cast_to_kind(&kind);
1220		assert!(result.is_err());
1221	}
1222
1223	#[test]
1224	fn test_cast_to_table_union() {
1225		// Test casting string to union of table types
1226		let value = Value::String(Strand::new_static("posts"));
1227		let kind = Kind::Table(vec!["users".into(), "posts".into()]);
1228		let result = value.cast_to_kind(&kind);
1229		assert!(result.is_ok());
1230		if let Ok(Value::Table(table)) = result {
1231			assert_eq!(table.as_str(), "posts");
1232		}
1233
1234		// Test casting string that doesn't match any in the union
1235		let value = Value::String(Strand::new_static("comments"));
1236		let kind = Kind::Table(vec!["users".into(), "posts".into()]);
1237		let result = value.cast_to_kind(&kind);
1238		assert!(result.is_err());
1239	}
1240
1241	#[test]
1242	fn test_cast_table_to_table() {
1243		// Test casting table value to table type
1244		let value = Value::Table(surrealdb_strand::TableName::new("users".to_string()));
1245		let kind = Kind::Table(vec!["users".into()]);
1246		let result = value.cast_to_kind(&kind);
1247		assert!(result.is_ok());
1248	}
1249
1250	#[test]
1251	fn test_can_cast_to_table() {
1252		// Test can_cast_to_kind for tables - String can always cast to generic table
1253		let value = Value::String(Strand::new_static("users"));
1254		let kind = Kind::Table(vec![]);
1255		assert!(value.can_cast_to_kind(&kind));
1256
1257		// Table value can cast to table type
1258		let value = Value::Table(surrealdb_strand::TableName::new("users".to_string()));
1259		let kind = Kind::Table(vec!["users".into()]);
1260		assert!(value.can_cast_to_kind(&kind));
1261
1262		// Table value cannot cast to wrong table type
1263		let value = Value::Table(surrealdb_strand::TableName::new("posts".to_string()));
1264		let kind = Kind::Table(vec!["users".into()]);
1265		assert!(!value.can_cast_to_kind(&kind));
1266	}
1267}