Improve type casting support ()

Co-authored-by: Finn Bear <finnbearlabs@gmail.com>
This commit is contained in:
Tobie Morgan Hitchcock 2023-06-06 09:12:59 +03:00 committed by GitHub
parent 1bcf57ecc9
commit f5f10d508f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1166 additions and 412 deletions

View file

@ -292,7 +292,7 @@ pub(crate) fn router(
if let Some(response) = option {
trace!(target: LOG, "{response:?}");
if let Some(Ok(id)) =
response.id.map(Value::convert_to_i64)
response.id.map(Value::coerce_to_i64)
{
if let Some((method, sender)) = routes.remove(&id) {
let _res = sender

View file

@ -265,7 +265,7 @@ pub(crate) fn router(
Ok(option) => {
if let Some(response) = option {
trace!(target: LOG, "{response:?}");
if let Some(Ok(id)) = response.id.map(Value::convert_to_i64) {
if let Some(Ok(id)) = response.id.map(Value::coerce_to_i64) {
if let Some((method, sender)) = routes.remove(&id) {
let _ = sender
.into_send_async(DbResponse::from((

View file

@ -377,6 +377,7 @@ fn into_json(value: Value, simplify: bool) -> JsonValue {
},
false => json!(constant),
},
Value::Cast(cast) => json!(cast),
Value::Function(function) => json!(function),
Value::Subquery(subquery) => json!(subquery),
Value::Expression(expression) => json!(expression),

View file

@ -34,9 +34,9 @@ impl<'a> Document<'a> {
// Check for a TYPE clause
if let Some(kind) = &fd.kind {
if !val.is_none() {
val = val.convert_to(kind).map_err(|e| match e {
val = val.coerce_to(kind).map_err(|e| match e {
// There was a conversion error
Error::ConvertTo {
Error::CoerceTo {
from,
..
} => Error::FieldCheck {
@ -63,9 +63,9 @@ impl<'a> Document<'a> {
}
// Check for a TYPE clause
if let Some(kind) = &fd.kind {
val = val.convert_to(kind).map_err(|e| match e {
val = val.coerce_to(kind).map_err(|e| match e {
// There was a conversion error
Error::ConvertTo {
Error::CoerceTo {
from,
..
} => Error::FieldCheck {

View file

@ -361,13 +361,27 @@ pub enum Error {
value: String,
},
/// The requested function does not exist
#[error("Expected a {into} but failed to convert {from} into a {into}")]
/// Unable to coerce to a value to another value
#[error("Expected a {into} but found {from}")]
CoerceTo {
from: Value,
into: Cow<'static, str>,
},
/// Unable to convert a value to another value
#[error("Expected a {into} but cannot convert {from} into a {into}")]
ConvertTo {
from: Value,
into: Cow<'static, str>,
},
/// Unable to coerce to a value to another value
#[error("Expected a {kind} but the array had {size} items")]
LengthInvalid {
kind: Cow<'static, str>,
size: usize,
},
/// Cannot perform addition
#[error("Cannot perform addition with '{0}' and '{1}'")]
TryAdd(String, String),

View file

@ -1,6 +1,6 @@
use crate::err::Error;
use crate::sql::value::Value;
use crate::sql::{Array, Bytes, Datetime, Duration, Number, Strand, Thing};
use crate::sql::{Array, Bytes, Datetime, Duration, Kind, Number, Strand, Thing};
/// Implemented by types that are commonly used, in a certain way, as arguments.
pub trait FromArg: Sized {
@ -15,85 +15,85 @@ impl FromArg for Value {
impl FromArg for String {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_string()
arg.coerce_to_string()
}
}
impl FromArg for Strand {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_strand()
arg.coerce_to_strand()
}
}
impl FromArg for Number {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_number()
arg.coerce_to_number()
}
}
impl FromArg for Datetime {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_datetime()
arg.coerce_to_datetime()
}
}
impl FromArg for Duration {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_duration()
arg.coerce_to_duration()
}
}
impl FromArg for Thing {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_record()
arg.coerce_to_record()
}
}
impl FromArg for Array {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_array()
arg.coerce_to_array()
}
}
impl FromArg for Bytes {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_bytes()
arg.coerce_to_bytes()
}
}
impl FromArg for i64 {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_i64()
arg.coerce_to_i64()
}
}
impl FromArg for u64 {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_u64()
arg.coerce_to_u64()
}
}
impl FromArg for f64 {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_f64()
arg.coerce_to_f64()
}
}
impl FromArg for isize {
fn from_arg(arg: Value) -> Result<Self, Error> {
Ok(arg.convert_to_i64()? as isize)
Ok(arg.coerce_to_i64()? as isize)
}
}
impl FromArg for usize {
fn from_arg(arg: Value) -> Result<Self, Error> {
Ok(arg.convert_to_u64()? as usize)
Ok(arg.coerce_to_u64()? as usize)
}
}
impl FromArg for Vec<Number> {
fn from_arg(arg: Value) -> Result<Self, Error> {
arg.convert_to_array()?.into_iter().map(Value::convert_to_number).collect()
arg.coerce_to_array_type(&Kind::Number)?.into_iter().map(Value::try_into).collect()
}
}

View file

@ -1,4 +1,4 @@
#[cfg(any(feature = "kv-tikv", feature = "kv-rocksdb", feature = "kv-fdb"))]
#[cfg(any(feature = "kv-tikv", feature = "kv-rocksdb", feature = "kv-speedb", feature = "kv-fdb"))]
pub(crate) mod transaction {
use crate::dbs::{Response, Session};
use crate::kvs::ds::Inner;

89
lib/src/sql/cast.rs Normal file
View file

@ -0,0 +1,89 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::sql::comment::mightbespace;
use crate::sql::error::IResult;
use crate::sql::idiom::Idiom;
use crate::sql::kind::{kind, Kind};
use crate::sql::value::{single, Value};
use async_recursion::async_recursion;
use nom::character::complete::char;
use nom::sequence::delimited;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[serde(rename = "$surrealdb::private::sql::Cast")]
pub struct Cast(pub Kind, pub Value);
impl PartialOrd for Cast {
#[inline]
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
None
}
}
impl Cast {
/// Convert cast to a field name
pub fn to_idiom(&self) -> Idiom {
self.1.to_idiom()
}
}
impl Cast {
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&'async_recursion Value>,
) -> Result<Value, Error> {
// Prevent long cast chains
let opt = &opt.dive(1)?;
// Compute the value to be cast and convert it
self.1.compute(ctx, opt, txn, doc).await?.convert_to(&self.0)
}
}
impl fmt::Display for Cast {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<{}> {}", self.0, self.1)
}
}
pub fn cast(i: &str) -> IResult<&str, Cast> {
let (i, k) = delimited(char('<'), kind, char('>'))(i)?;
let (i, _) = mightbespace(i)?;
let (i, v) = single(i)?;
Ok((i, Cast(k, v)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cast_int() {
let sql = "<int>1.2345";
let res = cast(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("<int> 1.2345", format!("{}", out));
assert_eq!(out, Cast(Kind::Int, 1.2345.into()));
}
#[test]
fn cast_string() {
let sql = "<string>1.2345";
let res = cast(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("<string> 1.2345", format!("{}", out));
assert_eq!(out, Cast(Kind::String, 1.2345.into()));
}
}

View file

@ -9,9 +9,8 @@ use crate::sql::common::{closeparentheses, commas, openparentheses};
use crate::sql::error::IResult;
use crate::sql::fmt::Fmt;
use crate::sql::idiom::Idiom;
use crate::sql::kind::{kind, Kind};
use crate::sql::script::{script as func, Script};
use crate::sql::value::{single, value, Value};
use crate::sql::value::{value, Value};
use async_recursion::async_recursion;
use futures::future::try_join_all;
use nom::branch::alt;
@ -21,7 +20,6 @@ use nom::character::complete::char;
use nom::combinator::recognize;
use nom::multi::separated_list0;
use nom::multi::separated_list1;
use nom::sequence::delimited;
use nom::sequence::preceded;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
@ -32,7 +30,6 @@ pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Function";
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[serde(rename = "$surrealdb::private::sql::Function")]
pub enum Function {
Cast(Kind, Value),
Normal(String, Vec<Value>),
Custom(String, Vec<Value>),
Script(Script, Vec<Value>),
@ -69,7 +66,6 @@ impl Function {
Self::Script(_, _) => "function".to_string().into(),
Self::Normal(f, _) => f.to_owned().into(),
Self::Custom(f, _) => format!("fn::{f}").into(),
Self::Cast(_, v) => v.to_idiom(),
}
}
/// Convert this function to an aggregate
@ -149,12 +145,6 @@ impl Function {
let opt = &opt.futures(true);
// Process the function type
match self {
Self::Cast(k, x) => {
// Compute the value to be cast
let a = x.compute(ctx, opt, txn, doc).await?;
// Run the cast function
a.convert_to(k)
}
Self::Normal(s, x) => {
// Compute the function arguments
let a = try_join_all(x.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?;
@ -187,7 +177,7 @@ impl Function {
let mut ctx = Context::new(ctx);
// Process the function arguments
for (val, (name, kind)) in a.into_iter().zip(val.args) {
ctx.add_value(name.to_raw(), val.convert_to(&kind)?);
ctx.add_value(name.to_raw(), val.coerce_to(&kind)?);
}
// Run the custom function
val.block.compute(&ctx, opt, txn, doc).await
@ -215,7 +205,6 @@ impl Function {
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Cast(k, e) => write!(f, "<{k}> {e}"),
Self::Normal(s, e) => write!(f, "{s}({})", Fmt::comma_separated(e)),
Self::Custom(s, e) => write!(f, "fn::{s}({})", Fmt::comma_separated(e)),
Self::Script(s, e) => write!(f, "function({}) {{{s}}}", Fmt::comma_separated(e)),
@ -224,7 +213,7 @@ impl fmt::Display for Function {
}
pub fn function(i: &str) -> IResult<&str, Function> {
alt((normal, custom, script, cast))(i)
alt((normal, custom, script))(i)
}
pub fn normal(i: &str) -> IResult<&str, Function> {
@ -259,13 +248,6 @@ fn script(i: &str) -> IResult<&str, Function> {
Ok((i, Function::Script(v, a)))
}
fn cast(i: &str) -> IResult<&str, Function> {
let (i, k) = delimited(char('<'), kind, char('>'))(i)?;
let (i, _) = mightbespace(i)?;
let (i, v) = single(i)?;
Ok((i, Function::Cast(k, v)))
}
pub(crate) fn function_names(i: &str) -> IResult<&str, &str> {
recognize(alt((
preceded(tag("array::"), function_array),
@ -598,26 +580,6 @@ mod tests {
assert_eq!(out, Function::Normal(String::from("is::numeric"), vec![Value::Null]));
}
#[test]
fn function_casting_number() {
let sql = "<int>1.2345";
let res = function(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("<int> 1.2345", format!("{}", out));
assert_eq!(out, Function::Cast(Kind::Int, 1.2345.into()));
}
#[test]
fn function_casting_string() {
let sql = "<string>1.2345";
let res = function(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("<string> 1.2345", format!("{}", out));
assert_eq!(out, Function::Cast(Kind::String, 1.2345.into()));
}
#[test]
fn function_script_expression() {
let sql = "function() { return this.tags.filter(t => { return t.length > 3; }); }";

View file

@ -49,6 +49,13 @@ impl Kind {
}
}
impl From<&Kind> for Box<Kind> {
#[inline]
fn from(v: &Kind) -> Self {
Box::new(v.clone())
}
}
impl Display for Kind {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {

View file

@ -5,6 +5,7 @@ pub(crate) mod array;
pub(crate) mod base;
pub(crate) mod block;
pub(crate) mod bytes;
pub(crate) mod cast;
pub(crate) mod comment;
pub(crate) mod common;
pub(crate) mod cond;
@ -78,6 +79,7 @@ pub use self::array::Array;
pub use self::base::Base;
pub use self::block::Block;
pub use self::bytes::Bytes;
pub use self::cast::Cast;
pub use self::cond::Cond;
pub use self::data::Data;
pub use self::datetime::Datetime;

View file

@ -0,0 +1,85 @@
use crate::err::Error;
use crate::sql::value::serde::ser;
use crate::sql::Cast;
use crate::sql::Kind;
use crate::sql::Value;
use ser::Serializer as _;
use serde::ser::Error as _;
use serde::ser::Impossible;
use serde::ser::Serialize;
pub(super) struct Serializer;
impl ser::Serializer for Serializer {
type Ok = Cast;
type Error = Error;
type SerializeSeq = Impossible<Cast, Error>;
type SerializeTuple = Impossible<Cast, Error>;
type SerializeTupleStruct = SerializeCast;
type SerializeTupleVariant = Impossible<Cast, Error>;
type SerializeMap = Impossible<Cast, Error>;
type SerializeStruct = Impossible<Cast, Error>;
type SerializeStructVariant = Impossible<Cast, Error>;
const EXPECTED: &'static str = "an struct `Cast`";
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Error> {
Ok(SerializeCast::default())
}
}
#[derive(Default)]
pub(super) struct SerializeCast {
index: usize,
kind: Option<Kind>,
value: Option<Value>,
}
impl serde::ser::SerializeTupleStruct for SerializeCast {
type Ok = Cast;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize + ?Sized,
{
match self.index {
0 => {
self.kind = Some(value.serialize(ser::kind::Serializer.wrap())?);
}
1 => {
self.value = Some(value.serialize(ser::value::Serializer.wrap())?);
}
index => {
return Err(Error::custom(format!("unexpected `Cast` index `{index}`")));
}
}
self.index += 1;
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
match (self.kind, self.value) {
(Some(kind), Some(value)) => Ok(Cast(kind, value)),
_ => Err(Error::custom("`Cast` missing required value(s)")),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Serialize;
#[test]
fn cast() {
let cast = Cast(Default::default(), Default::default());
let serialized = cast.serialize(Serializer.wrap()).unwrap();
assert_eq!(cast, serialized);
}
}

View file

@ -1,7 +1,6 @@
use crate::err::Error;
use crate::sql::value::serde::ser;
use crate::sql::Function;
use crate::sql::Kind;
use crate::sql::Script;
use crate::sql::Value;
use ser::Serializer as _;
@ -33,7 +32,6 @@ impl ser::Serializer for Serializer {
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
let inner = match variant {
"Cast" => Inner::Cast(None, None),
"Normal" => Inner::Normal(None, None),
"Custom" => Inner::Custom(None, None),
"Script" => Inner::Script(None, None),
@ -54,7 +52,6 @@ pub(super) struct SerializeFunction {
}
enum Inner {
Cast(Option<Kind>, Option<Value>),
Normal(Option<String>, Option<Vec<Value>>),
Custom(Option<String>, Option<Vec<Value>>),
Script(Option<Script>, Option<Vec<Value>>),
@ -75,12 +72,6 @@ impl serde::ser::SerializeTupleVariant for SerializeFunction {
(0, Inner::Script(ref mut var, _)) => {
*var = Some(Script(value.serialize(ser::string::Serializer.wrap())?));
}
(0, Inner::Cast(ref mut var, _)) => {
*var = Some(value.serialize(ser::kind::Serializer.wrap())?);
}
(1, Inner::Cast(_, ref mut var)) => {
*var = Some(value.serialize(ser::value::Serializer.wrap())?);
}
(
1,
Inner::Normal(_, ref mut var)
@ -91,7 +82,6 @@ impl serde::ser::SerializeTupleVariant for SerializeFunction {
}
(index, inner) => {
let variant = match inner {
Inner::Cast(..) => "Cast",
Inner::Normal(..) => "Normal",
Inner::Custom(..) => "Custom",
Inner::Script(..) => "Script",
@ -107,7 +97,6 @@ impl serde::ser::SerializeTupleVariant for SerializeFunction {
fn end(self) -> Result<Self::Ok, Self::Error> {
match self.inner {
Inner::Cast(Some(one), Some(two)) => Ok(Function::Cast(one, two)),
Inner::Normal(Some(one), Some(two)) => Ok(Function::Normal(one, two)),
Inner::Custom(Some(one), Some(two)) => Ok(Function::Custom(one, two)),
Inner::Script(Some(one), Some(two)) => Ok(Function::Script(one, two)),
@ -121,13 +110,6 @@ mod tests {
use super::*;
use serde::Serialize;
#[test]
fn cast() {
let function = Function::Cast(Default::default(), Default::default());
let serialized = function.serialize(Serializer.wrap()).unwrap();
assert_eq!(function, serialized);
}
#[test]
fn normal() {
let function = Function::Normal(Default::default(), vec![Default::default()]);

View file

@ -1,4 +1,5 @@
mod block;
mod cast;
mod cond;
mod constant;
mod data;

View file

@ -807,7 +807,7 @@ mod tests {
#[test]
fn function() {
let function = Box::new(Function::Cast(Default::default(), Default::default()));
let function = Box::new(Function::Normal(Default::default(), Default::default()));
let value = to_value(&function).unwrap();
let expected = Value::Function(function);
assert_eq!(value, expected);

View file

@ -8,6 +8,7 @@ use crate::sql::array::Uniq;
use crate::sql::array::{array, Array};
use crate::sql::block::{block, Block};
use crate::sql::bytes::Bytes;
use crate::sql::cast::{cast, Cast};
use crate::sql::comment::mightbespace;
use crate::sql::common::commas;
use crate::sql::constant::{constant, Constant};
@ -141,6 +142,7 @@ pub enum Value {
Table(Table),
Model(Model),
Regex(Regex),
Cast(Box<Cast>),
Block(Box<Block>),
Range(Box<Range>),
Edges(Box<Edges>),
@ -287,6 +289,12 @@ impl From<Future> for Value {
}
}
impl From<Cast> for Value {
fn from(v: Cast) -> Self {
Value::Cast(Box::new(v))
}
}
impl From<Function> for Value {
fn from(v: Function) -> Self {
Value::Function(Box::new(v))
@ -670,7 +678,7 @@ impl TryFrom<Value> for bool {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Bool(boolean) => Ok(boolean),
Value::Bool(v) => Ok(v),
_ => Err(Error::TryFrom(value.to_string(), "bool")),
}
}
@ -716,6 +724,16 @@ impl TryFrom<Value> for Vec<Value> {
}
}
impl TryFrom<Value> for Number {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Number(x) => Ok(x),
_ => Err(Error::TryFrom(value.to_string(), "Number")),
}
}
}
impl TryFrom<Value> for Object {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
@ -787,7 +805,7 @@ impl Value {
/// Check if this Value is truthy
pub fn is_truthy(&self) -> bool {
match self {
Value::Bool(boolean) => *boolean,
Value::Bool(v) => *v,
Value::Uuid(_) => true,
Value::Thing(_) => true,
Value::Geometry(_) => true,
@ -1042,7 +1060,564 @@ impl Value {
}
// -----------------------------------
// Simple conversion of value
// Simple output of value type
// -----------------------------------
/// Treat a string as a table name
pub fn kindof(&self) -> &'static str {
match self {
Self::None => "none",
Self::Null => "null",
Self::Bool(_) => "bool",
Self::Uuid(_) => "uuid",
Self::Array(_) => "array",
Self::Object(_) => "object",
Self::Strand(_) => "string",
Self::Duration(_) => "duration",
Self::Datetime(_) => "datetime",
Self::Number(Number::Int(_)) => "int",
Self::Number(Number::Float(_)) => "float",
Self::Number(Number::Decimal(_)) => "decimal",
Self::Geometry(Geometry::Point(_)) => "geometry<point>",
Self::Geometry(Geometry::Line(_)) => "geometry<line>",
Self::Geometry(Geometry::Polygon(_)) => "geometry<polygon>",
Self::Geometry(Geometry::MultiPoint(_)) => "geometry<multipoint>",
Self::Geometry(Geometry::MultiLine(_)) => "geometry<multiline>",
Self::Geometry(Geometry::MultiPolygon(_)) => "geometry<multipolygon>",
Self::Geometry(Geometry::Collection(_)) => "geometry<collection>",
Self::Bytes(_) => "bytes",
_ => "incorrect type",
}
}
// -----------------------------------
// Simple type coercion of values
// -----------------------------------
/// Try to coerce this value to the specified `Kind`
pub(crate) fn coerce_to(self, kind: &Kind) -> Result<Value, Error> {
// Attempt to convert to the desired type
let res = match kind {
Kind::Any => Ok(self),
Kind::Bool => self.coerce_to_bool().map(Value::from),
Kind::Int => self.coerce_to_int().map(Value::from),
Kind::Float => self.coerce_to_float().map(Value::from),
Kind::Decimal => self.coerce_to_decimal().map(Value::from),
Kind::Number => self.coerce_to_number().map(Value::from),
Kind::String => self.coerce_to_strand().map(Value::from),
Kind::Datetime => self.coerce_to_datetime().map(Value::from),
Kind::Duration => self.coerce_to_duration().map(Value::from),
Kind::Object => self.coerce_to_object().map(Value::from),
Kind::Point => self.coerce_to_point().map(Value::from),
Kind::Bytes => self.coerce_to_bytes().map(Value::from),
Kind::Uuid => self.coerce_to_uuid().map(Value::from),
Kind::Set(t, l) => match l {
Some(l) => self.coerce_to_set_type_len(t, l).map(Value::from),
None => self.coerce_to_set_type(t).map(Value::from),
},
Kind::Array(t, l) => match l {
Some(l) => self.coerce_to_array_type_len(t, l).map(Value::from),
None => self.coerce_to_array_type(t).map(Value::from),
},
Kind::Record(t) => match t.is_empty() {
true => self.coerce_to_record().map(Value::from),
false => self.coerce_to_record_type(t).map(Value::from),
},
Kind::Geometry(t) => match t.is_empty() {
true => self.coerce_to_geometry().map(Value::from),
false => self.coerce_to_geometry_type(t).map(Value::from),
},
Kind::Option(k) => match self {
Self::None => Ok(Self::None),
Self::Null => Ok(Self::None),
v => v.coerce_to(k),
},
Kind::Either(k) => {
let mut val = self;
for k in k {
match val.coerce_to(k) {
Err(Error::CoerceTo {
from,
..
}) => val = from,
Err(e) => return Err(e),
Ok(v) => return Ok(v),
}
}
Err(Error::CoerceTo {
from: val,
into: kind.to_string().into(),
})
}
};
// Check for any conversion errors
match res {
// There was a conversion error
Err(Error::CoerceTo {
from,
..
}) => Err(Error::CoerceTo {
from,
into: kind.to_string().into(),
}),
// There was a different error
Err(e) => Err(e),
// Everything converted ok
Ok(v) => Ok(v),
}
}
/// Try to coerce this value to an `i64`
pub(crate) fn coerce_to_i64(self) -> Result<i64, Error> {
match self {
// Allow any int number
Value::Number(Number::Int(v)) => Ok(v),
// Attempt to convert an float number
Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(v as i64),
// Attempt to convert a decimal number
Value::Number(Number::Decimal(ref v)) if v.is_integer() => match v.to_i64() {
// The Decimal can be represented as an i64
Some(v) => Ok(v),
// The Decimal is out of bounds
_ => Err(Error::CoerceTo {
from: self,
into: "i64".into(),
}),
},
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "i64".into(),
}),
}
}
/// Try to coerce this value to an `u64`
pub(crate) fn coerce_to_u64(self) -> Result<u64, Error> {
match self {
// Allow any int number
Value::Number(Number::Int(v)) => Ok(v as u64),
// Attempt to convert an float number
Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(v as u64),
// Attempt to convert a decimal number
Value::Number(Number::Decimal(ref v)) if v.is_integer() => match v.to_u64() {
// The Decimal can be represented as an u64
Some(v) => Ok(v),
// The Decimal is out of bounds
_ => Err(Error::CoerceTo {
from: self,
into: "u64".into(),
}),
},
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "u64".into(),
}),
}
}
/// Try to coerce this value to an `f64`
pub(crate) fn coerce_to_f64(self) -> Result<f64, Error> {
match self {
// Allow any float number
Value::Number(Number::Float(v)) => Ok(v),
// Attempt to convert an int number
Value::Number(Number::Int(v)) => Ok(v as f64),
// Attempt to convert a decimal number
Value::Number(Number::Decimal(ref v)) => match v.to_f64() {
// The Decimal can be represented as a f64
Some(v) => Ok(v),
// Ths Decimal loses precision
None => Err(Error::CoerceTo {
from: self,
into: "f64".into(),
}),
},
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "f64".into(),
}),
}
}
/// Try to coerce this value to a `bool`
pub(crate) fn coerce_to_bool(self) -> Result<bool, Error> {
match self {
// Allow any boolean value
Value::Bool(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "bool".into(),
}),
}
}
/// Try to coerce this value to an integer `Number`
pub(crate) fn coerce_to_int(self) -> Result<Number, Error> {
match self {
// Allow any int number
Value::Number(v) if v.is_int() => Ok(v),
// Attempt to convert an float number
Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(Number::Int(v as i64)),
// Attempt to convert a decimal number
Value::Number(Number::Decimal(ref v)) if v.is_integer() => match v.to_i64() {
// The Decimal can be represented as an Int
Some(v) => Ok(Number::Int(v)),
// The Decimal is out of bounds
_ => Err(Error::CoerceTo {
from: self,
into: "int".into(),
}),
},
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "int".into(),
}),
}
}
/// Try to coerce this value to a float `Number`
pub(crate) fn coerce_to_float(self) -> Result<Number, Error> {
match self {
// Allow any float number
Value::Number(v) if v.is_float() => Ok(v),
// Attempt to convert an int number
Value::Number(Number::Int(v)) => Ok(Number::Float(v as f64)),
// Attempt to convert a decimal number
Value::Number(Number::Decimal(ref v)) => match v.to_f64() {
// The Decimal can be represented as a Float
Some(v) => Ok(Number::Float(v)),
// Ths BigDecimal loses precision
None => Err(Error::CoerceTo {
from: self,
into: "float".into(),
}),
},
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "float".into(),
}),
}
}
/// Try to coerce this value to a decimal `Number`
pub(crate) fn coerce_to_decimal(self) -> Result<Number, Error> {
match self {
// Allow any decimal number
Value::Number(v) if v.is_decimal() => Ok(v),
// Attempt to convert an int number
Value::Number(Number::Int(ref v)) => match BigDecimal::from_i64(*v) {
// The Int can be represented as a Decimal
Some(v) => Ok(Number::Decimal(v)),
// Ths Int does not convert to a Decimal
None => Err(Error::CoerceTo {
from: self,
into: "decimal".into(),
}),
},
// Attempt to convert an float number
Value::Number(Number::Float(ref v)) => match BigDecimal::from_f64(*v) {
// The Float can be represented as a Decimal
Some(v) => Ok(Number::Decimal(v)),
// Ths Float does not convert to a Decimal
None => Err(Error::CoerceTo {
from: self,
into: "decimal".into(),
}),
},
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "decimal".into(),
}),
}
}
/// Try to coerce this value to a `Number`
pub(crate) fn coerce_to_number(self) -> Result<Number, Error> {
match self {
// Allow any number
Value::Number(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "number".into(),
}),
}
}
/// Try to coerce this value to a `String`
pub(crate) fn coerce_to_string(self) -> Result<String, Error> {
match self {
// Allow any uuid value
Value::Uuid(v) => Ok(v.to_raw()),
// Allow any datetime value
Value::Datetime(v) => Ok(v.to_raw()),
// Allow any string value
Value::Strand(v) => Ok(v.as_string()),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "string".into(),
}),
}
}
/// Try to coerce this value to a `Strand`
pub(crate) fn coerce_to_strand(self) -> Result<Strand, Error> {
match self {
// Allow any uuid value
Value::Uuid(v) => Ok(v.to_raw().into()),
// Allow any datetime value
Value::Datetime(v) => Ok(v.to_raw().into()),
// Allow any string value
Value::Strand(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "string".into(),
}),
}
}
/// Try to coerce this value to a `Uuid`
pub(crate) fn coerce_to_uuid(self) -> Result<Uuid, Error> {
match self {
// Uuids are allowed
Value::Uuid(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "uuid".into(),
}),
}
}
/// Try to coerce this value to a `Datetime`
pub(crate) fn coerce_to_datetime(self) -> Result<Datetime, Error> {
match self {
// Datetimes are allowed
Value::Datetime(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "datetime".into(),
}),
}
}
/// Try to coerce this value to a `Duration`
pub(crate) fn coerce_to_duration(self) -> Result<Duration, Error> {
match self {
// Durations are allowed
Value::Duration(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "duration".into(),
}),
}
}
/// Try to coerce this value to a `Bytes`
pub(crate) fn coerce_to_bytes(self) -> Result<Bytes, Error> {
match self {
// Bytes are allowed
Value::Bytes(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "bytes".into(),
}),
}
}
/// Try to coerce this value to an `Object`
pub(crate) fn coerce_to_object(self) -> Result<Object, Error> {
match self {
// Objects are allowed
Value::Object(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "object".into(),
}),
}
}
/// Try to coerce this value to an `Array`
pub(crate) fn coerce_to_array(self) -> Result<Array, Error> {
match self {
// Arrays are allowed
Value::Array(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "array".into(),
}),
}
}
/// Try to coerce this value to an `Geometry` point
pub(crate) fn coerce_to_point(self) -> Result<Geometry, Error> {
match self {
// Geometry points are allowed
Value::Geometry(Geometry::Point(v)) => Ok(v.into()),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "point".into(),
}),
}
}
/// Try to coerce this value to a Record or `Thing`
pub(crate) fn coerce_to_record(self) -> Result<Thing, Error> {
match self {
// Records are allowed
Value::Thing(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "record".into(),
}),
}
}
/// Try to coerce this value to an `Geometry` type
pub(crate) fn coerce_to_geometry(self) -> Result<Geometry, Error> {
match self {
// Geometries are allowed
Value::Geometry(v) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "geometry".into(),
}),
}
}
/// Try to coerce this value to a Record of a certain type
pub(crate) fn coerce_to_record_type(self, val: &[Table]) -> Result<Thing, Error> {
match self {
// Records are allowed if correct type
Value::Thing(v) if self.is_record_type(val) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "record".into(),
}),
}
}
/// Try to coerce this value to a `Geometry` of a certain type
pub(crate) fn coerce_to_geometry_type(self, val: &[String]) -> Result<Geometry, Error> {
match self {
// Geometries are allowed if correct type
Value::Geometry(v) if self.is_geometry_type(val) => Ok(v),
// Anything else raises an error
_ => Err(Error::CoerceTo {
from: self,
into: "geometry".into(),
}),
}
}
/// Try to coerce this value to an `Array` of a certain type
pub(crate) fn coerce_to_array_type(self, kind: &Kind) -> Result<Array, Error> {
self.coerce_to_array()?
.into_iter()
.map(|value| value.coerce_to(kind))
.collect::<Result<Array, Error>>()
.map_err(|e| match e {
Error::CoerceTo {
from,
..
} => Error::CoerceTo {
from,
into: format!("array<{kind}>").into(),
},
e => e,
})
}
/// Try to coerce this value to an `Array` of a certain type, and length
pub(crate) fn coerce_to_array_type_len(self, kind: &Kind, len: &u64) -> Result<Array, Error> {
self.coerce_to_array()?
.into_iter()
.map(|value| value.coerce_to(kind))
.collect::<Result<Array, Error>>()
.map_err(|e| match e {
Error::CoerceTo {
from,
..
} => Error::CoerceTo {
from,
into: format!("array<{kind}, {len}>").into(),
},
e => e,
})
.and_then(|v| match v.len() {
v if v > *len as usize => Err(Error::LengthInvalid {
kind: format!("array<{kind}, {len}>").into(),
size: v,
}),
_ => Ok(v),
})
}
/// Try to coerce this value to an `Array` of a certain type, unique values
pub(crate) fn coerce_to_set_type(self, kind: &Kind) -> Result<Array, Error> {
self.coerce_to_array()?
.uniq()
.into_iter()
.map(|value| value.coerce_to(kind))
.collect::<Result<Array, Error>>()
.map_err(|e| match e {
Error::CoerceTo {
from,
..
} => Error::CoerceTo {
from,
into: format!("set<{kind}>").into(),
},
e => e,
})
}
/// Try to coerce this value to an `Array` of a certain type, unique values, and length
pub(crate) fn coerce_to_set_type_len(self, kind: &Kind, len: &u64) -> Result<Array, Error> {
self.coerce_to_array()?
.uniq()
.into_iter()
.map(|value| value.coerce_to(kind))
.collect::<Result<Array, Error>>()
.map_err(|e| match e {
Error::CoerceTo {
from,
..
} => Error::CoerceTo {
from,
into: format!("set<{kind}, {len}>").into(),
},
e => e,
})
.and_then(|v| match v.len() {
v if v > *len as usize => Err(Error::LengthInvalid {
kind: format!("set<{kind}, {len}>").into(),
size: v,
}),
_ => Ok(v),
})
}
// -----------------------------------
// Advanced type conversion of values
// -----------------------------------
/// Try to convert this value to the specified `Kind`
@ -1062,8 +1637,14 @@ impl Value {
Kind::Point => self.convert_to_point().map(Value::from),
Kind::Bytes => self.convert_to_bytes().map(Value::from),
Kind::Uuid => self.convert_to_uuid().map(Value::from),
Kind::Set(t, l) => self.convert_to_set_type(t, l).map(Value::from),
Kind::Array(t, l) => self.convert_to_array_type(t, l).map(Value::from),
Kind::Set(t, l) => match l {
Some(l) => self.convert_to_set_type_len(t, l).map(Value::from),
None => self.convert_to_set_type(t).map(Value::from),
},
Kind::Array(t, l) => match l {
Some(l) => self.convert_to_array_type_len(t, l).map(Value::from),
None => self.convert_to_array_type(t).map(Value::from),
},
Kind::Record(t) => match t.is_empty() {
true => self.convert_to_record().map(Value::from),
false => self.convert_to_record_type(t).map(Value::from),
@ -1112,116 +1693,11 @@ impl Value {
}
}
/// Try to convert this value to an `i64`
pub(crate) fn convert_to_i64(self) -> Result<i64, Error> {
match self {
// Allow any int number
Value::Number(Number::Int(v)) => Ok(v),
// Attempt to convert an float number
Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(v as i64),
// Attempt to convert a decimal number
Value::Number(Number::Decimal(ref v)) if v.is_integer() => match v.to_i64() {
// The Decimal can be represented as an i64
Some(v) => Ok(v),
// The Decimal is out of bounds
_ => Err(Error::ConvertTo {
from: self,
into: "i64".into(),
}),
},
// Attempt to convert a string value
Value::Strand(ref v) => match v.parse::<i64>() {
// The Strand can be represented as an i64
Ok(v) => Ok(v),
// Ths string is not a float
_ => Err(Error::ConvertTo {
from: self,
into: "i64".into(),
}),
},
// Anything else raises an error
_ => Err(Error::ConvertTo {
from: self,
into: "i64".into(),
}),
}
}
/// Try to convert this value to an `u64`
pub(crate) fn convert_to_u64(self) -> Result<u64, Error> {
match self {
// Allow any int number
Value::Number(Number::Int(v)) => Ok(v as u64),
// Attempt to convert an float number
Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(v as u64),
// Attempt to convert a decimal number
Value::Number(Number::Decimal(ref v)) if v.is_integer() => match v.to_u64() {
// The Decimal can be represented as an u64
Some(v) => Ok(v),
// The Decimal is out of bounds
_ => Err(Error::ConvertTo {
from: self,
into: "u64".into(),
}),
},
// Attempt to convert a string value
Value::Strand(ref v) => match v.parse::<u64>() {
// The Strand can be represented as a Float
Ok(v) => Ok(v),
// Ths string is not a float
_ => Err(Error::ConvertTo {
from: self,
into: "u64".into(),
}),
},
// Anything else raises an error
_ => Err(Error::ConvertTo {
from: self,
into: "u64".into(),
}),
}
}
/// Try to convert this value to an `f64`
pub(crate) fn convert_to_f64(self) -> Result<f64, Error> {
match self {
// Allow any float number
Value::Number(Number::Float(v)) => Ok(v),
// Attempt to convert an int number
Value::Number(Number::Int(v)) => Ok(v as f64),
// Attempt to convert a decimal number
Value::Number(Number::Decimal(ref v)) => match v.to_f64() {
// The Decimal can be represented as a f64
Some(v) => Ok(v),
// Ths Decimal loses precision
None => Err(Error::ConvertTo {
from: self,
into: "f64".into(),
}),
},
// Attempt to convert a string value
Value::Strand(ref v) => match v.parse::<f64>() {
// The Strand can be represented as a f64
Ok(v) => Ok(v),
// Ths string is not a float
_ => Err(Error::ConvertTo {
from: self,
into: "f64".into(),
}),
},
// Anything else raises an error
_ => Err(Error::ConvertTo {
from: self,
into: "f64".into(),
}),
}
}
/// Try to convert this value to a `bool`
pub(crate) fn convert_to_bool(self) -> Result<bool, Error> {
match self {
// Allow any boolean value
Value::Bool(boolean) => Ok(boolean),
Value::Bool(v) => Ok(v),
// Attempt to convert a string value
Value::Strand(ref v) => match v.parse::<bool>() {
// The string can be represented as a Float
@ -1613,54 +2089,92 @@ impl Value {
}
}
/// Try to convert this value to ab `Array` of a certain type and optional length
pub(crate) fn convert_to_array_type(
self,
kind: &Kind,
size: &Option<u64>,
) -> Result<Array, Error> {
match size {
Some(l) => self
.convert_to_array()?
.into_iter()
.map(|value| value.convert_to(kind))
.collect::<Result<Array, Error>>()
.map(|mut v| {
v.truncate(*l as usize);
v
}),
None => self
.convert_to_array()?
.into_iter()
.map(|value| value.convert_to(kind))
.collect::<Result<Array, Error>>(),
}
/// Try to convert this value to ab `Array` of a certain type
pub(crate) fn convert_to_array_type(self, kind: &Kind) -> Result<Array, Error> {
self.convert_to_array()?
.into_iter()
.map(|value| value.convert_to(kind))
.collect::<Result<Array, Error>>()
.map_err(|e| match e {
Error::ConvertTo {
from,
..
} => Error::ConvertTo {
from,
into: format!("array<{kind}>").into(),
},
e => e,
})
}
/// Try to convert this value to an `Array` of a certain type, unique values, and optional length
pub(crate) fn convert_to_set_type(
self,
kind: &Kind,
size: &Option<u64>,
) -> Result<Array, Error> {
match size {
Some(l) => self
.convert_to_array()?
.uniq()
.into_iter()
.map(|value| value.convert_to(kind))
.collect::<Result<Array, Error>>()
.map(|mut v| {
v.truncate(*l as usize);
v
/// Try to convert this value to ab `Array` of a certain type and length
pub(crate) fn convert_to_array_type_len(self, kind: &Kind, len: &u64) -> Result<Array, Error> {
self.convert_to_array()?
.into_iter()
.map(|value| value.convert_to(kind))
.collect::<Result<Array, Error>>()
.map_err(|e| match e {
Error::ConvertTo {
from,
..
} => Error::ConvertTo {
from,
into: format!("array<{kind}, {len}>").into(),
},
e => e,
})
.and_then(|v| match v.len() {
v if v > *len as usize => Err(Error::LengthInvalid {
kind: format!("array<{kind}, {len}>").into(),
size: v,
}),
None => self
.convert_to_array()?
.uniq()
.into_iter()
.map(|value| value.convert_to(kind))
.collect::<Result<Array, Error>>(),
}
_ => Ok(v),
})
}
/// Try to convert this value to an `Array` of a certain type, unique values
pub(crate) fn convert_to_set_type(self, kind: &Kind) -> Result<Array, Error> {
self.convert_to_array()?
.uniq()
.into_iter()
.map(|value| value.convert_to(kind))
.collect::<Result<Array, Error>>()
.map_err(|e| match e {
Error::ConvertTo {
from,
..
} => Error::ConvertTo {
from,
into: format!("set<{kind}>").into(),
},
e => e,
})
}
/// Try to convert this value to an `Array` of a certain type, unique values, and length
pub(crate) fn convert_to_set_type_len(self, kind: &Kind, len: &u64) -> Result<Array, Error> {
self.convert_to_array()?
.uniq()
.into_iter()
.map(|value| value.convert_to(kind))
.collect::<Result<Array, Error>>()
.map_err(|e| match e {
Error::ConvertTo {
from,
..
} => Error::ConvertTo {
from,
into: format!("set<{kind}, {len}>").into(),
},
e => e,
})
.and_then(|v| match v.len() {
v if v > *len as usize => Err(Error::LengthInvalid {
kind: format!("set<{kind}, {len}>").into(),
size: v,
}),
_ => Ok(v),
})
}
// -----------------------------------
@ -1734,7 +2248,7 @@ impl Value {
match self {
Value::None => other.is_none(),
Value::Null => other.is_null(),
Value::Bool(boolean) => *boolean,
Value::Bool(v) => *v,
Value::Uuid(v) => match other {
Value::Uuid(w) => v == w,
Value::Regex(w) => w.regex().is_match(v.to_raw().as_str()),
@ -1922,30 +2436,31 @@ impl fmt::Display for Value {
match self {
Value::None => write!(f, "NONE"),
Value::Null => write!(f, "NULL"),
Value::Bool(v) => write!(f, "{v}"),
Value::Number(v) => write!(f, "{v}"),
Value::Strand(v) => write!(f, "{v}"),
Value::Duration(v) => write!(f, "{v}"),
Value::Datetime(v) => write!(f, "{v}"),
Value::Uuid(v) => write!(f, "{v}"),
Value::Array(v) => write!(f, "{v}"),
Value::Object(v) => write!(f, "{v}"),
Value::Block(v) => write!(f, "{v}"),
Value::Bool(v) => write!(f, "{v}"),
Value::Bytes(v) => write!(f, "{v}"),
Value::Cast(v) => write!(f, "{v}"),
Value::Constant(v) => write!(f, "{v}"),
Value::Datetime(v) => write!(f, "{v}"),
Value::Duration(v) => write!(f, "{v}"),
Value::Edges(v) => write!(f, "{v}"),
Value::Expression(v) => write!(f, "{v}"),
Value::Function(v) => write!(f, "{v}"),
Value::Future(v) => write!(f, "{v}"),
Value::Geometry(v) => write!(f, "{v}"),
Value::Param(v) => write!(f, "{v}"),
Value::Idiom(v) => write!(f, "{v}"),
Value::Model(v) => write!(f, "{v}"),
Value::Number(v) => write!(f, "{v}"),
Value::Object(v) => write!(f, "{v}"),
Value::Param(v) => write!(f, "{v}"),
Value::Range(v) => write!(f, "{v}"),
Value::Regex(v) => write!(f, "{v}"),
Value::Strand(v) => write!(f, "{v}"),
Value::Subquery(v) => write!(f, "{v}"),
Value::Table(v) => write!(f, "{v}"),
Value::Thing(v) => write!(f, "{v}"),
Value::Model(v) => write!(f, "{v}"),
Value::Regex(v) => write!(f, "{v}"),
Value::Block(v) => write!(f, "{v}"),
Value::Range(v) => write!(f, "{v}"),
Value::Edges(v) => write!(f, "{v}"),
Value::Future(v) => write!(f, "{v}"),
Value::Constant(v) => write!(f, "{v}"),
Value::Function(v) => write!(f, "{v}"),
Value::Subquery(v) => write!(f, "{v}"),
Value::Expression(v) => write!(f, "{v}"),
Value::Bytes(v) => write!(f, "{v}"),
Value::Uuid(v) => write!(f, "{v}"),
}
}
}
@ -1975,6 +2490,7 @@ impl Value {
doc: Option<&'async_recursion Value>,
) -> Result<Value, Error> {
match self {
Value::Cast(v) => v.compute(ctx, opt, txn, doc).await,
Value::Thing(v) => v.compute(ctx, opt, txn, doc).await,
Value::Block(v) => v.compute(ctx, opt, txn, doc).await,
Value::Range(v) => v.compute(ctx, opt, txn, doc).await,
@ -2125,9 +2641,10 @@ pub fn single(i: &str) -> IResult<&str, Value> {
map(tag_no_case("NULL"), |_| Value::Null),
map(tag_no_case("true"), |_| Value::Bool(true)),
map(tag_no_case("false"), |_| Value::Bool(false)),
map(idiom::multi, Value::from),
)),
alt((
map(idiom::multi, Value::from),
map(cast, Value::from),
map(function, Value::from),
map(subquery, Value::from),
map(constant, Value::from),
@ -2160,9 +2677,10 @@ pub fn select(i: &str) -> IResult<&str, Value> {
map(tag_no_case("NULL"), |_| Value::Null),
map(tag_no_case("true"), |_| Value::Bool(true)),
map(tag_no_case("false"), |_| Value::Bool(false)),
map(idiom::multi, Value::from),
)),
alt((
map(idiom::multi, Value::from),
map(cast, Value::from),
map(function, Value::from),
map(subquery, Value::from),
map(constant, Value::from),

View file

@ -36,28 +36,45 @@ async fn field_definition_value_assert_failure() -> Result<(), Error> {
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Found NONE for field `age`, with record `person:test`, but expected a number"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Found NONE for field `age`, with record `person:test`, but expected a number",
),
"{}",
tmp.unwrap_err().to_string()
);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Found NONE for field `age`, with record `person:test`, but expected a number"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Found NONE for field `age`, with record `person:test`, but expected a number"
),
"{}",
tmp.unwrap_err().to_string()
);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Found NULL for field `age`, with record `person:test`, but expected a number"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Found NULL for field `age`, with record `person:test`, but expected a number"
),
"{}",
tmp.unwrap_err().to_string()
);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Found 0 for field `age`, with record `person:test`, but field must conform to: $value > 0"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Found 0 for field `age`, with record `person:test`, but field must conform to: $value > 0"
),
"{}",
tmp.unwrap_err().to_string()
);
//
let tmp = res.remove(0).result?;
let val = Value::parse(

View file

@ -28,10 +28,13 @@ async fn function_array_add() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::add(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::add(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2]");
@ -65,10 +68,13 @@ async fn function_array_all() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::all(). Argument 1 was the wrong type. Expected a array but failed to convert 'some text' into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::all(). Argument 1 was the wrong type. Expected a array but found 'some text'"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::Bool(false);
@ -94,10 +100,13 @@ async fn function_array_any() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::any(). Argument 1 was the wrong type. Expected a array but failed to convert 'some text' into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::any(). Argument 1 was the wrong type. Expected a array but found 'some text'"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::Bool(true);
@ -123,10 +132,13 @@ async fn function_array_append() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::append(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::append(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,[2,3]]");
@ -152,10 +164,13 @@ async fn function_array_combine() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::combine(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::combine(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[ [1,2], [1,3], [2,2], [2,3] ]");
@ -181,10 +196,13 @@ async fn function_array_complement() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::complement(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::complement(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2]");
@ -210,10 +228,13 @@ async fn function_array_concat() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::concat(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::concat(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,3,4,3,4,5,6]");
@ -239,10 +260,13 @@ async fn function_array_difference() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::difference(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::difference(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,5,6]");
assert_eq!(tmp, val);
@ -267,10 +291,13 @@ async fn function_array_distinct() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::distinct(). Argument 1 was the wrong type. Expected a array but failed to convert 'some text' into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::distinct(). Argument 1 was the wrong type. Expected a array but found 'some text'"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,3,4]");
@ -297,10 +324,13 @@ async fn function_array_flatten() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::flatten(). Argument 1 was the wrong type. Expected a array but failed to convert 'some text' into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::flatten(). Argument 1 was the wrong type. Expected a array but found 'some text'"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,3,4]");
@ -330,10 +360,13 @@ async fn function_array_group() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::group(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::group(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,3,4,5,6]");
@ -391,10 +424,13 @@ async fn function_array_intersect() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::intersect(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::intersect(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[3,4]");
@ -452,10 +488,13 @@ async fn function_array_len() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::len(). Argument 1 was the wrong type. Expected a array but failed to convert 'some text' into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::len(). Argument 1 was the wrong type. Expected a array but found 'some text'"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::from(6);
@ -481,10 +520,13 @@ async fn function_array_max() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::max(). Argument 1 was the wrong type. Expected a array but failed to convert 'some text' into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::max(). Argument 1 was the wrong type. Expected a array but found 'some text'"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("'text'");
@ -510,10 +552,13 @@ async fn function_array_min() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::min(). Argument 1 was the wrong type. Expected a array but failed to convert 'some text' into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::min(). Argument 1 was the wrong type. Expected a array but found 'some text'"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("1");
@ -539,10 +584,13 @@ async fn function_array_pop() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::pop(). Argument 1 was the wrong type. Expected a array but failed to convert 'some text' into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::pop(). Argument 1 was the wrong type. Expected a array but found 'some text'"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::from(4);
@ -568,10 +616,13 @@ async fn function_array_prepend() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::prepend(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::prepend(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[[2,3],1,2]");
@ -597,10 +648,13 @@ async fn function_array_push() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::push(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::push(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,[2,3]]");
@ -658,10 +712,13 @@ async fn function_array_reverse() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::reverse(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::reverse(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[4,3,3,'text',2,1]");
@ -691,10 +748,13 @@ async fn function_array_slice() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::slice(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::slice(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,'text',3,3,4]");
@ -740,10 +800,13 @@ async fn function_array_sort() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::sort(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::sort(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,3,4,4,'text']");
@ -785,10 +848,13 @@ async fn function_array_sort_asc() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::sort::asc(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::sort::asc(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,3,4,4,'text']");
@ -814,10 +880,13 @@ async fn function_array_sort_desc() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::sort::desc(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::sort::desc(). Argument 1 was the wrong type. Expected a array but found 3"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("['text',4,4,3,2,1]");
@ -843,10 +912,13 @@ async fn function_array_union() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function array::union(). Argument 1 was the wrong type. Expected a array but failed to convert 3 into a array"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function array::union(). Argument 1 was the wrong type. Expected a array but found 3",
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1,2,6,3,4,5]");
@ -865,7 +937,7 @@ async fn function_bytes_len() -> Result<(), Error> {
RETURN bytes::len(<bytes>"");
RETURN bytes::len(true);
RETURN bytes::len(<bytes>"π");
RETURN bytes::len("ππ");
RETURN bytes::len(<bytes>"ππ");
"#;
let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test");
@ -877,10 +949,13 @@ async fn function_bytes_len() -> Result<(), Error> {
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function bytes::len(). Argument 1 was the wrong type. Expected a bytes but failed to convert true into a bytes"
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function bytes::len(). Argument 1 was the wrong type. Expected a bytes but found true"
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("2");
@ -1458,8 +1533,8 @@ async fn function_encoding_base64_decode() -> Result<(), Error> {
#[tokio::test]
async fn function_encoding_base64_encode() -> Result<(), Error> {
let sql = r#"
RETURN encoding::base64::encode("");
RETURN encoding::base64::encode("hello");
RETURN encoding::base64::encode(<bytes>"");
RETURN encoding::base64::encode(<bytes>"hello");
"#;
let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test");
@ -1968,10 +2043,13 @@ async fn function_math_bottom() -> Result<(), Error> {
assert_eq!(res.len(), 3);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function math::bottom(). The second argument must be an integer greater than 0."
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function math::bottom(). The second argument must be an integer greater than 0."
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[1]");
@ -2019,10 +2097,13 @@ async fn function_math_fixed() -> Result<(), Error> {
assert_eq!(res.len(), 3);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function math::fixed(). The second argument must be an integer greater than 0."
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function math::fixed(). The second argument must be an integer greater than 0."
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::from(101);
@ -2479,10 +2560,13 @@ async fn function_math_top() -> Result<(), Error> {
assert_eq!(res.len(), 3);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Incorrect arguments for function math::top(). The second argument must be an integer greater than 0."
));
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Incorrect arguments for function math::top(). The second argument must be an integer greater than 0."
),
"{tmp:?}"
);
//
let tmp = res.remove(0).result?;
let val = Value::parse("[3]");

View file

@ -26,7 +26,7 @@ async fn strict_typing_inline() -> Result<(), Error> {
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Expected a int but failed to convert NONE into a int"
Some(e) if e.to_string() == "Expected a int but cannot convert NONE into a int"
));
//
let tmp = res.remove(0).result?;
@ -43,7 +43,7 @@ async fn strict_typing_inline() -> Result<(), Error> {
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Expected a bool | int but failed to convert NONE into a bool | int"
Some(e) if e.to_string() == "Expected a bool | int but cannot convert NONE into a bool | int"
));
//
let tmp = res.remove(0).result?;
@ -113,19 +113,11 @@ async fn strict_typing_inline() -> Result<(), Error> {
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: person:test,
age: 18,
enabled: true,
name: 'Tobie Morgan Hitchcock',
scores: [1.0, 1.0, 2.0, 2.0, 3.0],
}
]",
);
assert_eq!(tmp, val);
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == "Expected a array<float, 5> but the array had 10 items"
));
//
Ok(())
}
@ -138,9 +130,9 @@ async fn strict_typing_defined() -> Result<(), Error> {
DEFINE FIELD name ON person TYPE string;
DEFINE FIELD scores ON person TYPE set<float, 5>;
UPDATE person:test SET age = NONE, enabled = NONE, name = NONE, scored = [1,1,2,2,3,3,4,4,5,5];
UPDATE person:test SET age = '18', enabled = NONE, name = NONE, scored = [1,1,2,2,3,3,4,4,5,5];
UPDATE person:test SET age = '18', enabled = true, name = NONE, scored = [1,1,2,2,3,3,4,4,5,5];
UPDATE person:test SET age = '18', enabled = true, name = 'Tobie Morgan Hitchcock', scores = [1,1,2,2,3,3,4,4,5,5];
UPDATE person:test SET age = 18, enabled = NONE, name = NONE, scored = [1,1,2,2,3,3,4,4,5,5];
UPDATE person:test SET age = 18, enabled = true, name = NONE, scored = [1,1,2,2,3,3,4,4,5,5];
UPDATE person:test SET age = 18, enabled = true, name = 'Tobie Morgan Hitchcock', scores = [1,1,2,2,3,3,4,4,5,5];
";
let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test");

View file

@ -20,7 +20,7 @@ use std::collections::HashMap;
use std::sync::Arc;
use surrealdb::channel;
use surrealdb::channel::Sender;
use surrealdb::dbs::{Response, Session};
use surrealdb::dbs::Session;
use surrealdb::sql::Array;
use surrealdb::sql::Object;
use surrealdb::sql::Strand;