Add support for stricter typings (#1861)
Co-authored-by: Rushmore Mushambi <rushmore@surrealdb.com>
This commit is contained in:
parent
3c027e9e2f
commit
66b105dac0
59 changed files with 3397 additions and 1478 deletions
lib
src
api/engine/remote
doc
err
fnc
key
sql
tests
src/iam
|
@ -404,7 +404,7 @@ async fn router(
|
|||
});
|
||||
} else {
|
||||
*auth = Some(Auth::Bearer {
|
||||
token: value.to_strand().as_string(),
|
||||
token: value.to_raw_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -323,10 +323,10 @@ pub(crate) fn router(
|
|||
Ok(option) => {
|
||||
if let Some(response) = option {
|
||||
trace!(target: LOG, "{response:?}");
|
||||
if let Some(id) = response.id {
|
||||
if let Some((method, sender)) =
|
||||
routes.remove(&id.as_int())
|
||||
{
|
||||
if let Some(Ok(id)) =
|
||||
response.id.map(Value::convert_to_i64)
|
||||
{
|
||||
if let Some((method, sender)) = routes.remove(&id) {
|
||||
let _res = sender
|
||||
.into_send_async(DbResponse::from((
|
||||
method,
|
||||
|
|
|
@ -297,9 +297,8 @@ pub(crate) fn router(
|
|||
Ok(option) => {
|
||||
if let Some(response) = option {
|
||||
trace!(target: LOG, "{response:?}");
|
||||
if let Some(id) = response.id {
|
||||
if let Some((method, sender)) = routes.remove(&id.as_int())
|
||||
{
|
||||
if let Some(Ok(id)) = response.id.map(Value::convert_to_i64) {
|
||||
if let Some((method, sender)) = routes.remove(&id) {
|
||||
let _ = sender
|
||||
.into_send_async(DbResponse::from((
|
||||
method,
|
||||
|
|
|
@ -13,6 +13,6 @@ impl<'a> Document<'a> {
|
|||
_txn: &Transaction,
|
||||
_stm: &Statement<'_>,
|
||||
) -> Result<(), Error> {
|
||||
self.current.to_mut().clear().await
|
||||
self.current.to_mut().clear()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,25 @@ impl<'a> Document<'a> {
|
|||
},
|
||||
_ => Value::None,
|
||||
};
|
||||
// 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 {
|
||||
// There was a conversion error
|
||||
Error::ConvertTo {
|
||||
from,
|
||||
..
|
||||
} => Error::FieldCheck {
|
||||
thing: rid.to_string(),
|
||||
field: fd.name.clone(),
|
||||
value: from.to_string(),
|
||||
check: kind.to_string(),
|
||||
},
|
||||
// There was a different error
|
||||
e => e,
|
||||
})?;
|
||||
}
|
||||
}
|
||||
// Check for a VALUE clause
|
||||
if let Some(expr) = &fd.value {
|
||||
// Configure the context
|
||||
|
@ -52,11 +71,20 @@ impl<'a> Document<'a> {
|
|||
}
|
||||
// Check for a TYPE clause
|
||||
if let Some(kind) = &fd.kind {
|
||||
val = match val {
|
||||
Value::None => val,
|
||||
Value::Null => val,
|
||||
_ => val.convert_to(kind),
|
||||
}
|
||||
val = val.convert_to(kind).map_err(|e| match e {
|
||||
// There was a conversion error
|
||||
Error::ConvertTo {
|
||||
from,
|
||||
..
|
||||
} => Error::FieldCheck {
|
||||
thing: rid.to_string(),
|
||||
field: fd.name.clone(),
|
||||
value: from.to_string(),
|
||||
check: kind.to_string(),
|
||||
},
|
||||
// There was a different error
|
||||
e => e,
|
||||
})?;
|
||||
}
|
||||
// Check for a ASSERT clause
|
||||
if let Some(expr) = &fd.assert {
|
||||
|
@ -70,8 +98,8 @@ impl<'a> Document<'a> {
|
|||
if !expr.compute(&ctx, opt, txn, Some(&self.current)).await?.is_truthy() {
|
||||
return Err(Error::FieldValue {
|
||||
thing: rid.to_string(),
|
||||
value: val.to_string(),
|
||||
field: fd.name.clone(),
|
||||
value: val.to_string(),
|
||||
check: expr.to_string(),
|
||||
});
|
||||
}
|
||||
|
@ -79,9 +107,7 @@ impl<'a> Document<'a> {
|
|||
// Check for a PERMISSIONS clause
|
||||
if opt.perms && opt.auth.perms() {
|
||||
// Get the permission clause
|
||||
let perms = if stm.is_delete() {
|
||||
&fd.permissions.delete
|
||||
} else if self.is_new() {
|
||||
let perms = if self.is_new() {
|
||||
&fd.permissions.create
|
||||
} else {
|
||||
&fd.permissions.update
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::sql::data::Data;
|
|||
use crate::sql::expression::Expression;
|
||||
use crate::sql::field::{Field, Fields};
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::operator::Operator;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::statement::Statement as Query;
|
||||
|
@ -399,9 +400,21 @@ impl<'a> Document<'a> {
|
|||
Expression {
|
||||
l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new(
|
||||
Expression {
|
||||
l: Value::Idiom(key),
|
||||
l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(
|
||||
Box::new(Expression {
|
||||
l: Value::Idiom(key),
|
||||
o: Operator::Nco,
|
||||
r: Value::Number(Number::Int(0)),
|
||||
}),
|
||||
)))),
|
||||
o: Operator::Mul,
|
||||
r: Value::Idiom(key_c.clone()),
|
||||
r: Value::Subquery(Box::new(Subquery::Value(Value::Expression(
|
||||
Box::new(Expression {
|
||||
l: Value::Idiom(key_c.clone()),
|
||||
o: Operator::Nco,
|
||||
r: Value::Number(Number::Int(0)),
|
||||
}),
|
||||
)))),
|
||||
},
|
||||
))))),
|
||||
o: match act {
|
||||
|
@ -415,7 +428,13 @@ impl<'a> Document<'a> {
|
|||
o: Operator::Div,
|
||||
r: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new(
|
||||
Expression {
|
||||
l: Value::Idiom(key_c.clone()),
|
||||
l: Value::Subquery(Box::new(Subquery::Value(Value::Expression(Box::new(
|
||||
Expression {
|
||||
l: Value::Idiom(key_c.clone()),
|
||||
o: Operator::Nco,
|
||||
r: Value::Number(Number::Int(0)),
|
||||
},
|
||||
))))),
|
||||
o: match act {
|
||||
Action::Delete => Operator::Sub,
|
||||
Action::Update => Operator::Add,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::value::Value;
|
||||
use bung::encode::Error as SerdeError;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use storekey::decode::Error as DecodeError;
|
||||
use storekey::encode::Error as EncodeError;
|
||||
use thiserror::Error;
|
||||
|
@ -42,10 +44,6 @@ pub enum Error {
|
|||
#[error("The key being inserted already exists")]
|
||||
TxKeyAlreadyExists,
|
||||
|
||||
/// It's is not possible to convert between the two types
|
||||
#[error("Cannot convert from '{0}' to '{1}'")]
|
||||
TryFromError(String, &'static str),
|
||||
|
||||
/// No namespace has been selected
|
||||
#[error("Specify a namespace to use")]
|
||||
NsEmpty,
|
||||
|
@ -312,6 +310,15 @@ pub enum Error {
|
|||
value: String,
|
||||
},
|
||||
|
||||
/// The specified field did not conform to the field type check
|
||||
#[error("Found {value} for field `{field}`, with record `{thing}`, but expected a {check}")]
|
||||
FieldCheck {
|
||||
thing: String,
|
||||
value: String,
|
||||
field: Idiom,
|
||||
check: String,
|
||||
},
|
||||
|
||||
/// The specified field did not conform to the field ASSERT clause
|
||||
#[error("Found {value} for field `{field}`, with record `{thing}`, but field must conform to: {check}")]
|
||||
FieldValue {
|
||||
|
@ -327,6 +334,37 @@ pub enum Error {
|
|||
value: String,
|
||||
},
|
||||
|
||||
/// The requested function does not exist
|
||||
#[error("Expected a {into} but failed to convert {from} into a {into}")]
|
||||
ConvertTo {
|
||||
from: Value,
|
||||
into: Cow<'static, str>,
|
||||
},
|
||||
|
||||
/// The requested function does not exist
|
||||
#[error("Cannot perform addition with '{0}' and '{1}'")]
|
||||
TryAdd(String, String),
|
||||
|
||||
/// The requested function does not exist
|
||||
#[error("Cannot perform subtraction with '{0}' and '{1}'")]
|
||||
TrySub(String, String),
|
||||
|
||||
/// The requested function does not exist
|
||||
#[error("Cannot perform multiplication with '{0}' and '{1}'")]
|
||||
TryMul(String, String),
|
||||
|
||||
/// The requested function does not exist
|
||||
#[error("Cannot perform division with '{0}' and '{1}'")]
|
||||
TryDiv(String, String),
|
||||
|
||||
/// The requested function does not exist
|
||||
#[error("Cannot raise the value '{0}' with '{1}'")]
|
||||
TryPow(String, String),
|
||||
|
||||
/// It's is not possible to convert between the two types
|
||||
#[error("Cannot convert from '{0}' to '{1}'")]
|
||||
TryFrom(String, &'static str),
|
||||
|
||||
/// There was an error processing a remote HTTP request
|
||||
#[error("There was an error processing a remote HTTP request")]
|
||||
Http(String),
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::{Duration, Number, Strand};
|
||||
use crate::sql::{Array, Datetime, Duration, Number, Strand, Thing};
|
||||
|
||||
/// Implemented by types that are commonly used, in a certain way, as arguments.
|
||||
pub trait FromArg: Sized {
|
||||
/// Potentially fallible conversion from a Value to an argument. Errors will be propagated
|
||||
/// to the caller, although it is also possible to return a none/null Value.
|
||||
fn from_arg(arg: Value) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
|
@ -17,49 +15,79 @@ impl FromArg for Value {
|
|||
|
||||
impl FromArg for String {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
Ok(arg.as_string())
|
||||
arg.convert_to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for Strand {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
Ok(arg.as_strand())
|
||||
arg.convert_to_strand()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for Number {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
Ok(arg.as_number())
|
||||
arg.convert_to_number()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for Datetime {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
arg.convert_to_datetime()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for Duration {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
Ok(arg.as_duration())
|
||||
arg.convert_to_duration()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for f64 {
|
||||
impl FromArg for Thing {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
Ok(arg.as_float())
|
||||
arg.convert_to_record()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for Array {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
arg.convert_to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for i64 {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
Ok(arg.as_int())
|
||||
arg.convert_to_i64()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for u64 {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
arg.convert_to_u64()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for f64 {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
arg.convert_to_f64()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for isize {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
Ok(arg.as_int() as isize)
|
||||
Ok(arg.convert_to_i64()? as isize)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromArg for usize {
|
||||
fn from_arg(arg: Value) -> Result<Self, Error> {
|
||||
Ok(arg.as_int() as usize)
|
||||
Ok(arg.convert_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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +121,17 @@ macro_rules! impl_tuple {
|
|||
_ => format!("Expected {} arguments.", $len),
|
||||
}
|
||||
})?;
|
||||
Ok(($($T::from_arg($T)?,)*))
|
||||
#[allow(unused_mut, unused_variables)]
|
||||
let mut i = 0;
|
||||
Ok((
|
||||
$({
|
||||
i += 1;
|
||||
$T::from_arg($T).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument {i} was the wrong type. {e}"),
|
||||
})?
|
||||
},)*
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,14 +150,19 @@ impl<A: FromArg> FromArgs for (Option<A>,) {
|
|||
name: name.to_owned(),
|
||||
message: String::from("Expected 0 or 1 arguments."),
|
||||
};
|
||||
|
||||
// Process the function arguments
|
||||
let mut args = args.into_iter();
|
||||
// Process the first function argument
|
||||
let a = match args.next() {
|
||||
Some(a) => Some(A::from_arg(a)?),
|
||||
Some(a) => Some(A::from_arg(a).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 1 was the wrong type. {e}"),
|
||||
})?),
|
||||
None => None,
|
||||
};
|
||||
// Process additional function arguments
|
||||
if args.next().is_some() {
|
||||
// Too many.
|
||||
// Too many arguments
|
||||
return Err(err());
|
||||
}
|
||||
Ok((a,))
|
||||
|
@ -133,15 +176,20 @@ impl<A: FromArg, B: FromArg> FromArgs for (A, Option<B>) {
|
|||
name: name.to_owned(),
|
||||
message: String::from("Expected 1 or 2 arguments."),
|
||||
};
|
||||
|
||||
// Process the function arguments
|
||||
let mut args = args.into_iter();
|
||||
let a = A::from_arg(args.next().ok_or_else(err)?)?;
|
||||
// Process the first argument
|
||||
let a = A::from_arg(args.next().ok_or_else(err)?).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 1 was the wrong type. {e}"),
|
||||
})?;
|
||||
let b = match args.next() {
|
||||
Some(b) => Some(B::from_arg(b)?),
|
||||
None => None,
|
||||
};
|
||||
// Process additional function arguments
|
||||
if args.next().is_some() {
|
||||
// Too many.
|
||||
// Too many arguments
|
||||
return Err(err());
|
||||
}
|
||||
Ok((a, b))
|
||||
|
@ -155,16 +203,29 @@ impl<A: FromArg, B: FromArg, C: FromArg> FromArgs for (A, B, Option<C>) {
|
|||
name: name.to_owned(),
|
||||
message: String::from("Expected 2 or 3 arguments."),
|
||||
};
|
||||
|
||||
// Process the function arguments
|
||||
let mut args = args.into_iter();
|
||||
let a = A::from_arg(args.next().ok_or_else(err)?)?;
|
||||
let b = B::from_arg(args.next().ok_or_else(err)?)?;
|
||||
// Process the first function argument
|
||||
let a = A::from_arg(args.next().ok_or_else(err)?).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 1 was the wrong type. {e}"),
|
||||
})?;
|
||||
// Process the second function argument
|
||||
let b = B::from_arg(args.next().ok_or_else(err)?).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 2 was the wrong type. {e}"),
|
||||
})?;
|
||||
// Process the third function argument
|
||||
let c = match args.next() {
|
||||
Some(c) => Some(C::from_arg(c)?),
|
||||
Some(c) => Some(C::from_arg(c).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 3 was the wrong type. {e}"),
|
||||
})?),
|
||||
None => None,
|
||||
};
|
||||
// Process additional function arguments
|
||||
if args.next().is_some() {
|
||||
// Too many.
|
||||
// Too many arguments
|
||||
return Err(err());
|
||||
}
|
||||
Ok((a, b, c))
|
||||
|
@ -179,18 +240,27 @@ impl<A: FromArg, B: FromArg> FromArgs for (Option<A>, Option<B>) {
|
|||
name: name.to_owned(),
|
||||
message: String::from("Expected 0, 1, or 2 arguments."),
|
||||
};
|
||||
|
||||
// Process the function arguments
|
||||
let mut args = args.into_iter();
|
||||
// Process the first function argument
|
||||
let a = match args.next() {
|
||||
Some(a) => Some(A::from_arg(a)?),
|
||||
Some(a) => Some(A::from_arg(a).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 1 was the wrong type. {e}"),
|
||||
})?),
|
||||
None => None,
|
||||
};
|
||||
// Process the second function argument
|
||||
let b = match args.next() {
|
||||
Some(b) => Some(B::from_arg(b)?),
|
||||
Some(b) => Some(B::from_arg(b).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 2 was the wrong type. {e}"),
|
||||
})?),
|
||||
None => None,
|
||||
};
|
||||
// Process additional function arguments
|
||||
if args.next().is_some() {
|
||||
// Too many.
|
||||
// Too many arguments
|
||||
return Err(err());
|
||||
}
|
||||
Ok((a, b))
|
||||
|
@ -204,18 +274,27 @@ impl<A: FromArg, B: FromArg> FromArgs for (Option<(A, B)>,) {
|
|||
name: name.to_owned(),
|
||||
message: String::from("Expected 0 or 2 arguments."),
|
||||
};
|
||||
|
||||
// Process the function arguments
|
||||
let mut args = args.into_iter();
|
||||
// Process the first function argument
|
||||
let a = match args.next() {
|
||||
Some(a) => Some(A::from_arg(a)?),
|
||||
Some(a) => Some(A::from_arg(a).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 1 was the wrong type. {e}"),
|
||||
})?),
|
||||
None => None,
|
||||
};
|
||||
// Process the second function argument
|
||||
let b = match args.next() {
|
||||
Some(b) => Some(B::from_arg(b)?),
|
||||
Some(b) => Some(B::from_arg(b).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 2 was the wrong type. {e}"),
|
||||
})?),
|
||||
None => None,
|
||||
};
|
||||
// Process additional function arguments
|
||||
if a.is_some() != b.is_some() || args.next().is_some() {
|
||||
// One argument, or too many arguments.
|
||||
// One argument, or too many arguments
|
||||
return Err(err());
|
||||
}
|
||||
Ok((a.zip(b),))
|
||||
|
@ -230,19 +309,32 @@ impl<A: FromArg, B: FromArg, C: FromArg> FromArgs for (A, Option<B>, Option<C>)
|
|||
name: name.to_owned(),
|
||||
message: String::from("Expected 1, 2, or 3 arguments."),
|
||||
};
|
||||
|
||||
// Process the function arguments
|
||||
let mut args = args.into_iter();
|
||||
let a = A::from_arg(args.next().ok_or_else(err)?)?;
|
||||
// Process the first function argument
|
||||
let a = A::from_arg(args.next().ok_or_else(err)?).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 1 was the wrong type. {e}"),
|
||||
})?;
|
||||
// Process the second function argument
|
||||
let b = match args.next() {
|
||||
Some(b) => Some(B::from_arg(b)?),
|
||||
Some(b) => Some(B::from_arg(b).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 2 was the wrong type. {e}"),
|
||||
})?),
|
||||
None => None,
|
||||
};
|
||||
// Process the third function argument
|
||||
let c = match args.next() {
|
||||
Some(c) => Some(C::from_arg(c)?),
|
||||
Some(c) => Some(C::from_arg(c).map_err(|e| Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Argument 3 was the wrong type. {e}"),
|
||||
})?),
|
||||
None => None,
|
||||
};
|
||||
// Process additional function arguments
|
||||
if args.next().is_some() {
|
||||
// Too many.
|
||||
// Too many arguments
|
||||
return Err(err());
|
||||
}
|
||||
Ok((a, b, c))
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::array::Array;
|
||||
use crate::sql::array::Combine;
|
||||
use crate::sql::array::Complement;
|
||||
use crate::sql::array::Concat;
|
||||
|
@ -9,273 +10,186 @@ use crate::sql::array::Union;
|
|||
use crate::sql::array::Uniq;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
pub fn add((array, value): (Value, Value)) -> Result<Value, Error> {
|
||||
match (array, value) {
|
||||
(Value::Array(mut arr), Value::Array(other)) => {
|
||||
for v in other.0 {
|
||||
if !arr.0.iter().any(|x| *x == v) {
|
||||
arr.0.push(v)
|
||||
pub fn add((mut array, value): (Array, Value)) -> Result<Value, Error> {
|
||||
match value {
|
||||
Value::Array(value) => {
|
||||
for v in value.0 {
|
||||
if !array.0.iter().any(|x| *x == v) {
|
||||
array.0.push(v)
|
||||
}
|
||||
}
|
||||
Ok(arr.into())
|
||||
Ok(array.into())
|
||||
}
|
||||
(Value::Array(mut arr), value) => {
|
||||
if !arr.0.iter().any(|x| *x == value) {
|
||||
arr.0.push(value)
|
||||
value => {
|
||||
if !array.0.iter().any(|x| *x == value) {
|
||||
array.0.push(value)
|
||||
}
|
||||
Ok(arr.into())
|
||||
Ok(array.into())
|
||||
}
|
||||
_ => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Array(v) => Ok(v.iter().all(Value::is_truthy).into()),
|
||||
_ => Ok(Value::False),
|
||||
}
|
||||
pub fn all((array,): (Array,)) -> Result<Value, Error> {
|
||||
Ok(array.iter().all(Value::is_truthy).into())
|
||||
}
|
||||
|
||||
pub fn any((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Array(v) => Ok(v.iter().any(Value::is_truthy).into()),
|
||||
_ => Ok(Value::False),
|
||||
}
|
||||
pub fn any((array,): (Array,)) -> Result<Value, Error> {
|
||||
Ok(array.iter().any(Value::is_truthy).into())
|
||||
}
|
||||
|
||||
pub fn append((array, value): (Value, Value)) -> Result<Value, Error> {
|
||||
match array {
|
||||
Value::Array(mut v) => {
|
||||
v.push(value);
|
||||
Ok(v.into())
|
||||
}
|
||||
_ => Ok(Value::None),
|
||||
}
|
||||
pub fn append((mut array, value): (Array, Value)) -> Result<Value, Error> {
|
||||
array.push(value);
|
||||
Ok(array.into())
|
||||
}
|
||||
|
||||
pub fn combine(arrays: (Value, Value)) -> Result<Value, Error> {
|
||||
Ok(match arrays {
|
||||
(Value::Array(v), Value::Array(w)) => v.combine(w).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn combine((array, other): (Array, Array)) -> Result<Value, Error> {
|
||||
Ok(array.combine(other).into())
|
||||
}
|
||||
|
||||
pub fn complement(arrays: (Value, Value)) -> Result<Value, Error> {
|
||||
Ok(match arrays {
|
||||
(Value::Array(v), Value::Array(w)) => v.complement(w).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn complement((array, other): (Array, Array)) -> Result<Value, Error> {
|
||||
Ok(array.complement(other).into())
|
||||
}
|
||||
|
||||
pub fn concat(arrays: (Value, Value)) -> Result<Value, Error> {
|
||||
Ok(match arrays {
|
||||
(Value::Array(v), Value::Array(w)) => v.concat(w).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn concat((array, other): (Array, Array)) -> Result<Value, Error> {
|
||||
Ok(array.concat(other).into())
|
||||
}
|
||||
|
||||
pub fn difference(arrays: (Value, Value)) -> Result<Value, Error> {
|
||||
Ok(match arrays {
|
||||
(Value::Array(v), Value::Array(w)) => v.difference(w).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn difference((array, other): (Array, Array)) -> Result<Value, Error> {
|
||||
Ok(array.difference(other).into())
|
||||
}
|
||||
|
||||
pub fn distinct((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Array(v) => Ok(v.uniq().into()),
|
||||
_ => Ok(Value::None),
|
||||
}
|
||||
pub fn distinct((array,): (Array,)) -> Result<Value, Error> {
|
||||
Ok(array.uniq().into())
|
||||
}
|
||||
|
||||
pub fn flatten((arg,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match arg {
|
||||
Value::Array(v) => v.flatten().into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn flatten((array,): (Array,)) -> Result<Value, Error> {
|
||||
Ok(array.flatten().into())
|
||||
}
|
||||
|
||||
pub fn group((arg,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match arg {
|
||||
Value::Array(v) => v.flatten().uniq().into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn group((array,): (Array,)) -> Result<Value, Error> {
|
||||
Ok(array.flatten().uniq().into())
|
||||
}
|
||||
|
||||
pub fn insert((array, value, index): (Value, Value, Option<Value>)) -> Result<Value, Error> {
|
||||
match (array, index) {
|
||||
(Value::Array(mut v), Some(Value::Number(i))) => {
|
||||
let mut i = i.as_int();
|
||||
pub fn insert((mut array, value, index): (Array, Value, Option<i64>)) -> Result<Value, Error> {
|
||||
match index {
|
||||
Some(mut index) => {
|
||||
// Negative index means start from the back
|
||||
if i < 0 {
|
||||
i += v.len() as i64;
|
||||
if index < 0 {
|
||||
index += array.len() as i64;
|
||||
}
|
||||
// Invalid index so return array unaltered
|
||||
if i > v.len() as i64 || i < 0 {
|
||||
return Ok(v.into());
|
||||
if index > array.len() as i64 || index < 0 {
|
||||
return Ok(array.into());
|
||||
}
|
||||
// Insert the value into the array
|
||||
v.insert(i as usize, value);
|
||||
array.insert(index as usize, value);
|
||||
// Return the array
|
||||
Ok(v.into())
|
||||
Ok(array.into())
|
||||
}
|
||||
(Value::Array(mut v), None) => {
|
||||
v.push(value);
|
||||
Ok(v.into())
|
||||
None => {
|
||||
array.push(value);
|
||||
Ok(array.into())
|
||||
}
|
||||
(_, _) => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intersect(arrays: (Value, Value)) -> Result<Value, Error> {
|
||||
Ok(match arrays {
|
||||
(Value::Array(v), Value::Array(w)) => v.intersect(w).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn intersect((array, other): (Array, Array)) -> Result<Value, Error> {
|
||||
Ok(array.intersect(other).into())
|
||||
}
|
||||
|
||||
pub fn len((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Array(v) => Ok(v.len().into()),
|
||||
_ => Ok(Value::None),
|
||||
pub fn len((array,): (Array,)) -> Result<Value, Error> {
|
||||
Ok(array.len().into())
|
||||
}
|
||||
|
||||
pub fn max((array,): (Array,)) -> Result<Value, Error> {
|
||||
Ok(array.into_iter().max().unwrap_or_default())
|
||||
}
|
||||
|
||||
pub fn min((array,): (Array,)) -> Result<Value, Error> {
|
||||
Ok(array.into_iter().min().unwrap_or_default())
|
||||
}
|
||||
|
||||
pub fn pop((mut array,): (Array,)) -> Result<Value, Error> {
|
||||
Ok(array.pop().into())
|
||||
}
|
||||
|
||||
pub fn prepend((mut array, value): (Array, Value)) -> Result<Value, Error> {
|
||||
array.insert(0, value);
|
||||
Ok(array.into())
|
||||
}
|
||||
|
||||
pub fn push((mut array, value): (Array, Value)) -> Result<Value, Error> {
|
||||
array.push(value);
|
||||
Ok(array.into())
|
||||
}
|
||||
|
||||
pub fn remove((mut array, mut index): (Array, i64)) -> Result<Value, Error> {
|
||||
// Negative index means start from the back
|
||||
if index < 0 {
|
||||
index += array.len() as i64;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Array(v) => Ok(v.into_iter().max().unwrap_or(Value::None)),
|
||||
_ => Ok(Value::None),
|
||||
// Invalid index so return array unaltered
|
||||
if index >= array.len() as i64 || index < 0 {
|
||||
return Ok(array.into());
|
||||
}
|
||||
// Remove the value from the array
|
||||
array.remove(index as usize);
|
||||
// Return the array
|
||||
Ok(array.into())
|
||||
}
|
||||
|
||||
pub fn min((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Array(v) => Ok(v.into_iter().min().unwrap_or(Value::None)),
|
||||
_ => Ok(Value::None),
|
||||
}
|
||||
pub fn reverse((mut array,): (Array,)) -> Result<Value, Error> {
|
||||
array.reverse();
|
||||
Ok(array.into())
|
||||
}
|
||||
|
||||
pub fn pop((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Array(mut v) => Ok(v.pop().into()),
|
||||
_ => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepend((array, value): (Value, Value)) -> Result<Value, Error> {
|
||||
match array {
|
||||
Value::Array(mut v) => {
|
||||
v.insert(0, value);
|
||||
Ok(v.into())
|
||||
pub fn sort((mut array, order): (Array, Option<Value>)) -> Result<Value, Error> {
|
||||
match order {
|
||||
// If "asc", sort ascending
|
||||
Some(Value::Strand(s)) if s.as_str() == "asc" => {
|
||||
array.sort_unstable();
|
||||
Ok(array.into())
|
||||
}
|
||||
_ => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push((array, value): (Value, Value)) -> Result<Value, Error> {
|
||||
match array {
|
||||
Value::Array(mut v) => {
|
||||
v.push(value);
|
||||
Ok(v.into())
|
||||
// If "desc", sort descending
|
||||
Some(Value::Strand(s)) if s.as_str() == "desc" => {
|
||||
array.sort_unstable_by(|a, b| b.cmp(a));
|
||||
Ok(array.into())
|
||||
}
|
||||
_ => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove((array, index): (Value, Value)) -> Result<Value, Error> {
|
||||
match (array, index) {
|
||||
(Value::Array(mut v), Value::Number(i)) => {
|
||||
let mut i = i.as_int();
|
||||
// Negative index means start from the back
|
||||
if i < 0 {
|
||||
i += v.len() as i64;
|
||||
}
|
||||
// Invalid index so return array unaltered
|
||||
if i >= v.len() as i64 || i < 0 {
|
||||
return Ok(v.into());
|
||||
}
|
||||
// Remove the value from the array
|
||||
v.remove(i as usize);
|
||||
// Return the array
|
||||
Ok(v.into())
|
||||
// If true, sort ascending
|
||||
Some(Value::True) => {
|
||||
array.sort_unstable();
|
||||
Ok(array.into())
|
||||
}
|
||||
(Value::Array(v), _) => Ok(v.into()),
|
||||
(_, _) => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reverse((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Array(mut v) => {
|
||||
v.reverse();
|
||||
Ok(v.into())
|
||||
// If false, sort descending
|
||||
Some(Value::False) => {
|
||||
array.sort_unstable_by(|a, b| b.cmp(a));
|
||||
Ok(array.into())
|
||||
}
|
||||
// Sort ascending by default
|
||||
_ => {
|
||||
array.sort_unstable();
|
||||
Ok(array.into())
|
||||
}
|
||||
_ => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort((array, order): (Value, Option<Value>)) -> Result<Value, Error> {
|
||||
match array {
|
||||
Value::Array(mut v) => match order {
|
||||
// If "asc", sort ascending
|
||||
Some(Value::Strand(s)) if s.as_str() == "asc" => {
|
||||
v.sort_unstable();
|
||||
Ok(v.into())
|
||||
}
|
||||
// If "desc", sort descending
|
||||
Some(Value::Strand(s)) if s.as_str() == "desc" => {
|
||||
v.sort_unstable_by(|a, b| b.cmp(a));
|
||||
Ok(v.into())
|
||||
}
|
||||
// If true, sort ascending
|
||||
Some(Value::True) => {
|
||||
v.sort_unstable();
|
||||
Ok(v.into())
|
||||
}
|
||||
// If false, sort descending
|
||||
Some(Value::False) => {
|
||||
v.sort_unstable_by(|a, b| b.cmp(a));
|
||||
Ok(v.into())
|
||||
}
|
||||
// Sort ascending by default
|
||||
_ => {
|
||||
v.sort_unstable();
|
||||
Ok(v.into())
|
||||
}
|
||||
},
|
||||
v => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(arrays: (Value, Value)) -> Result<Value, Error> {
|
||||
Ok(match arrays {
|
||||
(Value::Array(v), Value::Array(w)) => v.union(w).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn union((array, other): (Array, Array)) -> Result<Value, Error> {
|
||||
Ok(array.union(other).into())
|
||||
}
|
||||
|
||||
pub mod sort {
|
||||
|
||||
use crate::err::Error;
|
||||
use crate::sql::array::Array;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
pub fn asc((array,): (Value,)) -> Result<Value, Error> {
|
||||
match array {
|
||||
Value::Array(mut v) => {
|
||||
v.sort_unstable();
|
||||
Ok(v.into())
|
||||
}
|
||||
v => Ok(v),
|
||||
}
|
||||
pub fn asc((mut array,): (Array,)) -> Result<Value, Error> {
|
||||
array.sort_unstable();
|
||||
Ok(array.into())
|
||||
}
|
||||
|
||||
pub fn desc((array,): (Value,)) -> Result<Value, Error> {
|
||||
match array {
|
||||
Value::Array(mut v) => {
|
||||
v.sort_unstable_by(|a, b| b.cmp(a));
|
||||
Ok(v.into())
|
||||
}
|
||||
v => Ok(v),
|
||||
}
|
||||
pub fn desc((mut array,): (Array,)) -> Result<Value, Error> {
|
||||
array.sort_unstable_by(|a, b| b.cmp(a));
|
||||
Ok(array.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::err::Error;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
pub fn run(_: &Context, name: &str, val: Value) -> Result<Value, Error> {
|
||||
match name {
|
||||
"bool" => bool(val),
|
||||
"datetime" => datetime(val),
|
||||
"decimal" => decimal(val),
|
||||
"duration" => duration(val),
|
||||
"float" => float(val),
|
||||
"int" => int(val),
|
||||
"number" => number(val),
|
||||
"string" => string(val),
|
||||
_ => Ok(val),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bool(val: Value) -> Result<Value, Error> {
|
||||
Ok(val.is_truthy().into())
|
||||
}
|
||||
|
||||
pub fn datetime(val: Value) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Value::Datetime(_) => val,
|
||||
_ => Value::Datetime(val.as_datetime()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn decimal(val: Value) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Value::Number(Number::Decimal(_)) => val,
|
||||
_ => Value::Number(Number::Decimal(val.as_decimal())),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn duration(val: Value) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Value::Duration(_) => val,
|
||||
_ => Value::Duration(val.as_duration()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn float(val: Value) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Value::Number(Number::Float(_)) => val,
|
||||
_ => Value::Number(Number::Float(val.as_float())),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn int(val: Value) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Value::Number(Number::Int(_)) => val,
|
||||
_ => Value::Number(Number::Int(val.as_int())),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn number(val: Value) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Value::Number(Number::Decimal(_)) => val,
|
||||
_ => Value::Number(Number::Decimal(val.as_decimal())),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn string(val: Value) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Value::Strand(_) => val,
|
||||
_ => Value::Strand(val.as_strand()),
|
||||
})
|
||||
}
|
|
@ -1,44 +1,78 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::duration::Duration;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
pub fn days((duration,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match duration {
|
||||
Value::Duration(d) => d.days(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn days((val,): (Duration,)) -> Result<Value, Error> {
|
||||
Ok(val.days().into())
|
||||
}
|
||||
|
||||
pub fn hours((duration,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match duration {
|
||||
Value::Duration(d) => d.hours(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn hours((val,): (Duration,)) -> Result<Value, Error> {
|
||||
Ok(val.hours().into())
|
||||
}
|
||||
|
||||
pub fn mins((duration,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match duration {
|
||||
Value::Duration(d) => d.mins(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn micros((val,): (Duration,)) -> Result<Value, Error> {
|
||||
Ok(val.micros().into())
|
||||
}
|
||||
|
||||
pub fn secs((duration,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match duration {
|
||||
Value::Duration(d) => d.secs(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn millis((val,): (Duration,)) -> Result<Value, Error> {
|
||||
Ok(val.millis().into())
|
||||
}
|
||||
|
||||
pub fn weeks((duration,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match duration {
|
||||
Value::Duration(d) => d.weeks(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn mins((val,): (Duration,)) -> Result<Value, Error> {
|
||||
Ok(val.mins().into())
|
||||
}
|
||||
|
||||
pub fn years((duration,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match duration {
|
||||
Value::Duration(d) => d.years(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn nanos((val,): (Duration,)) -> Result<Value, Error> {
|
||||
Ok(val.nanos().into())
|
||||
}
|
||||
|
||||
pub fn secs((val,): (Duration,)) -> Result<Value, Error> {
|
||||
Ok(val.secs().into())
|
||||
}
|
||||
|
||||
pub fn weeks((val,): (Duration,)) -> Result<Value, Error> {
|
||||
Ok(val.weeks().into())
|
||||
}
|
||||
|
||||
pub fn years((val,): (Duration,)) -> Result<Value, Error> {
|
||||
Ok(val.years().into())
|
||||
}
|
||||
|
||||
pub mod from {
|
||||
|
||||
use crate::err::Error;
|
||||
use crate::sql::duration::Duration;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
pub fn days((val,): (u64,)) -> Result<Value, Error> {
|
||||
Ok(Duration::from_days(val).into())
|
||||
}
|
||||
|
||||
pub fn hours((val,): (u64,)) -> Result<Value, Error> {
|
||||
Ok(Duration::from_hours(val).into())
|
||||
}
|
||||
|
||||
pub fn micros((val,): (u64,)) -> Result<Value, Error> {
|
||||
Ok(Duration::from_micros(val).into())
|
||||
}
|
||||
|
||||
pub fn millis((val,): (u64,)) -> Result<Value, Error> {
|
||||
Ok(Duration::from_millis(val).into())
|
||||
}
|
||||
|
||||
pub fn mins((val,): (u64,)) -> Result<Value, Error> {
|
||||
Ok(Duration::from_mins(val).into())
|
||||
}
|
||||
|
||||
pub fn nanos((val,): (u64,)) -> Result<Value, Error> {
|
||||
Ok(Duration::from_nanos(val).into())
|
||||
}
|
||||
|
||||
pub fn secs((val,): (u64,)) -> Result<Value, Error> {
|
||||
Ok(Duration::from_secs(val).into())
|
||||
}
|
||||
|
||||
pub fn weeks((val,): (u64,)) -> Result<Value, Error> {
|
||||
Ok(Duration::from_weeks(val).into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,24 @@ pub fn abs((arg,): (Number,)) -> Result<Value, Error> {
|
|||
Ok(arg.abs().into())
|
||||
}
|
||||
|
||||
pub fn bottom((array, c): (Value, i64)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().bottom(c).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn bottom((array, c): (Vec<Number>, i64)) -> Result<Value, Error> {
|
||||
if c > 0 {
|
||||
Ok(array.bottom(c).into())
|
||||
} else {
|
||||
Err(Error::InvalidArguments {
|
||||
name: String::from("math::bottom"),
|
||||
message: String::from("The second argument must be an integer greater than 0."),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ceil((arg,): (Number,)) -> Result<Value, Error> {
|
||||
Ok(arg.ceil().into())
|
||||
}
|
||||
|
||||
pub fn fixed((v, p): (Number, i64)) -> Result<Value, Error> {
|
||||
pub fn fixed((arg, p): (Number, i64)) -> Result<Value, Error> {
|
||||
if p > 0 {
|
||||
Ok(v.fixed(p as usize).into())
|
||||
Ok(arg.fixed(p as usize).into())
|
||||
} else {
|
||||
Err(Error::InvalidArguments {
|
||||
name: String::from("math::fixed"),
|
||||
|
@ -45,101 +49,65 @@ pub fn floor((arg,): (Number,)) -> Result<Value, Error> {
|
|||
Ok(arg.floor().into())
|
||||
}
|
||||
|
||||
pub fn interquartile((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().sorted().interquartile().into(),
|
||||
_ => Value::None,
|
||||
pub fn interquartile((mut array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.sorted().interquartile().into())
|
||||
}
|
||||
|
||||
pub fn max((array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(match array.into_iter().max() {
|
||||
Some(v) => v.into(),
|
||||
None => Value::None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn max((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => match v.as_numbers().into_iter().max() {
|
||||
Some(v) => v.into(),
|
||||
None => Value::None,
|
||||
},
|
||||
v => v,
|
||||
pub fn mean((array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.mean().into())
|
||||
}
|
||||
|
||||
pub fn median((mut array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(match array.is_empty() {
|
||||
true => Value::None,
|
||||
false => array.sorted().median().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mean((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => match v.is_empty() {
|
||||
true => Value::None,
|
||||
false => v.as_numbers().mean().into(),
|
||||
},
|
||||
_ => Value::None,
|
||||
pub fn midhinge((mut array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.sorted().midhinge().into())
|
||||
}
|
||||
|
||||
pub fn min((array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(match array.into_iter().min() {
|
||||
Some(v) => v.into(),
|
||||
None => Value::None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn median((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => match v.is_empty() {
|
||||
true => Value::None,
|
||||
false => v.as_numbers().sorted().median().into(),
|
||||
},
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn mode((array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.mode().into())
|
||||
}
|
||||
|
||||
pub fn midhinge((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().sorted().midhinge().into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn nearestrank((mut array, n): (Vec<Number>, Number)) -> Result<Value, Error> {
|
||||
Ok(array.sorted().nearestrank(n).into())
|
||||
}
|
||||
|
||||
pub fn min((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => match v.as_numbers().into_iter().min() {
|
||||
Some(v) => v.into(),
|
||||
None => Value::None,
|
||||
},
|
||||
v => v,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mode((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().mode().into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn nearestrank((array, n): (Value, Number)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().sorted().nearestrank(n).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn percentile((array, n): (Value, Number)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().sorted().percentile(n).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn percentile((mut array, n): (Vec<Number>, Number)) -> Result<Value, Error> {
|
||||
Ok(array.sorted().percentile(n).into())
|
||||
}
|
||||
|
||||
pub fn pow((arg, pow): (Number, Number)) -> Result<Value, Error> {
|
||||
Ok(arg.pow(pow).into())
|
||||
}
|
||||
|
||||
pub fn product((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().into_iter().product::<Number>().into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn product((array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.into_iter().product::<Number>().into())
|
||||
}
|
||||
|
||||
pub fn round((arg,): (Number,)) -> Result<Value, Error> {
|
||||
Ok(arg.round().into())
|
||||
}
|
||||
|
||||
pub fn spread((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().spread().into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn spread((array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.spread().into())
|
||||
}
|
||||
|
||||
pub fn sqrt((arg,): (Number,)) -> Result<Value, Error> {
|
||||
|
@ -149,37 +117,29 @@ pub fn sqrt((arg,): (Number,)) -> Result<Value, Error> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn stddev((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().deviation(true).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn stddev((array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.deviation(true).into())
|
||||
}
|
||||
|
||||
pub fn sum((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().into_iter().sum::<Number>().into(),
|
||||
v => v.as_number().into(),
|
||||
})
|
||||
pub fn sum((array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.into_iter().sum::<Number>().into())
|
||||
}
|
||||
|
||||
pub fn top((array, c): (Value, i64)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().top(c).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn top((array, c): (Vec<Number>, i64)) -> Result<Value, Error> {
|
||||
if c > 0 {
|
||||
Ok(array.top(c).into())
|
||||
} else {
|
||||
Err(Error::InvalidArguments {
|
||||
name: String::from("math::top"),
|
||||
message: String::from("The second argument must be an integer greater than 0."),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trimean((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().sorted().trimean().into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn trimean((mut array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.sorted().trimean().into())
|
||||
}
|
||||
|
||||
pub fn variance((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().variance(true).into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn variance((array,): (Vec<Number>,)) -> Result<Value, Error> {
|
||||
Ok(array.variance(true).into())
|
||||
}
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::thing::Thing;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
pub fn id((arg,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match arg {
|
||||
Value::Thing(v) => v.id.into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn id((arg,): (Thing,)) -> Result<Value, Error> {
|
||||
Ok(arg.id.into())
|
||||
}
|
||||
|
||||
pub fn tb((arg,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match arg {
|
||||
Value::Thing(v) => v.tb.into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn tb((arg,): (Thing,)) -> Result<Value, Error> {
|
||||
Ok(arg.tb.into())
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::sql::value::Value;
|
|||
|
||||
pub mod args;
|
||||
pub mod array;
|
||||
pub mod cast;
|
||||
pub mod count;
|
||||
pub mod crypto;
|
||||
pub mod duration;
|
||||
|
@ -25,22 +24,21 @@ pub mod time;
|
|||
pub mod r#type;
|
||||
pub mod util;
|
||||
|
||||
/// Attempts to run any function.
|
||||
/// Attempts to run any function
|
||||
pub async fn run(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Value, Error> {
|
||||
if is_asynchronous(name) {
|
||||
if name.eq("sleep")
|
||||
|| name.starts_with("http")
|
||||
|| name.starts_with("crypto::argon2")
|
||||
|| name.starts_with("crypto::bcrypt")
|
||||
|| name.starts_with("crypto::pbkdf2")
|
||||
|| name.starts_with("crypto::scrypt")
|
||||
{
|
||||
asynchronous(ctx, name, args).await
|
||||
} else {
|
||||
synchronous(ctx, name, args)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tells if the function is asynchronous
|
||||
fn is_asynchronous(name: &str) -> bool {
|
||||
name.eq("sleep")
|
||||
|| name.starts_with("http")
|
||||
|| (name.starts_with("crypto") && (name.ends_with("compare") || name.ends_with("generate")))
|
||||
}
|
||||
|
||||
/// Each function is specified by its name (a string literal) followed by its path. The path
|
||||
/// may be followed by one parenthesized argument, e.g. ctx, which is passed to the function
|
||||
/// before the remainder of the arguments. The path may be followed by `.await` to signify that
|
||||
|
@ -101,10 +99,21 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Va
|
|||
//
|
||||
"duration::days" => duration::days,
|
||||
"duration::hours" => duration::hours,
|
||||
"duration::micros" => duration::micros,
|
||||
"duration::millis" => duration::millis,
|
||||
"duration::mins" => duration::mins,
|
||||
"duration::nanos" => duration::nanos,
|
||||
"duration::secs" => duration::secs,
|
||||
"duration::weeks" => duration::weeks,
|
||||
"duration::years" => duration::years,
|
||||
"duration::from::days" => duration::from::days,
|
||||
"duration::from::hours" => duration::from::hours,
|
||||
"duration::from::micros" => duration::from::micros,
|
||||
"duration::from::millis" => duration::from::millis,
|
||||
"duration::from::mins" => duration::from::mins,
|
||||
"duration::from::nanos" => duration::from::nanos,
|
||||
"duration::from::secs" => duration::from::secs,
|
||||
"duration::from::weeks" => duration::from::weeks,
|
||||
//
|
||||
"geo::area" => geo::area,
|
||||
"geo::bearing" => geo::bearing,
|
||||
|
@ -223,6 +232,10 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Va
|
|||
"time::week" => time::week,
|
||||
"time::yday" => time::yday,
|
||||
"time::year" => time::year,
|
||||
"time::from::micros" => time::from::micros,
|
||||
"time::from::millis" => time::from::millis,
|
||||
"time::from::secs" => time::from::secs,
|
||||
"time::from::unix" => time::from::unix,
|
||||
//
|
||||
"type::bool" => r#type::bool,
|
||||
"type::datetime" => r#type::datetime,
|
||||
|
@ -232,7 +245,6 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Va
|
|||
"type::int" => r#type::int,
|
||||
"type::number" => r#type::number,
|
||||
"type::point" => r#type::point,
|
||||
"type::regex" => r#type::regex,
|
||||
"type::string" => r#type::string,
|
||||
"type::table" => r#type::table,
|
||||
"type::thing" => r#type::thing,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::TryAdd;
|
||||
use crate::sql::value::TryDiv;
|
||||
use crate::sql::value::TryMul;
|
||||
use crate::sql::value::TryPow;
|
||||
use crate::sql::value::TrySub;
|
||||
use crate::sql::value::Value;
|
||||
use std::ops::Add;
|
||||
use std::ops::Div;
|
||||
use std::ops::Mul;
|
||||
use std::ops::Sub;
|
||||
|
||||
pub fn or(a: Value, b: Value) -> Result<Value, Error> {
|
||||
Ok(match a.is_truthy() {
|
||||
|
@ -34,23 +35,23 @@ pub fn nco(a: Value, b: Value) -> Result<Value, Error> {
|
|||
}
|
||||
|
||||
pub fn add(a: Value, b: Value) -> Result<Value, Error> {
|
||||
Ok(a.add(b))
|
||||
a.try_add(b)
|
||||
}
|
||||
|
||||
pub fn sub(a: Value, b: Value) -> Result<Value, Error> {
|
||||
Ok(a.sub(b))
|
||||
a.try_sub(b)
|
||||
}
|
||||
|
||||
pub fn mul(a: Value, b: Value) -> Result<Value, Error> {
|
||||
Ok(a.mul(b))
|
||||
a.try_mul(b)
|
||||
}
|
||||
|
||||
pub fn div(a: Value, b: Value) -> Result<Value, Error> {
|
||||
Ok(a.div(b))
|
||||
a.try_div(b)
|
||||
}
|
||||
|
||||
pub fn pow(a: Value, b: Value) -> Result<Value, Error> {
|
||||
Ok(a.pow(b))
|
||||
a.try_pow(b)
|
||||
}
|
||||
|
||||
pub fn exact(a: &Value, b: &Value) -> Result<Value, Error> {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#[allow(clippy::module_inception)]
|
||||
pub mod duration {
|
||||
|
||||
use crate::sql::duration;
|
||||
use crate::sql::value::Value;
|
||||
use js::Rest;
|
||||
|
||||
|
@ -13,31 +14,40 @@ pub mod duration {
|
|||
#[quickjs(cloneable)]
|
||||
pub struct Duration {
|
||||
#[quickjs(hide)]
|
||||
pub(crate) value: String,
|
||||
pub(crate) value: Option<duration::Duration>,
|
||||
}
|
||||
|
||||
impl Duration {
|
||||
#[quickjs(constructor)]
|
||||
pub fn new(value: String, args: Rest<Value>) -> Self {
|
||||
Self {
|
||||
value,
|
||||
value: duration::Duration::try_from(value).ok(),
|
||||
}
|
||||
}
|
||||
#[quickjs(get)]
|
||||
pub fn value(&self) -> &str {
|
||||
&self.value
|
||||
pub fn value(&self) -> String {
|
||||
match &self.value {
|
||||
Some(v) => v.to_raw(),
|
||||
None => String::from("Invalid Duration"),
|
||||
}
|
||||
}
|
||||
// Compare two Duration instances
|
||||
pub fn is(a: &Duration, b: &Duration, args: Rest<Value>) -> bool {
|
||||
a.value == b.value
|
||||
a.value.is_some() && b.value.is_some() && a.value == b.value
|
||||
}
|
||||
/// Convert the object to a string
|
||||
pub fn toString(&self, args: Rest<Value>) -> String {
|
||||
self.value.to_owned()
|
||||
match &self.value {
|
||||
Some(v) => v.to_raw(),
|
||||
None => String::from("Invalid Duration"),
|
||||
}
|
||||
}
|
||||
/// Convert the object to JSON
|
||||
pub fn toJSON(&self, args: Rest<Value>) -> String {
|
||||
self.value.to_owned()
|
||||
match &self.value {
|
||||
Some(v) => v.to_raw(),
|
||||
None => String::from("Invalid Duration"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#[allow(clippy::module_inception)]
|
||||
pub mod record {
|
||||
|
||||
use crate::sql::thing;
|
||||
use crate::sql::value::Value;
|
||||
use js::Rest;
|
||||
|
||||
|
@ -13,38 +14,43 @@ pub mod record {
|
|||
#[quickjs(cloneable)]
|
||||
pub struct Record {
|
||||
#[quickjs(hide)]
|
||||
pub(crate) tb: String,
|
||||
#[quickjs(hide)]
|
||||
pub(crate) id: String,
|
||||
pub(crate) value: thing::Thing,
|
||||
}
|
||||
|
||||
impl Record {
|
||||
#[quickjs(constructor)]
|
||||
pub fn new(tb: String, id: String, args: Rest<Value>) -> Self {
|
||||
pub fn new(tb: String, id: Value, args: Rest<Value>) -> Self {
|
||||
Self {
|
||||
tb,
|
||||
id,
|
||||
value: thing::Thing {
|
||||
tb,
|
||||
id: match id {
|
||||
Value::Array(v) => v.into(),
|
||||
Value::Object(v) => v.into(),
|
||||
Value::Number(v) => v.into(),
|
||||
v => v.as_string().into(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
#[quickjs(get)]
|
||||
pub fn tb(&self) -> &str {
|
||||
&self.tb
|
||||
&self.value.tb
|
||||
}
|
||||
#[quickjs(get)]
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
pub fn id(&self) -> String {
|
||||
self.value.id.to_raw()
|
||||
}
|
||||
// Compare two Record instances
|
||||
pub fn is(a: &Record, b: &Record, args: Rest<Value>) -> bool {
|
||||
a.tb == b.tb && a.id == b.id
|
||||
a.value == b.value
|
||||
}
|
||||
/// Convert the object to a string
|
||||
pub fn toString(&self, args: Rest<Value>) -> String {
|
||||
format!("{}:{}", self.tb, self.id)
|
||||
self.value.to_raw()
|
||||
}
|
||||
/// Convert the object to JSON
|
||||
pub fn toJSON(&self, args: Rest<Value>) -> String {
|
||||
format!("{}:{}", self.tb, self.id)
|
||||
self.value.to_raw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#[allow(clippy::module_inception)]
|
||||
pub mod uuid {
|
||||
|
||||
use crate::sql::uuid;
|
||||
use crate::sql::value::Value;
|
||||
use js::Rest;
|
||||
|
||||
|
@ -13,31 +14,40 @@ pub mod uuid {
|
|||
#[quickjs(cloneable)]
|
||||
pub struct Uuid {
|
||||
#[quickjs(hide)]
|
||||
pub(crate) value: String,
|
||||
pub(crate) value: Option<uuid::Uuid>,
|
||||
}
|
||||
|
||||
impl Uuid {
|
||||
#[quickjs(constructor)]
|
||||
pub fn new(value: String, args: Rest<Value>) -> Self {
|
||||
Self {
|
||||
value,
|
||||
value: uuid::Uuid::try_from(value).ok(),
|
||||
}
|
||||
}
|
||||
#[quickjs(get)]
|
||||
pub fn value(&self) -> &str {
|
||||
&self.value
|
||||
pub fn value(&self) -> String {
|
||||
match &self.value {
|
||||
Some(v) => v.to_raw(),
|
||||
None => String::from("Invalid Uuid"),
|
||||
}
|
||||
}
|
||||
// Compare two Uuid instances
|
||||
pub fn is(a: &Uuid, b: &Uuid, args: Rest<Value>) -> bool {
|
||||
a.value == b.value
|
||||
a.value.is_some() && b.value.is_some() && a.value == b.value
|
||||
}
|
||||
/// Convert the object to a string
|
||||
pub fn toString(&self, args: Rest<Value>) -> String {
|
||||
self.value.to_owned()
|
||||
match &self.value {
|
||||
Some(v) => v.to_raw(),
|
||||
None => String::from("Invalid Uuid"),
|
||||
}
|
||||
}
|
||||
/// Convert the object to JSON
|
||||
pub fn toJSON(&self, args: Rest<Value>) -> String {
|
||||
self.value.to_owned()
|
||||
match &self.value {
|
||||
Some(v) => v.to_raw(),
|
||||
None => String::from("Invalid Uuid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use super::classes;
|
||||
use crate::sql::array::Array;
|
||||
use crate::sql::datetime::Datetime;
|
||||
use crate::sql::duration::Duration;
|
||||
use crate::sql::object::Object;
|
||||
use crate::sql::thing::Thing;
|
||||
use crate::sql::uuid::Uuid;
|
||||
use crate::sql::value::Value;
|
||||
use chrono::{TimeZone, Utc};
|
||||
use js::Ctx;
|
||||
|
@ -47,26 +44,29 @@ impl<'js> FromJs<'js> for Value {
|
|||
stack: String::new(),
|
||||
});
|
||||
}
|
||||
// Check to see if this object is a duration
|
||||
if (v).instance_of::<classes::duration::duration::Duration>() {
|
||||
let v = v.into_instance::<classes::duration::duration::Duration>().unwrap();
|
||||
let v: &classes::duration::duration::Duration = v.as_ref();
|
||||
let v = v.value.clone();
|
||||
return Ok(Duration::from(v).into());
|
||||
}
|
||||
// Check to see if this object is a record
|
||||
if (v).instance_of::<classes::record::record::Record>() {
|
||||
let v = v.into_instance::<classes::record::record::Record>().unwrap();
|
||||
let v: &classes::record::record::Record = v.as_ref();
|
||||
let v = (v.tb.clone(), v.id.clone());
|
||||
return Ok(Thing::from(v).into());
|
||||
return Ok(v.value.clone().into());
|
||||
}
|
||||
// Check to see if this object is a duration
|
||||
if (v).instance_of::<classes::duration::duration::Duration>() {
|
||||
let v = v.into_instance::<classes::duration::duration::Duration>().unwrap();
|
||||
let v: &classes::duration::duration::Duration = v.as_ref();
|
||||
return match &v.value {
|
||||
Some(v) => Ok(v.clone().into()),
|
||||
None => Ok(Value::None),
|
||||
};
|
||||
}
|
||||
// Check to see if this object is a uuid
|
||||
if (v).instance_of::<classes::uuid::uuid::Uuid>() {
|
||||
let v = v.into_instance::<classes::uuid::uuid::Uuid>().unwrap();
|
||||
let v: &classes::uuid::uuid::Uuid = v.as_ref();
|
||||
let v = v.value.clone();
|
||||
return Ok(Uuid::from(v).into());
|
||||
return match &v.value {
|
||||
Some(v) => Ok(v.clone().into()),
|
||||
None => Ok(Value::None),
|
||||
};
|
||||
}
|
||||
// Check to see if this object is a date
|
||||
let date: js::Object = ctx.globals().get("Date")?;
|
||||
|
|
|
@ -35,25 +35,24 @@ impl<'js> IntoJs<'js> for &Value {
|
|||
let date: js::Function = ctx.globals().get("Date")?;
|
||||
date.construct((v.0.timestamp_millis(),))
|
||||
}
|
||||
Value::Duration(v) => Ok(Class::<classes::duration::duration::Duration>::instance(
|
||||
ctx,
|
||||
classes::duration::duration::Duration {
|
||||
value: v.to_raw(),
|
||||
},
|
||||
)?
|
||||
.into_value()),
|
||||
Value::Thing(v) => Ok(Class::<classes::record::record::Record>::instance(
|
||||
ctx,
|
||||
classes::record::record::Record {
|
||||
tb: v.tb.to_owned(),
|
||||
id: v.id.to_raw(),
|
||||
value: v.to_owned(),
|
||||
},
|
||||
)?
|
||||
.into_value()),
|
||||
Value::Duration(v) => Ok(Class::<classes::duration::duration::Duration>::instance(
|
||||
ctx,
|
||||
classes::duration::duration::Duration {
|
||||
value: Some(v.to_owned()),
|
||||
},
|
||||
)?
|
||||
.into_value()),
|
||||
Value::Uuid(v) => Ok(Class::<classes::uuid::uuid::Uuid>::instance(
|
||||
ctx,
|
||||
classes::uuid::uuid::Uuid {
|
||||
value: v.to_raw(),
|
||||
value: Some(v.to_owned()),
|
||||
},
|
||||
)?
|
||||
.into_value()),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::datetime::Datetime;
|
||||
use crate::sql::duration::Duration;
|
||||
use crate::sql::value::Value;
|
||||
use chrono::offset::TimeZone;
|
||||
use chrono::Datelike;
|
||||
|
@ -8,187 +9,234 @@ use chrono::Local;
|
|||
use chrono::Timelike;
|
||||
use chrono::Utc;
|
||||
|
||||
pub fn day((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.day().into())
|
||||
}
|
||||
|
||||
pub fn floor((datetime, duration): (Value, Value)) -> Result<Value, Error> {
|
||||
Ok(match (datetime, duration) {
|
||||
(Value::Datetime(v), Value::Duration(w)) => match chrono::Duration::from_std(*w) {
|
||||
Ok(d) => match v.duration_trunc(d) {
|
||||
Ok(v) => v.into(),
|
||||
_ => Value::None,
|
||||
},
|
||||
_ => Value::None,
|
||||
},
|
||||
_ => Value::None,
|
||||
pub fn day((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.day().into(),
|
||||
None => Datetime::default().day().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format((datetime, format): (Value, String)) -> Result<Value, Error> {
|
||||
Ok(match datetime {
|
||||
Value::Datetime(v) => v.format(&format).to_string().into(),
|
||||
_ => Value::None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn group((datetime, strand): (Value, Value)) -> Result<Value, Error> {
|
||||
match datetime {
|
||||
Value::Datetime(v) => match strand {
|
||||
Value::Strand(g) => match g.as_str() {
|
||||
"year" => Ok(Utc
|
||||
.with_ymd_and_hms(v.year(), 1, 1, 0,0,0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"month" => Ok(Utc
|
||||
.with_ymd_and_hms(v.year(), v.month(), 1, 0,0,0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"day" => Ok(Utc
|
||||
.with_ymd_and_hms(v.year(), v.month(), v.day(), 0,0,0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"hour" => Ok(Utc
|
||||
.with_ymd_and_hms(v.year(), v.month(), v.day(), v.hour(),0,0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"minute" => Ok(Utc
|
||||
.with_ymd_and_hms(v.year(), v.month(), v.day(), v.hour(),v.minute(),0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"second" => Ok(Utc
|
||||
.with_ymd_and_hms(v.year(), v.month(), v.day(), v.hour(), v.minute(), v.second())
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
_ => Err(Error::InvalidArguments {
|
||||
name: String::from("time::group"),
|
||||
message: String::from("The second argument must be a string, and can be one of 'year', 'month', 'day', 'hour', 'minute', or 'second'."),
|
||||
}),
|
||||
},
|
||||
_ => Ok(Value::None),
|
||||
pub fn floor((val, duration): (Datetime, Duration)) -> Result<Value, Error> {
|
||||
match chrono::Duration::from_std(*duration) {
|
||||
Ok(d) => match val.duration_trunc(d) {
|
||||
Ok(v) => Ok(v.into()),
|
||||
_ => Err(Error::InvalidArguments {
|
||||
name: String::from("time::floor"),
|
||||
message: String::from("The second argument must be a duration, and must be able to be represented as nanoseconds."),
|
||||
}),
|
||||
},
|
||||
_ => Ok(Value::None),
|
||||
_ => Err(Error::InvalidArguments {
|
||||
name: String::from("time::floor"),
|
||||
message: String::from("The second argument must be a duration, and must be able to be represented as nanoseconds."),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hour((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.hour().into())
|
||||
pub fn format((val, format): (Datetime, String)) -> Result<Value, Error> {
|
||||
Ok(val.format(&format).to_string().into())
|
||||
}
|
||||
|
||||
pub fn minute((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.minute().into())
|
||||
pub fn group((val, group): (Datetime, String)) -> Result<Value, Error> {
|
||||
match group.as_str() {
|
||||
"year" => Ok(Utc
|
||||
.with_ymd_and_hms(val.year(), 1, 1, 0,0,0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"month" => Ok(Utc
|
||||
.with_ymd_and_hms(val.year(), val.month(), 1, 0,0,0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"day" => Ok(Utc
|
||||
.with_ymd_and_hms(val.year(), val.month(), val.day(), 0,0,0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"hour" => Ok(Utc
|
||||
.with_ymd_and_hms(val.year(), val.month(), val.day(), val.hour(),0,0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"minute" => Ok(Utc
|
||||
.with_ymd_and_hms(val.year(), val.month(), val.day(), val.hour(), val.minute(),0)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
"second" => Ok(Utc
|
||||
.with_ymd_and_hms(val.year(), val.month(), val.day(), val.hour(), val.minute(), val.second())
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.into()),
|
||||
_ => Err(Error::InvalidArguments {
|
||||
name: String::from("time::group"),
|
||||
message: String::from("The second argument must be a string, and can be one of 'year', 'month', 'day', 'hour', 'minute', or 'second'."),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn month((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.month().into())
|
||||
pub fn hour((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.hour().into(),
|
||||
None => Datetime::default().hour().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn nano((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.timestamp_nanos().into())
|
||||
pub fn minute((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.minute().into(),
|
||||
None => Datetime::default().minute().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn month((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.minute().into(),
|
||||
None => Datetime::default().minute().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn nano((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.timestamp_nanos().into(),
|
||||
None => Datetime::default().timestamp_nanos().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn now(_: ()) -> Result<Value, Error> {
|
||||
Ok(Datetime::default().into())
|
||||
}
|
||||
|
||||
pub fn round((datetime, duration): (Value, Value)) -> Result<Value, Error> {
|
||||
Ok(match (datetime, duration) {
|
||||
(Value::Datetime(v), Value::Duration(w)) => match chrono::Duration::from_std(*w) {
|
||||
Ok(d) => match v.duration_round(d) {
|
||||
Ok(v) => v.into(),
|
||||
_ => Value::None,
|
||||
},
|
||||
_ => Value::None,
|
||||
pub fn round((val, duration): (Datetime, Duration)) -> Result<Value, Error> {
|
||||
match chrono::Duration::from_std(*duration) {
|
||||
Ok(d) => match val.duration_round(d) {
|
||||
Ok(v) => Ok(v.into()),
|
||||
_ => Err(Error::InvalidArguments {
|
||||
name: String::from("time::round"),
|
||||
message: String::from("The second argument must be a duration, and must be able to be represented as nanoseconds."),
|
||||
}),
|
||||
},
|
||||
_ => Value::None,
|
||||
})
|
||||
_ => Err(Error::InvalidArguments {
|
||||
name: String::from("time::round"),
|
||||
message: String::from("The second argument must be a duration, and must be able to be represented as nanoseconds."),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn second((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.second().into())
|
||||
pub fn second((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.second().into(),
|
||||
None => Datetime::default().second().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn timezone(_: ()) -> Result<Value, Error> {
|
||||
Ok(Local::now().offset().to_string().into())
|
||||
}
|
||||
|
||||
pub fn unix((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.timestamp().into())
|
||||
pub fn unix((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.timestamp().into(),
|
||||
None => Datetime::default().timestamp().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn wday((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.weekday().number_from_monday().into())
|
||||
pub fn wday((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.weekday().number_from_monday().into(),
|
||||
None => Datetime::default().weekday().number_from_monday().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn week((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.iso_week().week().into())
|
||||
pub fn week((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.iso_week().week().into(),
|
||||
None => Datetime::default().iso_week().week().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn yday((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.ordinal().into())
|
||||
pub fn yday((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.ordinal().into(),
|
||||
None => Datetime::default().ordinal().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn year((datetime,): (Option<Value>,)) -> Result<Value, Error> {
|
||||
let date = match datetime {
|
||||
Some(Value::Datetime(v)) => v,
|
||||
None => Datetime::default(),
|
||||
Some(_) => return Ok(Value::None),
|
||||
};
|
||||
Ok(date.year().into())
|
||||
pub fn year((val,): (Option<Datetime>,)) -> Result<Value, Error> {
|
||||
Ok(match val {
|
||||
Some(v) => v.year().into(),
|
||||
None => Datetime::default().year().into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub mod from {
|
||||
|
||||
use crate::err::Error;
|
||||
use crate::sql::datetime::Datetime;
|
||||
use crate::sql::value::Value;
|
||||
use chrono::{NaiveDateTime, Offset, TimeZone, Utc};
|
||||
|
||||
pub fn micros((val,): (i64,)) -> Result<Value, Error> {
|
||||
match NaiveDateTime::from_timestamp_micros(val) {
|
||||
Some(v) => match Utc.fix().from_local_datetime(&v).earliest() {
|
||||
Some(v) => Ok(Datetime::from(v.with_timezone(&Utc)).into()),
|
||||
None => Err(Error::InvalidArguments {
|
||||
name: String::from("time::from::micros"),
|
||||
message: String::from("The first argument must be an in-bounds number of microseconds relative to January 1, 1970 0:00:00 UTC."),
|
||||
}),
|
||||
}
|
||||
None => Err(Error::InvalidArguments {
|
||||
name: String::from("time::from::micros"),
|
||||
message: String::from("The first argument must be an in-bounds number of microseconds relative to January 1, 1970 0:00:00 UTC."),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn millis((val,): (i64,)) -> Result<Value, Error> {
|
||||
match NaiveDateTime::from_timestamp_millis(val) {
|
||||
Some(v) => match Utc.fix().from_local_datetime(&v).earliest() {
|
||||
Some(v) => Ok(Datetime::from(v.with_timezone(&Utc)).into()),
|
||||
None => Err(Error::InvalidArguments {
|
||||
name: String::from("time::from::millis"),
|
||||
message: String::from("The first argument must be an in-bounds number of milliseconds relative to January 1, 1970 0:00:00 UTC."),
|
||||
}),
|
||||
}
|
||||
None => Err(Error::InvalidArguments {
|
||||
name: String::from("time::from::millis"),
|
||||
message: String::from("The first argument must be an in-bounds number of milliseconds relative to January 1, 1970 0:00:00 UTC."),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn secs((val,): (i64,)) -> Result<Value, Error> {
|
||||
match NaiveDateTime::from_timestamp_opt(val, 0) {
|
||||
Some(v) => match Utc.fix().from_local_datetime(&v).earliest() {
|
||||
Some(v) => Ok(Datetime::from(v.with_timezone(&Utc)).into()),
|
||||
None => Err(Error::InvalidArguments {
|
||||
name: String::from("time::from::secs"),
|
||||
message: String::from("The first argument must be an in-bounds number of seconds relative to January 1, 1970 0:00:00 UTC."),
|
||||
}),
|
||||
}
|
||||
None => Err(Error::InvalidArguments {
|
||||
name: String::from("time::from::secs"),
|
||||
message: String::from("The first argument must be an in-bounds number of seconds relative to January 1, 1970 0:00:00 UTC."),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unix((val,): (i64,)) -> Result<Value, Error> {
|
||||
match NaiveDateTime::from_timestamp_opt(val, 0) {
|
||||
Some(v) => match Utc.fix().from_local_datetime(&v).earliest() {
|
||||
Some(v) => Ok(Datetime::from(v.with_timezone(&Utc)).into()),
|
||||
None => Err(Error::InvalidArguments {
|
||||
name: String::from("time::from::unix"),
|
||||
message: String::from("The first argument must be an in-bounds number of seconds relative to January 1, 1970 0:00:00 UTC."),
|
||||
}),
|
||||
}
|
||||
None => Err(Error::InvalidArguments {
|
||||
name: String::from("time::from::unix"),
|
||||
message: String::from("The first argument must be an in-bounds number of seconds relative to January 1, 1970 0:00:00 UTC."),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,83 +1,46 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::geometry::Geometry;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::table::Table;
|
||||
use crate::sql::thing::Thing;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::Strand;
|
||||
|
||||
pub fn bool((arg,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(arg.is_truthy().into())
|
||||
pub fn bool((val,): (Value,)) -> Result<Value, Error> {
|
||||
val.convert_to_bool().map(Value::from)
|
||||
}
|
||||
|
||||
pub fn datetime((arg,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match arg {
|
||||
Value::Datetime(_) => arg,
|
||||
_ => Value::Datetime(arg.as_datetime()),
|
||||
})
|
||||
pub fn datetime((val,): (Value,)) -> Result<Value, Error> {
|
||||
val.convert_to_datetime().map(Value::from)
|
||||
}
|
||||
|
||||
pub fn decimal((arg,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match arg {
|
||||
Value::Number(Number::Decimal(_)) => arg,
|
||||
_ => Value::Number(Number::Decimal(arg.as_decimal())),
|
||||
})
|
||||
pub fn decimal((val,): (Value,)) -> Result<Value, Error> {
|
||||
val.convert_to_decimal().map(Value::from)
|
||||
}
|
||||
|
||||
pub fn duration((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Duration(_) => Ok(arg),
|
||||
_ => Ok(Value::Duration(arg.as_duration())),
|
||||
}
|
||||
pub fn duration((val,): (Value,)) -> Result<Value, Error> {
|
||||
val.convert_to_duration().map(Value::from)
|
||||
}
|
||||
|
||||
pub fn float((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Number(Number::Float(_)) => Ok(arg),
|
||||
_ => Ok(Value::Number(Number::Float(arg.as_float()))),
|
||||
}
|
||||
pub fn float((val,): (Value,)) -> Result<Value, Error> {
|
||||
val.convert_to_float().map(Value::from)
|
||||
}
|
||||
|
||||
pub fn int((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Number(Number::Int(_)) => Ok(arg),
|
||||
_ => Ok(Value::Number(Number::Int(arg.as_int()))),
|
||||
}
|
||||
pub fn int((val,): (Value,)) -> Result<Value, Error> {
|
||||
val.convert_to_int().map(Value::from)
|
||||
}
|
||||
|
||||
pub fn number((arg,): (Value,)) -> Result<Value, Error> {
|
||||
match arg {
|
||||
Value::Number(_) => Ok(arg),
|
||||
_ => Ok(Value::Number(arg.as_number())),
|
||||
}
|
||||
pub fn number((val,): (Value,)) -> Result<Value, Error> {
|
||||
val.convert_to_number().map(Value::from)
|
||||
}
|
||||
|
||||
pub fn point((arg1, arg2): (Value, Option<Value>)) -> Result<Value, Error> {
|
||||
Ok(if let Some(y) = arg2 {
|
||||
let x = arg1;
|
||||
(x.as_float(), y.as_float()).into()
|
||||
} else {
|
||||
match arg1 {
|
||||
Value::Array(v) if v.len() == 2 => v.as_point().into(),
|
||||
Value::Geometry(Geometry::Point(v)) => v.into(),
|
||||
_ => Value::None,
|
||||
}
|
||||
})
|
||||
pub fn point((val,): (Value,)) -> Result<Value, Error> {
|
||||
val.convert_to_point().map(Value::from)
|
||||
}
|
||||
|
||||
pub fn regex((arg,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match arg {
|
||||
Value::Strand(v) => v.parse().map(Value::Regex).unwrap_or(Value::None),
|
||||
_ => Value::None,
|
||||
})
|
||||
pub fn string((val,): (Value,)) -> Result<Value, Error> {
|
||||
val.convert_to_strand().map(Value::from)
|
||||
}
|
||||
|
||||
pub fn string((arg,): (Strand,)) -> Result<Value, Error> {
|
||||
Ok(arg.into())
|
||||
}
|
||||
|
||||
pub fn table((arg,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(Value::Table(Table(match arg {
|
||||
pub fn table((val,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(Value::Table(Table(match val {
|
||||
Value::Thing(t) => t.tb,
|
||||
v => v.as_string(),
|
||||
})))
|
||||
|
@ -91,7 +54,7 @@ pub fn thing((arg1, arg2): (Value, Option<Value>)) -> Result<Value, Error> {
|
|||
Value::Thing(v) => v.id,
|
||||
Value::Array(v) => v.into(),
|
||||
Value::Object(v) => v.into(),
|
||||
Value::Number(Number::Int(v)) => v.into(),
|
||||
Value::Number(v) => v.into(),
|
||||
v => v.as_string().into(),
|
||||
},
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ pub async fn head(ctx: &Context<'_>, uri: Strand, opts: impl Into<Object>) -> Re
|
|||
}
|
||||
// Add specified header values
|
||||
for (k, v) in opts.into().iter() {
|
||||
req = req.header(k.as_str(), v.to_strand().as_str());
|
||||
req = req.header(k.as_str(), v.to_raw_string());
|
||||
}
|
||||
// Send the request and wait
|
||||
let res = match ctx.timeout() {
|
||||
|
@ -47,7 +47,7 @@ pub async fn get(ctx: &Context<'_>, uri: Strand, opts: impl Into<Object>) -> Res
|
|||
}
|
||||
// Add specified header values
|
||||
for (k, v) in opts.into().iter() {
|
||||
req = req.header(k.as_str(), v.to_strand().as_str());
|
||||
req = req.header(k.as_str(), v.to_raw_string());
|
||||
}
|
||||
// Send the request and wait
|
||||
let res = match ctx.timeout() {
|
||||
|
@ -92,7 +92,7 @@ pub async fn put(
|
|||
}
|
||||
// Add specified header values
|
||||
for (k, v) in opts.into().iter() {
|
||||
req = req.header(k.as_str(), v.to_strand().as_str());
|
||||
req = req.header(k.as_str(), v.to_raw_string());
|
||||
}
|
||||
// Submit the request body
|
||||
if body.is_some() {
|
||||
|
@ -141,7 +141,7 @@ pub async fn post(
|
|||
}
|
||||
// Add specified header values
|
||||
for (k, v) in opts.into().iter() {
|
||||
req = req.header(k.as_str(), v.to_strand().as_str());
|
||||
req = req.header(k.as_str(), v.to_raw_string());
|
||||
}
|
||||
// Submit the request body
|
||||
if body.is_some() {
|
||||
|
@ -190,7 +190,7 @@ pub async fn patch(
|
|||
}
|
||||
// Add specified header values
|
||||
for (k, v) in opts.into().iter() {
|
||||
req = req.header(k.as_str(), v.to_strand().as_str());
|
||||
req = req.header(k.as_str(), v.to_raw_string());
|
||||
}
|
||||
// Submit the request body
|
||||
if body.is_some() {
|
||||
|
@ -238,7 +238,7 @@ pub async fn delete(
|
|||
}
|
||||
// Add specified header values
|
||||
for (k, v) in opts.into().iter() {
|
||||
req = req.header(k.as_str(), v.to_strand().as_str());
|
||||
req = req.header(k.as_str(), v.to_raw_string());
|
||||
}
|
||||
// Send the request and wait
|
||||
let res = match ctx.timeout() {
|
||||
|
|
|
@ -44,7 +44,7 @@ mod tests {
|
|||
let val = Lq::new(
|
||||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
"00000000-0000-0000-0000-000000000000".into(),
|
||||
Uuid::default(),
|
||||
);
|
||||
let enc = Lq::encode(&val).unwrap();
|
||||
let dec = Lq::decode(&enc).unwrap();
|
||||
|
|
|
@ -61,7 +61,7 @@ mod tests {
|
|||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
"00000000-0000-0000-0000-000000000000".into(),
|
||||
Uuid::default(),
|
||||
);
|
||||
let enc = Lv::encode(&val).unwrap();
|
||||
let dec = Lv::decode(&enc).unwrap();
|
||||
|
|
|
@ -9,7 +9,6 @@ use crate::sql::fmt::{pretty_indent, Fmt, Pretty};
|
|||
use crate::sql::number::Number;
|
||||
use crate::sql::operation::Operation;
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use crate::sql::strand::Strand;
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::character::complete::char;
|
||||
use nom::combinator::opt;
|
||||
|
@ -75,6 +74,16 @@ impl From<Array> for Vec<Value> {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Value> for Array {
|
||||
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
|
||||
let mut a: Vec<Value> = vec![];
|
||||
for v in iter {
|
||||
a.push(v)
|
||||
}
|
||||
Array(a)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Array {
|
||||
type Target = Vec<Value>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -104,30 +113,6 @@ impl Array {
|
|||
pub fn with_capacity(len: usize) -> Self {
|
||||
Self(Vec::with_capacity(len))
|
||||
}
|
||||
|
||||
pub fn as_ints(self) -> Vec<i64> {
|
||||
self.0.into_iter().map(|v| v.as_int()).collect()
|
||||
}
|
||||
|
||||
pub fn as_floats(self) -> Vec<f64> {
|
||||
self.0.into_iter().map(|v| v.as_float()).collect()
|
||||
}
|
||||
|
||||
pub fn as_numbers(self) -> Vec<Number> {
|
||||
self.0.into_iter().map(|v| v.as_number()).collect()
|
||||
}
|
||||
|
||||
pub fn as_strands(self) -> Vec<Strand> {
|
||||
self.0.into_iter().map(|v| v.as_strand()).collect()
|
||||
}
|
||||
|
||||
pub fn as_point(mut self) -> [f64; 2] {
|
||||
match self.len() {
|
||||
0 => [0.0, 0.0],
|
||||
1 => [self.0.remove(0).as_float(), 0.0],
|
||||
_ => [self.0.remove(0).as_float(), self.0.remove(0).as_float()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Array {
|
||||
|
@ -247,7 +232,7 @@ impl<T> Abolish<T> for Vec<T> {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Combine<T> {
|
||||
pub(crate) trait Combine<T> {
|
||||
fn combine(self, other: T) -> T;
|
||||
}
|
||||
|
||||
|
@ -265,7 +250,7 @@ impl Combine<Array> for Array {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Complement<T> {
|
||||
pub(crate) trait Complement<T> {
|
||||
fn complement(self, other: T) -> T;
|
||||
}
|
||||
|
||||
|
@ -283,7 +268,7 @@ impl Complement<Array> for Array {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Concat<T> {
|
||||
pub(crate) trait Concat<T> {
|
||||
fn concat(self, other: T) -> T;
|
||||
}
|
||||
|
||||
|
@ -296,7 +281,7 @@ impl Concat<Array> for Array {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Difference<T> {
|
||||
pub(crate) trait Difference<T> {
|
||||
fn difference(self, other: T) -> T;
|
||||
}
|
||||
|
||||
|
@ -317,7 +302,7 @@ impl Difference<Array> for Array {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Flatten<T> {
|
||||
pub(crate) trait Flatten<T> {
|
||||
fn flatten(self) -> T;
|
||||
}
|
||||
|
||||
|
@ -336,7 +321,7 @@ impl Flatten<Array> for Array {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Intersect<T> {
|
||||
pub(crate) trait Intersect<T> {
|
||||
fn intersect(self, other: T) -> T;
|
||||
}
|
||||
|
||||
|
@ -355,7 +340,7 @@ impl Intersect<Self> for Array {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Union<T> {
|
||||
pub(crate) trait Union<T> {
|
||||
fn union(self, other: T) -> T;
|
||||
}
|
||||
|
||||
|
@ -368,7 +353,7 @@ impl Union<Self> for Array {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Uniq<T> {
|
||||
pub(crate) trait Uniq<T> {
|
||||
fn uniq(self) -> T;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,13 @@ pub fn commas(i: &str) -> IResult<&str, ()> {
|
|||
Ok((i, ()))
|
||||
}
|
||||
|
||||
pub fn verbar(i: &str) -> IResult<&str, ()> {
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char('|')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
Ok((i, ()))
|
||||
}
|
||||
|
||||
pub fn commasorspace(i: &str) -> IResult<&str, ()> {
|
||||
alt((commas, shouldbespace))(i)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::sql::duration::Duration;
|
|||
use crate::sql::error::IResult;
|
||||
use crate::sql::escape::escape_str;
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use crate::sql::strand::Strand;
|
||||
use chrono::{DateTime, FixedOffset, Offset, SecondsFormat, TimeZone, Utc};
|
||||
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||
use nom::branch::alt;
|
||||
|
@ -16,6 +17,7 @@ use std::fmt::{self, Display, Formatter};
|
|||
use std::ops;
|
||||
use std::ops::Deref;
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Datetime";
|
||||
|
||||
|
@ -34,11 +36,39 @@ impl From<DateTime<Utc>> for Datetime {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Datetime {
|
||||
fn from(s: &str) -> Self {
|
||||
match datetime_all_raw(s) {
|
||||
Ok((_, v)) => v,
|
||||
Err(_) => Self::default(),
|
||||
impl From<Datetime> for DateTime<Utc> {
|
||||
fn from(x: Datetime) -> Self {
|
||||
x.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Datetime {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Datetime {
|
||||
type Error = ();
|
||||
fn try_from(v: String) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Strand> for Datetime {
|
||||
type Error = ();
|
||||
fn try_from(v: Strand) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Datetime {
|
||||
type Error = ();
|
||||
fn try_from(v: &str) -> Result<Self, Self::Error> {
|
||||
match datetime_all_raw(v) {
|
||||
Ok((_, v)) => Ok(v),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,12 +80,6 @@ impl Deref for Datetime {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Datetime> for DateTime<Utc> {
|
||||
fn from(x: Datetime) -> Self {
|
||||
x.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Datetime {
|
||||
/// Convert the Datetime to a raw String
|
||||
pub fn to_raw(&self) -> String {
|
||||
|
@ -262,8 +286,20 @@ fn sign(i: &str) -> IResult<&str, i32> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
// use chrono::Date;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn date_zone() {
|
||||
let sql = "2020-01-01T00:00:00Z";
|
||||
let res = datetime_all_raw(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'2020-01-01T00:00:00Z'", format!("{}", out));
|
||||
assert_eq!(out, Datetime::try_from("2020-01-01T00:00:00Z").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_time() {
|
||||
let sql = "2012-04-23T18:25:43Z";
|
||||
|
@ -271,6 +307,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'2012-04-23T18:25:43Z'", format!("{}", out));
|
||||
assert_eq!(out, Datetime::try_from("2012-04-23T18:25:43Z").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -280,6 +317,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'2012-04-23T18:25:43.563100Z'", format!("{}", out));
|
||||
assert_eq!(out, Datetime::try_from("2012-04-23T18:25:43.563100Z").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -289,6 +327,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'2012-04-23T18:25:43.000051100Z'", format!("{}", out));
|
||||
assert_eq!(out, Datetime::try_from("2012-04-23T18:25:43.000051100Z").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -298,6 +337,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'2012-04-24T02:25:43.511Z'", format!("{}", out));
|
||||
assert_eq!(out, Datetime::try_from("2012-04-24T02:25:43.511Z").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -307,6 +347,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'2012-04-24T02:55:43.511Z'", format!("{}", out));
|
||||
assert_eq!(out, Datetime::try_from("2012-04-24T02:55:43.511Z").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -316,6 +357,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'2012-04-23T18:25:43.511Z'", format!("{}", out));
|
||||
assert_eq!(out, Datetime::try_from("2012-04-23T18:25:43.511Z").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -325,6 +367,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'2012-04-23T18:25:43.000051100Z'", format!("{}", out));
|
||||
assert_eq!(out, Datetime::try_from("2012-04-23T18:25:43.000051100Z").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::sql::datetime::Datetime;
|
|||
use crate::sql::ending::duration as ending;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::strand::Strand;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::multi::many1;
|
||||
|
@ -12,6 +12,7 @@ use std::fmt;
|
|||
use std::iter::Sum;
|
||||
use std::ops;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::time;
|
||||
|
||||
static SECONDS_PER_YEAR: u64 = 365 * SECONDS_PER_DAY;
|
||||
|
@ -32,27 +33,43 @@ impl From<time::Duration> for Duration {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<String> for Duration {
|
||||
fn from(s: String) -> Self {
|
||||
s.as_str().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Duration {
|
||||
fn from(s: &str) -> Self {
|
||||
match duration(s) {
|
||||
Ok((_, v)) => v,
|
||||
Err(_) => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Duration> for time::Duration {
|
||||
fn from(s: Duration) -> Self {
|
||||
s.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Duration {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Duration {
|
||||
type Error = ();
|
||||
fn try_from(v: String) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Strand> for Duration {
|
||||
type Error = ();
|
||||
fn try_from(v: Strand) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Duration {
|
||||
type Error = ();
|
||||
fn try_from(v: &str) -> Result<Self, Self::Error> {
|
||||
match duration(v) {
|
||||
Ok((_, v)) => Ok(v),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Duration {
|
||||
type Target = time::Duration;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -65,39 +82,73 @@ impl Duration {
|
|||
pub fn to_raw(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
/// Get the total number of nanoseconds
|
||||
pub fn nanos(&self) -> u128 {
|
||||
self.0.as_nanos()
|
||||
}
|
||||
/// Get the total number of microseconds
|
||||
pub fn micros(&self) -> u128 {
|
||||
self.0.as_micros()
|
||||
}
|
||||
/// Get the total number of milliseconds
|
||||
pub fn millis(&self) -> u128 {
|
||||
self.0.as_millis()
|
||||
}
|
||||
/// Get the total number of seconds
|
||||
pub fn secs(&self) -> Value {
|
||||
self.0.as_secs().into()
|
||||
pub fn secs(&self) -> u64 {
|
||||
self.0.as_secs()
|
||||
}
|
||||
/// Get the total number of minutes
|
||||
pub fn mins(&self) -> Value {
|
||||
let secs = self.0.as_secs();
|
||||
let mins = secs / SECONDS_PER_MINUTE;
|
||||
mins.into()
|
||||
pub fn mins(&self) -> u64 {
|
||||
self.0.as_secs() / SECONDS_PER_MINUTE
|
||||
}
|
||||
/// Get the total number of hours
|
||||
pub fn hours(&self) -> Value {
|
||||
let secs = self.0.as_secs();
|
||||
let hours = secs / SECONDS_PER_HOUR;
|
||||
hours.into()
|
||||
pub fn hours(&self) -> u64 {
|
||||
self.0.as_secs() / SECONDS_PER_HOUR
|
||||
}
|
||||
/// Get the total number of dats
|
||||
pub fn days(&self) -> Value {
|
||||
let secs = self.0.as_secs();
|
||||
let days = secs / SECONDS_PER_DAY;
|
||||
days.into()
|
||||
pub fn days(&self) -> u64 {
|
||||
self.0.as_secs() / SECONDS_PER_DAY
|
||||
}
|
||||
/// Get the total number of months
|
||||
pub fn weeks(&self) -> Value {
|
||||
let secs = self.0.as_secs();
|
||||
let weeks = secs / SECONDS_PER_WEEK;
|
||||
weeks.into()
|
||||
pub fn weeks(&self) -> u64 {
|
||||
self.0.as_secs() / SECONDS_PER_WEEK
|
||||
}
|
||||
/// Get the total number of years
|
||||
pub fn years(&self) -> Value {
|
||||
let secs = self.0.as_secs();
|
||||
let years = secs / SECONDS_PER_YEAR;
|
||||
years.into()
|
||||
pub fn years(&self) -> u64 {
|
||||
self.0.as_secs() / SECONDS_PER_YEAR
|
||||
}
|
||||
/// Create a duration from nanoseconds
|
||||
pub fn from_nanos(nanos: u64) -> Duration {
|
||||
time::Duration::from_nanos(nanos).into()
|
||||
}
|
||||
/// Create a duration from microseconds
|
||||
pub fn from_micros(micros: u64) -> Duration {
|
||||
time::Duration::from_micros(micros).into()
|
||||
}
|
||||
/// Create a duration from milliseconds
|
||||
pub fn from_millis(millis: u64) -> Duration {
|
||||
time::Duration::from_millis(millis).into()
|
||||
}
|
||||
/// Create a duration from seconds
|
||||
pub fn from_secs(secs: u64) -> Duration {
|
||||
time::Duration::from_secs(secs).into()
|
||||
}
|
||||
/// Create a duration from minutes
|
||||
pub fn from_mins(mins: u64) -> Duration {
|
||||
time::Duration::from_secs(mins * SECONDS_PER_MINUTE).into()
|
||||
}
|
||||
/// Create a duration from hours
|
||||
pub fn from_hours(hours: u64) -> Duration {
|
||||
time::Duration::from_secs(hours * SECONDS_PER_HOUR).into()
|
||||
}
|
||||
/// Create a duration from days
|
||||
pub fn from_days(days: u64) -> Duration {
|
||||
time::Duration::from_secs(days * SECONDS_PER_DAY).into()
|
||||
}
|
||||
/// Create a duration from weeks
|
||||
pub fn from_weeks(days: u64) -> Duration {
|
||||
time::Duration::from_secs(days * SECONDS_PER_WEEK).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ impl<I: IntoIterator<Item = T>, T: Display> Fmt<I, fn(I, &mut Formatter) -> fmt:
|
|||
Self::new(into_iter, fmt_comma_separated)
|
||||
}
|
||||
|
||||
/// Formats values with a verbar and a space separating them.
|
||||
pub(crate) fn verbar_separated(into_iter: I) -> Self {
|
||||
Self::new(into_iter, fmt_verbar_separated)
|
||||
}
|
||||
|
||||
/// Formats values with a comma and a space separating them or, if pretty printing is in
|
||||
/// effect, a comma, a newline, and indentation.
|
||||
pub(crate) fn pretty_comma_separated(into_iter: I) -> Self {
|
||||
|
@ -48,8 +53,8 @@ impl<I: IntoIterator<Item = T>, T: Display> Fmt<I, fn(I, &mut Formatter) -> fmt:
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_comma_separated<T: Display>(
|
||||
into_iter: impl IntoIterator<Item = T>,
|
||||
fn fmt_comma_separated<T: Display, I: IntoIterator<Item = T>>(
|
||||
into_iter: I,
|
||||
f: &mut Formatter,
|
||||
) -> fmt::Result {
|
||||
for (i, v) in into_iter.into_iter().enumerate() {
|
||||
|
@ -61,8 +66,21 @@ fn fmt_comma_separated<T: Display>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_pretty_comma_separated<T: Display>(
|
||||
into_iter: impl IntoIterator<Item = T>,
|
||||
fn fmt_verbar_separated<T: Display, I: IntoIterator<Item = T>>(
|
||||
into_iter: I,
|
||||
f: &mut Formatter,
|
||||
) -> fmt::Result {
|
||||
for (i, v) in into_iter.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
f.write_str(" | ")?;
|
||||
}
|
||||
Display::fmt(&v, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_pretty_comma_separated<T: Display, I: IntoIterator<Item = T>>(
|
||||
into_iter: I,
|
||||
f: &mut Formatter,
|
||||
) -> fmt::Result {
|
||||
for (i, v) in into_iter.into_iter().enumerate() {
|
||||
|
@ -79,8 +97,8 @@ fn fmt_pretty_comma_separated<T: Display>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_one_line_separated<T: Display>(
|
||||
into_iter: impl IntoIterator<Item = T>,
|
||||
fn fmt_one_line_separated<T: Display, I: IntoIterator<Item = T>>(
|
||||
into_iter: I,
|
||||
f: &mut Formatter,
|
||||
) -> fmt::Result {
|
||||
for (i, v) in into_iter.into_iter().enumerate() {
|
||||
|
@ -96,8 +114,8 @@ fn fmt_one_line_separated<T: Display>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_two_line_separated<T: Display>(
|
||||
into_iter: impl IntoIterator<Item = T>,
|
||||
fn fmt_two_line_separated<T: Display, I: IntoIterator<Item = T>>(
|
||||
into_iter: I,
|
||||
f: &mut Formatter,
|
||||
) -> fmt::Result {
|
||||
for (i, v) in into_iter.into_iter().enumerate() {
|
||||
|
@ -116,13 +134,12 @@ fn fmt_two_line_separated<T: Display>(
|
|||
}
|
||||
|
||||
/// Creates a formatting function that joins iterators with an arbitrary separator.
|
||||
pub fn fmt_separated_by<T: Display, II: IntoIterator<Item = T>>(
|
||||
pub fn fmt_separated_by<T: Display, I: IntoIterator<Item = T>>(
|
||||
separator: impl Display,
|
||||
) -> impl Fn(II, &mut Formatter) -> fmt::Result {
|
||||
move |into_iter: II, f: &mut Formatter| {
|
||||
) -> impl Fn(I, &mut Formatter) -> fmt::Result {
|
||||
move |into_iter: I, f: &mut Formatter| {
|
||||
for (i, v) in into_iter.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
// This separator goes after the item formatted in the last iteration.
|
||||
Display::fmt(&separator, f)?;
|
||||
}
|
||||
Display::fmt(&v, f)?;
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::sql::common::val_char;
|
|||
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::serde::is_internal_serialization;
|
||||
use crate::sql::value::{single, value, Value};
|
||||
|
@ -32,7 +33,7 @@ pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Function";
|
|||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Hash)]
|
||||
pub enum Function {
|
||||
Cast(String, Value),
|
||||
Cast(Kind, Value),
|
||||
Normal(String, Vec<Value>),
|
||||
Custom(String, Vec<Value>),
|
||||
Script(Script, Vec<Value>),
|
||||
|
@ -147,11 +148,11 @@ impl Function {
|
|||
let opt = &opt.futures(true);
|
||||
// Process the function type
|
||||
match self {
|
||||
Self::Cast(s, x) => {
|
||||
Self::Cast(k, x) => {
|
||||
// Compute the value to be cast
|
||||
let a = x.compute(ctx, opt, txn, doc).await?;
|
||||
// Run the cast function
|
||||
fnc::cast::run(ctx, s, a)
|
||||
a.convert_to(k)
|
||||
}
|
||||
Self::Normal(s, x) => {
|
||||
// Compute the function arguments
|
||||
|
@ -185,14 +186,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(),
|
||||
match val {
|
||||
Value::None => val,
|
||||
Value::Null => val,
|
||||
_ => val.convert_to(&kind),
|
||||
},
|
||||
);
|
||||
ctx.add_value(name.to_raw(), val.convert_to(&kind)?);
|
||||
}
|
||||
// Run the custom function
|
||||
val.block.compute(&ctx, opt, txn, doc).await
|
||||
|
@ -220,7 +214,7 @@ impl Function {
|
|||
impl fmt::Display for Function {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Cast(s, e) => write!(f, "<{s}> {e}"),
|
||||
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)),
|
||||
|
@ -309,23 +303,10 @@ fn script(i: &str) -> IResult<&str, Function> {
|
|||
}
|
||||
|
||||
fn cast(i: &str) -> IResult<&str, Function> {
|
||||
let (i, s) = delimited(
|
||||
char('<'),
|
||||
alt((
|
||||
tag("bool"),
|
||||
tag("datetime"),
|
||||
tag("decimal"),
|
||||
tag("duration"),
|
||||
tag("float"),
|
||||
tag("int"),
|
||||
tag("number"),
|
||||
tag("string"),
|
||||
)),
|
||||
char('>'),
|
||||
)(i)?;
|
||||
let (i, k) = delimited(char('<'), kind, char('>'))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = single(i)?;
|
||||
Ok((i, Function::Cast(s.to_string(), v)))
|
||||
Ok((i, Function::Cast(k, v)))
|
||||
}
|
||||
|
||||
fn function_names(i: &str) -> IResult<&str, &str> {
|
||||
|
@ -399,7 +380,30 @@ fn function_crypto(i: &str) -> IResult<&str, &str> {
|
|||
}
|
||||
|
||||
fn function_duration(i: &str) -> IResult<&str, &str> {
|
||||
alt((tag("days"), tag("hours"), tag("mins"), tag("secs"), tag("weeks"), tag("years")))(i)
|
||||
alt((
|
||||
tag("days"),
|
||||
tag("hours"),
|
||||
tag("micros"),
|
||||
tag("millis"),
|
||||
tag("mins"),
|
||||
tag("nanos"),
|
||||
tag("secs"),
|
||||
tag("weeks"),
|
||||
tag("years"),
|
||||
preceded(
|
||||
tag("from"),
|
||||
alt((
|
||||
tag("days"),
|
||||
tag("hours"),
|
||||
tag("micros"),
|
||||
tag("millis"),
|
||||
tag("mins"),
|
||||
tag("nanos"),
|
||||
tag("secs"),
|
||||
tag("weeks"),
|
||||
)),
|
||||
),
|
||||
))(i)
|
||||
}
|
||||
|
||||
fn function_geo(i: &str) -> IResult<&str, &str> {
|
||||
|
@ -557,6 +561,7 @@ fn function_time(i: &str) -> IResult<&str, &str> {
|
|||
tag("week"),
|
||||
tag("yday"),
|
||||
tag("year"),
|
||||
preceded(tag("from"), alt((tag("micros"), tag("millis"), tag("secs"), tag("unix")))),
|
||||
))(i)
|
||||
}
|
||||
|
||||
|
@ -570,7 +575,6 @@ fn function_type(i: &str) -> IResult<&str, &str> {
|
|||
tag("int"),
|
||||
tag("number"),
|
||||
tag("point"),
|
||||
tag("regex"),
|
||||
tag("string"),
|
||||
tag("table"),
|
||||
tag("thing"),
|
||||
|
@ -630,7 +634,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("<int> 1.2345", format!("{}", out));
|
||||
assert_eq!(out, Function::Cast(String::from("int"), 1.2345.into()));
|
||||
assert_eq!(out, Function::Cast(Kind::Int, 1.2345.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -640,7 +644,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("<string> 1.2345", format!("{}", out));
|
||||
assert_eq!(out, Function::Cast(String::from("string"), 1.2345.into()));
|
||||
assert_eq!(out, Function::Cast(Kind::String, 1.2345.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::sql::error::IResult;
|
|||
use crate::sql::escape::escape_rid;
|
||||
use crate::sql::ident::ident_raw;
|
||||
use crate::sql::number::integer;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::object::{object, Object};
|
||||
use crate::sql::strand::Strand;
|
||||
use crate::sql::thing::Thing;
|
||||
|
@ -106,6 +107,16 @@ impl From<Vec<Value>> for Id {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Number> for Id {
|
||||
fn from(v: Number) -> Self {
|
||||
match v {
|
||||
Number::Int(v) => v.into(),
|
||||
Number::Float(v) => v.to_string().into(),
|
||||
Number::Decimal(v) => v.to_string().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Thing> for Id {
|
||||
fn from(v: Thing) -> Self {
|
||||
v.id
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::common::verbar;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::fmt::Fmt;
|
||||
use crate::sql::table::{table, Table};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::char;
|
||||
use nom::character::complete::u64;
|
||||
use nom::combinator::map;
|
||||
use nom::multi::{separated_list0, separated_list1};
|
||||
use nom::combinator::opt;
|
||||
use nom::multi::separated_list1;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
pub enum Kind {
|
||||
Any,
|
||||
Array,
|
||||
Bool,
|
||||
Bytes,
|
||||
Datetime,
|
||||
|
@ -24,9 +26,15 @@ pub enum Kind {
|
|||
Int,
|
||||
Number,
|
||||
Object,
|
||||
Point,
|
||||
String,
|
||||
Uuid,
|
||||
Record(Vec<Table>),
|
||||
Geometry(Vec<String>),
|
||||
Option(Box<Kind>),
|
||||
Either(Vec<Kind>),
|
||||
Set(Box<Kind>, Option<u64>),
|
||||
Array(Box<Kind>, Option<u64>),
|
||||
}
|
||||
|
||||
impl Default for Kind {
|
||||
|
@ -35,11 +43,16 @@ impl Default for Kind {
|
|||
}
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
fn is_any(&self) -> bool {
|
||||
matches!(self, Kind::Any)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Kind {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Kind::Any => f.write_str("any"),
|
||||
Kind::Array => f.write_str("array"),
|
||||
Kind::Bool => f.write_str("bool"),
|
||||
Kind::Bytes => f.write_str("bytes"),
|
||||
Kind::Datetime => f.write_str("datetime"),
|
||||
|
@ -49,17 +62,43 @@ impl Display for Kind {
|
|||
Kind::Int => f.write_str("int"),
|
||||
Kind::Number => f.write_str("number"),
|
||||
Kind::Object => f.write_str("object"),
|
||||
Kind::Point => f.write_str("point"),
|
||||
Kind::String => f.write_str("string"),
|
||||
Kind::Record(v) => write!(f, "record({})", Fmt::comma_separated(v)),
|
||||
Kind::Geometry(v) => write!(f, "geometry({})", Fmt::comma_separated(v)),
|
||||
Kind::Uuid => f.write_str("uuid"),
|
||||
Kind::Option(k) => write!(f, "option<{}>", k),
|
||||
Kind::Record(k) => match k {
|
||||
k if k.is_empty() => write!(f, "record"),
|
||||
k => write!(f, "record<{}>", Fmt::verbar_separated(k)),
|
||||
},
|
||||
Kind::Geometry(k) => match k {
|
||||
k if k.is_empty() => write!(f, "geometry"),
|
||||
k => write!(f, "geometry<{}>", Fmt::verbar_separated(k)),
|
||||
},
|
||||
Kind::Set(k, l) => match (k, l) {
|
||||
(k, None) if k.is_any() => write!(f, "set"),
|
||||
(k, None) => write!(f, "set<{k}>"),
|
||||
(k, Some(l)) => write!(f, "set<{k}, {l}>"),
|
||||
},
|
||||
Kind::Array(k, l) => match (k, l) {
|
||||
(k, None) if k.is_any() => write!(f, "array"),
|
||||
(k, None) => write!(f, "array<{k}>"),
|
||||
(k, Some(l)) => write!(f, "array<{k}, {l}>"),
|
||||
},
|
||||
Kind::Either(k) => write!(f, "{}", Fmt::verbar_separated(k)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kind(i: &str) -> IResult<&str, Kind> {
|
||||
alt((any, either, option))(i)
|
||||
}
|
||||
|
||||
pub fn any(i: &str) -> IResult<&str, Kind> {
|
||||
map(tag("any"), |_| Kind::Any)(i)
|
||||
}
|
||||
|
||||
pub fn simple(i: &str) -> IResult<&str, Kind> {
|
||||
alt((
|
||||
map(tag("any"), |_| Kind::Any),
|
||||
map(tag("array"), |_| Kind::Array),
|
||||
map(tag("bool"), |_| Kind::Bool),
|
||||
map(tag("bytes"), |_| Kind::Bytes),
|
||||
map(tag("datetime"), |_| Kind::Datetime),
|
||||
|
@ -69,41 +108,412 @@ pub fn kind(i: &str) -> IResult<&str, Kind> {
|
|||
map(tag("int"), |_| Kind::Int),
|
||||
map(tag("number"), |_| Kind::Number),
|
||||
map(tag("object"), |_| Kind::Object),
|
||||
map(tag("point"), |_| Kind::Point),
|
||||
map(tag("string"), |_| Kind::String),
|
||||
map(geometry, Kind::Geometry),
|
||||
map(record, Kind::Record),
|
||||
map(tag("uuid"), |_| Kind::Uuid),
|
||||
))(i)
|
||||
}
|
||||
|
||||
fn record(i: &str) -> IResult<&str, Vec<Table>> {
|
||||
let (i, _) = tag("record")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char('(')(i)?;
|
||||
let (i, v) = separated_list0(commas, table)(i)?;
|
||||
let (i, _) = char(')')(i)?;
|
||||
Ok((i, v))
|
||||
fn either(i: &str) -> IResult<&str, Kind> {
|
||||
let (i, mut v) = separated_list1(verbar, alt((simple, geometry, record, array, set)))(i)?;
|
||||
match v.len() {
|
||||
1 => Ok((i, v.remove(0))),
|
||||
_ => Ok((i, Kind::Either(v))),
|
||||
}
|
||||
}
|
||||
|
||||
fn geometry(i: &str) -> IResult<&str, Vec<String>> {
|
||||
let (i, _) = tag("geometry")(i)?;
|
||||
fn option(i: &str) -> IResult<&str, Kind> {
|
||||
let (i, _) = tag("option")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char('(')(i)?;
|
||||
let (i, v) = separated_list1(
|
||||
commas,
|
||||
map(
|
||||
alt((
|
||||
tag("feature"),
|
||||
tag("point"),
|
||||
tag("line"),
|
||||
tag("polygon"),
|
||||
tag("multipoint"),
|
||||
tag("multiline"),
|
||||
tag("multipolygon"),
|
||||
tag("collection"),
|
||||
)),
|
||||
String::from,
|
||||
),
|
||||
)(i)?;
|
||||
let (i, _) = char(')')(i)?;
|
||||
Ok((i, v))
|
||||
let (i, _) = char('<')(i)?;
|
||||
let (i, v) = map(alt((either, simple, geometry, record, array, set)), Box::new)(i)?;
|
||||
let (i, _) = char('>')(i)?;
|
||||
Ok((i, Kind::Option(v)))
|
||||
}
|
||||
|
||||
fn record(i: &str) -> IResult<&str, Kind> {
|
||||
let (i, _) = tag("record")(i)?;
|
||||
let (i, v) = opt(alt((
|
||||
|i| {
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char('(')(i)?;
|
||||
let (i, v) = separated_list1(commas, table)(i)?;
|
||||
let (i, _) = char(')')(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
|i| {
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char('<')(i)?;
|
||||
let (i, v) = separated_list1(verbar, table)(i)?;
|
||||
let (i, _) = char('>')(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
)))(i)?;
|
||||
Ok((i, Kind::Record(v.unwrap_or_default())))
|
||||
}
|
||||
|
||||
fn geometry(i: &str) -> IResult<&str, Kind> {
|
||||
let (i, _) = tag("geometry")(i)?;
|
||||
let (i, v) = opt(alt((
|
||||
|i| {
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char('(')(i)?;
|
||||
let (i, v) = separated_list1(commas, geo)(i)?;
|
||||
let (i, _) = char(')')(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
|i| {
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char('<')(i)?;
|
||||
let (i, v) = separated_list1(verbar, geo)(i)?;
|
||||
let (i, _) = char('>')(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
)))(i)?;
|
||||
Ok((i, Kind::Geometry(v.unwrap_or_default())))
|
||||
}
|
||||
|
||||
fn array(i: &str) -> IResult<&str, Kind> {
|
||||
let (i, _) = tag("array")(i)?;
|
||||
let (i, v) = opt(|i| {
|
||||
let (i, _) = char('<')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, k) = kind(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, l) = opt(|i| {
|
||||
let (i, _) = char(',')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, l) = u64(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
Ok((i, l))
|
||||
})(i)?;
|
||||
let (i, _) = char('>')(i)?;
|
||||
Ok((i, (k, l)))
|
||||
})(i)?;
|
||||
Ok((
|
||||
i,
|
||||
match v {
|
||||
Some((k, l)) => Kind::Array(Box::new(k), l),
|
||||
None => Kind::Array(Box::new(Kind::Any), None),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn set(i: &str) -> IResult<&str, Kind> {
|
||||
let (i, _) = tag("set")(i)?;
|
||||
let (i, v) = opt(|i| {
|
||||
let (i, _) = char('<')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, k) = kind(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, l) = opt(|i| {
|
||||
let (i, _) = char(',')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, l) = u64(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
Ok((i, l))
|
||||
})(i)?;
|
||||
let (i, _) = char('>')(i)?;
|
||||
Ok((i, (k, l)))
|
||||
})(i)?;
|
||||
Ok((
|
||||
i,
|
||||
match v {
|
||||
Some((k, l)) => Kind::Set(Box::new(k), l),
|
||||
None => Kind::Set(Box::new(Kind::Any), None),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn geo(i: &str) -> IResult<&str, String> {
|
||||
map(
|
||||
alt((
|
||||
tag("feature"),
|
||||
tag("point"),
|
||||
tag("line"),
|
||||
tag("polygon"),
|
||||
tag("multipoint"),
|
||||
tag("multiline"),
|
||||
tag("multipolygon"),
|
||||
tag("collection"),
|
||||
)),
|
||||
String::from,
|
||||
)(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::table::Table;
|
||||
|
||||
#[test]
|
||||
fn kind_any() {
|
||||
let sql = "any";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("any", format!("{}", out));
|
||||
assert_eq!(out, Kind::Any);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_bool() {
|
||||
let sql = "bool";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("bool", format!("{}", out));
|
||||
assert_eq!(out, Kind::Bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_bytes() {
|
||||
let sql = "bytes";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("bytes", format!("{}", out));
|
||||
assert_eq!(out, Kind::Bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_datetime() {
|
||||
let sql = "datetime";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("datetime", format!("{}", out));
|
||||
assert_eq!(out, Kind::Datetime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_decimal() {
|
||||
let sql = "decimal";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("decimal", format!("{}", out));
|
||||
assert_eq!(out, Kind::Decimal);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_duration() {
|
||||
let sql = "duration";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("duration", format!("{}", out));
|
||||
assert_eq!(out, Kind::Duration);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_float() {
|
||||
let sql = "float";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("float", format!("{}", out));
|
||||
assert_eq!(out, Kind::Float);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_number() {
|
||||
let sql = "number";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("number", format!("{}", out));
|
||||
assert_eq!(out, Kind::Number);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_object() {
|
||||
let sql = "object";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("object", format!("{}", out));
|
||||
assert_eq!(out, Kind::Object);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_point() {
|
||||
let sql = "point";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("point", format!("{}", out));
|
||||
assert_eq!(out, Kind::Point);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_string() {
|
||||
let sql = "string";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("string", format!("{}", out));
|
||||
assert_eq!(out, Kind::String);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_uuid() {
|
||||
let sql = "uuid";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("uuid", format!("{}", out));
|
||||
assert_eq!(out, Kind::Uuid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_either() {
|
||||
let sql = "int | float";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("int | float", format!("{}", out));
|
||||
assert_eq!(out, Kind::Either(vec![Kind::Int, Kind::Float]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_record_any() {
|
||||
let sql = "record";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("record", format!("{}", out));
|
||||
assert_eq!(out, Kind::Record(vec![]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_record_one() {
|
||||
let sql = "record<person>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("record<person>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Record(vec![Table::from("person")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_record_many() {
|
||||
let sql = "record<person | animal>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("record<person | animal>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Record(vec![Table::from("person"), Table::from("animal")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_geometry_any() {
|
||||
let sql = "geometry";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("geometry", format!("{}", out));
|
||||
assert_eq!(out, Kind::Geometry(vec![]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_geometry_one() {
|
||||
let sql = "geometry<point>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("geometry<point>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Geometry(vec![String::from("point")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_geometry_many() {
|
||||
let sql = "geometry<point | multipoint>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("geometry<point | multipoint>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Geometry(vec![String::from("point"), String::from("multipoint")]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_option_one() {
|
||||
let sql = "option<int>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("option<int>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Option(Box::new(Kind::Int)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_option_many() {
|
||||
let sql = "option<int | float>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("option<int | float>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Option(Box::new(Kind::Either(vec![Kind::Int, Kind::Float]))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_array_any() {
|
||||
let sql = "array";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("array", format!("{}", out));
|
||||
assert_eq!(out, Kind::Array(Box::new(Kind::Any), None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_array_some() {
|
||||
let sql = "array<float>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("array<float>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Array(Box::new(Kind::Float), None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_array_some_size() {
|
||||
let sql = "array<float, 10>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("array<float, 10>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Array(Box::new(Kind::Float), Some(10)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_set_any() {
|
||||
let sql = "set";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("set", format!("{}", out));
|
||||
assert_eq!(out, Kind::Set(Box::new(Kind::Any), None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_set_some() {
|
||||
let sql = "set<float>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("set<float>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Set(Box::new(Kind::Float), None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_set_some_size() {
|
||||
let sql = "set<float, 10>";
|
||||
let res = kind(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("set<float, 10>", format!("{}", out));
|
||||
assert_eq!(out, Kind::Set(Box::new(Kind::Float), Some(10)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::dbs::Transaction;
|
|||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
|
@ -23,10 +24,13 @@ impl Limit {
|
|||
doc: Option<&Value>,
|
||||
) -> Result<usize, Error> {
|
||||
match self.0.compute(ctx, opt, txn, doc).await {
|
||||
Ok(v) if v.is_integer() && v.is_positive() => Ok(v.as_usize()),
|
||||
// This is a valid limiting number
|
||||
Ok(Value::Number(Number::Int(v))) if v >= 0 => Ok(v as usize),
|
||||
// An invalid value was specified
|
||||
Ok(v) => Err(Error::InvalidLimit {
|
||||
value: v.as_string(),
|
||||
}),
|
||||
// A different error occured
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::ending::number as ending;
|
||||
use crate::sql::error::Error::Parser;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use crate::sql::strand::Strand;
|
||||
use bigdecimal::num_traits::Pow;
|
||||
use bigdecimal::BigDecimal;
|
||||
use bigdecimal::FromPrimitive;
|
||||
use bigdecimal::ToPrimitive;
|
||||
use nom::branch::alt;
|
||||
use nom::character::complete::i64;
|
||||
use nom::combinator::map;
|
||||
use nom::number::complete::recognize_float;
|
||||
use nom::Err::Failure;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
@ -58,6 +60,12 @@ impl From<i64> for Number {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<i128> for Number {
|
||||
fn from(i: i128) -> Self {
|
||||
Self::Int(i as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<isize> for Number {
|
||||
fn from(i: isize) -> Self {
|
||||
Self::Int(i as i64)
|
||||
|
@ -88,6 +96,12 @@ impl From<u64> for Number {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<u128> for Number {
|
||||
fn from(i: u128) -> Self {
|
||||
Self::Int(i as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Number {
|
||||
fn from(i: usize) -> Self {
|
||||
Self::Int(i as i64)
|
||||
|
@ -106,27 +120,108 @@ impl From<f64> for Number {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Number {
|
||||
fn from(s: &str) -> Self {
|
||||
impl From<BigDecimal> for Number {
|
||||
fn from(v: BigDecimal) -> Self {
|
||||
Self::Decimal(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Number {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Number {
|
||||
type Error = ();
|
||||
fn try_from(v: String) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Strand> for Number {
|
||||
type Error = ();
|
||||
fn try_from(v: Strand) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Number {
|
||||
type Error = ();
|
||||
fn try_from(v: &str) -> Result<Self, Self::Error> {
|
||||
// Attempt to parse as i64
|
||||
match s.parse::<i64>() {
|
||||
match v.parse::<i64>() {
|
||||
// Store it as an i64
|
||||
Ok(v) => Self::Int(v),
|
||||
// It wasn't parsed as a i64 so store as a decimal
|
||||
_ => Self::Decimal(BigDecimal::from_str(s).unwrap_or_default()),
|
||||
Ok(v) => Ok(Self::Int(v)),
|
||||
// It wasn't parsed as a i64 so parse as a decimal
|
||||
_ => match BigDecimal::from_str(v) {
|
||||
// Store it as a BigDecimal
|
||||
Ok(v) => Ok(Self::Decimal(v)),
|
||||
// It wasn't parsed as a number
|
||||
_ => Err(()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Number {
|
||||
fn from(s: String) -> Self {
|
||||
Self::from(s.as_str())
|
||||
impl TryFrom<Number> for i8 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_i8() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i8")),
|
||||
},
|
||||
Number::Float(v) => match v.to_i8() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i8")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_i8() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i8")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BigDecimal> for Number {
|
||||
fn from(v: BigDecimal) -> Self {
|
||||
Self::Decimal(v)
|
||||
impl TryFrom<Number> for i16 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_i16() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i16")),
|
||||
},
|
||||
Number::Float(v) => match v.to_i16() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i16")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_i16() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i16")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Number> for i32 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_i32() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i32")),
|
||||
},
|
||||
Number::Float(v) => match v.to_i32() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i32")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_i32() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i32")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,8 +229,158 @@ impl TryFrom<Number> for i64 {
|
|||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(x) => Ok(x),
|
||||
_ => Err(Error::TryFromError(value.to_string(), "i64")),
|
||||
Number::Int(v) => match v.to_i64() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i64")),
|
||||
},
|
||||
Number::Float(v) => match v.to_i64() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i64")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_i64() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i64")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Number> for i128 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_i128() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i128")),
|
||||
},
|
||||
Number::Float(v) => match v.to_i128() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i128")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_i128() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "i128")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Number> for u8 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_u8() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u8")),
|
||||
},
|
||||
Number::Float(v) => match v.to_u8() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u8")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_u8() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u8")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Number> for u16 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_u16() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u16")),
|
||||
},
|
||||
Number::Float(v) => match v.to_u16() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u16")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_u16() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u16")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Number> for u32 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_u32() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u32")),
|
||||
},
|
||||
Number::Float(v) => match v.to_u32() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u32")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_u32() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u32")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Number> for u64 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_u64() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u64")),
|
||||
},
|
||||
Number::Float(v) => match v.to_u64() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u64")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_u64() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u64")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Number> for u128 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_u128() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u128")),
|
||||
},
|
||||
Number::Float(v) => match v.to_u128() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u128")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_u128() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "u128")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Number> for f32 {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match v.to_f32() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "f32")),
|
||||
},
|
||||
Number::Float(v) => match v.to_f32() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "f32")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_f32() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "f32")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,8 +389,18 @@ impl TryFrom<Number> for f64 {
|
|||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Float(x) => Ok(x),
|
||||
_ => Err(Error::TryFromError(value.to_string(), "f64")),
|
||||
Number::Int(v) => match v.to_f64() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "f64")),
|
||||
},
|
||||
Number::Float(v) => match v.to_f64() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "f64")),
|
||||
},
|
||||
Number::Decimal(ref v) => match v.to_f64() {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "f64")),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,8 +409,15 @@ impl TryFrom<Number> for BigDecimal {
|
|||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match BigDecimal::from_i64(v) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
},
|
||||
Number::Float(v) => match BigDecimal::from_f64(v) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
},
|
||||
Number::Decimal(x) => Ok(x),
|
||||
_ => Err(Error::TryFromError(value.to_string(), "BigDecimal")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -393,9 +655,9 @@ impl Number {
|
|||
|
||||
pub fn fixed(self, precision: usize) -> Number {
|
||||
match self {
|
||||
Number::Int(v) => format!("{v:.precision$}").into(),
|
||||
Number::Float(v) => format!("{v:.precision$}").into(),
|
||||
Number::Decimal(v) => format!("{v:.precision$}").into(),
|
||||
Number::Int(v) => format!("{v:.precision$}").try_into().unwrap_or_default(),
|
||||
Number::Float(v) => format!("{v:.precision$}").try_into().unwrap_or_default(),
|
||||
Number::Decimal(v) => format!("{v:.precision$}").try_into().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -651,7 +913,7 @@ impl Sort for Vec<Number> {
|
|||
}
|
||||
|
||||
pub fn number(i: &str) -> IResult<&str, Number> {
|
||||
alt((map(decimal, Number::from), map(integer, Number::from)))(i)
|
||||
alt((int, decimal))(i)
|
||||
}
|
||||
|
||||
pub fn integer(i: &str) -> IResult<&str, i64> {
|
||||
|
@ -660,10 +922,16 @@ pub fn integer(i: &str) -> IResult<&str, i64> {
|
|||
Ok((i, v))
|
||||
}
|
||||
|
||||
pub fn decimal(i: &str) -> IResult<&str, &str> {
|
||||
fn int(i: &str) -> IResult<&str, Number> {
|
||||
let (i, v) = i64(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, Number::from(v)))
|
||||
}
|
||||
|
||||
fn decimal(i: &str) -> IResult<&str, Number> {
|
||||
let (i, v) = recognize_float(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, v))
|
||||
Ok((i, Number::try_from(v).map_err(|_| Failure(Parser(i)))?))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -672,43 +940,43 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn number_integer() {
|
||||
fn number_int() {
|
||||
let sql = "123";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("123", format!("{}", out));
|
||||
assert_eq!(out, Number::from(123));
|
||||
assert_eq!(out, Number::Int(123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_integer_neg() {
|
||||
fn number_int_neg() {
|
||||
let sql = "-123";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("-123", format!("{}", out));
|
||||
assert_eq!(out, Number::from(-123));
|
||||
assert_eq!(out, Number::Int(-123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_decimal() {
|
||||
fn number_float() {
|
||||
let sql = "123.45";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("123.45", format!("{}", out));
|
||||
assert_eq!(out, Number::from(123.45));
|
||||
assert_eq!(out, Number::Float(123.45));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_decimal_neg() {
|
||||
fn number_float_neg() {
|
||||
let sql = "-123.45";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("-123.45", format!("{}", out));
|
||||
assert_eq!(out, Number::from(-123.45));
|
||||
assert_eq!(out, Number::Float(-123.45));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -718,7 +986,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("1234.5", format!("{}", out));
|
||||
assert_eq!(out, Number::from(1234.5));
|
||||
assert_eq!(out, Number::Float(1234.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -728,7 +996,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("-1234.5", format!("{}", out));
|
||||
assert_eq!(out, Number::from(-1234.5));
|
||||
assert_eq!(out, Number::Float(-1234.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -738,7 +1006,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("123.45", format!("{}", out));
|
||||
assert_eq!(out, Number::from(123.45));
|
||||
assert_eq!(out, Number::Float(123.45));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -748,74 +1016,72 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("-123.45", format!("{}", out));
|
||||
assert_eq!(out, Number::from(-123.45));
|
||||
assert_eq!(out, Number::Float(-123.45));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_float_keeps_precision() {
|
||||
let sql = "13.571938471938472";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("13.571938471938472", format!("{}", out));
|
||||
assert_eq!(out, Number::try_from("13.571938471938472").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_decimal_keeps_precision() {
|
||||
let sql = "13.571938471938471938563985639413947693775636";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("13.571938471938471938563985639413947693775636", format!("{}", out));
|
||||
assert_eq!(out, Number::try_from("13.571938471938471938563985639413947693775636").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_int() {
|
||||
let res = number("3");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("4");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
|
||||
assert_eq!(res.pow(power), Number::from(81));
|
||||
let res = Number::Int(3).pow(Number::Int(4));
|
||||
assert_eq!(res, Number::Int(81));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_negatives() {
|
||||
let res = number("4");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("-0.5");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
|
||||
assert_eq!(res.pow(power), Number::from(0.5));
|
||||
fn number_pow_int_negative() {
|
||||
let res = Number::Int(4).pow(Number::Float(-0.5));
|
||||
assert_eq!(res, Number::Float(0.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_float() {
|
||||
let res = number("2.5");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("2");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
|
||||
assert_eq!(res.pow(power), Number::from(6.25));
|
||||
let res = Number::Float(2.5).pow(Number::Int(2));
|
||||
assert_eq!(res, Number::Float(6.25));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_bigdecimal_one() {
|
||||
let res = number("13.5719384719384719385639856394139476937756394756");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("1");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
fn number_pow_float_negative() {
|
||||
let res = Number::Int(4).pow(Number::Float(-0.5));
|
||||
assert_eq!(res, Number::Float(0.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_decimal_one() {
|
||||
let res = Number::try_from("13.5719384719384719385639856394139476937756394756")
|
||||
.unwrap()
|
||||
.pow(Number::Int(1));
|
||||
assert_eq!(
|
||||
res.pow(power),
|
||||
Number::from("13.5719384719384719385639856394139476937756394756")
|
||||
res,
|
||||
Number::try_from("13.5719384719384719385639856394139476937756394756").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_bigdecimal_int() {
|
||||
let res = number("13.5719384719384719385639856394139476937756394756");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("2");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
|
||||
assert_eq!(res.pow(power), Number::from("184.19751388608358465578173996877942643463869043732548087725588482334195240945031617770904299536"));
|
||||
fn number_pow_decimal_two() {
|
||||
let res = Number::try_from("13.5719384719384719385639856394139476937756394756")
|
||||
.unwrap()
|
||||
.pow(Number::Int(2));
|
||||
assert_eq!(
|
||||
res,
|
||||
Number::try_from("184.19751388608358465578173996877942643463869043732548087725588482334195240945031617770904299536").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ pub enum Op {
|
|||
|
||||
impl From<&Value> for Op {
|
||||
fn from(v: &Value) -> Self {
|
||||
match v.to_strand().as_str() {
|
||||
match v.to_raw_string().as_str() {
|
||||
"add" => Op::Add,
|
||||
"remove" => Op::Remove,
|
||||
"replace" => Op::Replace,
|
||||
|
|
|
@ -232,7 +232,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("[0]", format!("{}", out));
|
||||
assert_eq!(out, Part::Index(Number::from("0")));
|
||||
assert_eq!(out, Part::Index(Number::from(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::dbs::Transaction;
|
|||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
|
@ -23,10 +24,13 @@ impl Start {
|
|||
doc: Option<&Value>,
|
||||
) -> Result<usize, Error> {
|
||||
match self.0.compute(ctx, opt, txn, doc).await {
|
||||
Ok(v) if v.is_integer() && v.is_zero_or_positive() => Ok(v.as_usize()),
|
||||
// This is a valid starting number
|
||||
Ok(Value::Number(Number::Int(v))) if v >= 0 => Ok(v as usize),
|
||||
// An invalid value was specified
|
||||
Ok(v) => Err(Error::InvalidStart {
|
||||
value: v.as_string(),
|
||||
}),
|
||||
// A different error occured
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::sql::escape::escape_rid;
|
|||
use crate::sql::id::{id, Id};
|
||||
use crate::sql::ident::ident_raw;
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use crate::sql::strand::Strand;
|
||||
use crate::sql::value::Value;
|
||||
use derive::Store;
|
||||
use nom::branch::alt;
|
||||
|
@ -17,6 +18,7 @@ use nom::sequence::delimited;
|
|||
use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Thing";
|
||||
|
||||
|
@ -47,6 +49,37 @@ impl From<(&str, &str)> for Thing {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Thing {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Thing {
|
||||
type Error = ();
|
||||
fn try_from(v: String) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Strand> for Thing {
|
||||
type Error = ();
|
||||
fn try_from(v: Strand) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Thing {
|
||||
type Error = ();
|
||||
fn try_from(v: &str) -> Result<Self, Self::Error> {
|
||||
match thing_raw(v) {
|
||||
Ok((_, v)) => Ok(v),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Thing {
|
||||
/// Convert the Thing to a raw String
|
||||
pub fn to_raw(&self) -> String {
|
||||
|
|
|
@ -40,7 +40,7 @@ mod tests {
|
|||
let res = timeout(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(out, Timeout(Duration::from("5s")));
|
||||
assert_eq!("TIMEOUT 5s", format!("{}", out));
|
||||
assert_eq!(out, Timeout(Duration::try_from("5s").unwrap()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::sql::common::is_hex;
|
|||
use crate::sql::error::IResult;
|
||||
use crate::sql::escape::escape_str;
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use crate::sql::strand::Strand;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::take_while_m_n;
|
||||
use nom::character::complete::char;
|
||||
|
@ -12,36 +13,56 @@ use serde::{Deserialize, Serialize};
|
|||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Uuid";
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Deserialize, Hash)]
|
||||
pub struct Uuid(pub uuid::Uuid);
|
||||
|
||||
impl From<&str> for Uuid {
|
||||
fn from(s: &str) -> Self {
|
||||
uuid::Uuid::try_parse(s).map(Self).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uuid::Uuid> for Uuid {
|
||||
fn from(v: uuid::Uuid) -> Self {
|
||||
Uuid(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Uuid {
|
||||
fn from(s: String) -> Self {
|
||||
Self::from(s.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Uuid> for uuid::Uuid {
|
||||
fn from(s: Uuid) -> Self {
|
||||
s.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Uuid {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Uuid {
|
||||
type Error = ();
|
||||
fn try_from(v: String) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Strand> for Uuid {
|
||||
type Error = ();
|
||||
fn try_from(v: Strand) -> Result<Self, Self::Error> {
|
||||
Self::try_from(v.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Uuid {
|
||||
type Error = ();
|
||||
fn try_from(v: &str) -> Result<Self, Self::Error> {
|
||||
match uuid::Uuid::try_parse(v) {
|
||||
Ok(v) => Ok(Self(v)),
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Uuid {
|
||||
type Target = uuid::Uuid;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -129,7 +150,7 @@ fn uuid_raw(i: &str) -> IResult<&str, Uuid> {
|
|||
char('-'),
|
||||
take_while_m_n(12, 12, is_hex),
|
||||
)))(i)?;
|
||||
Ok((i, Uuid::from(v)))
|
||||
Ok((i, Uuid::try_from(v).unwrap()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -144,7 +165,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'e72bee20-f49b-11ec-b939-0242ac120002'", format!("{}", out));
|
||||
assert_eq!(out, Uuid::from("e72bee20-f49b-11ec-b939-0242ac120002"));
|
||||
assert_eq!(out, Uuid::try_from("e72bee20-f49b-11ec-b939-0242ac120002").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -154,6 +175,6 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("'b19bc00b-aa98-486c-ae37-c8e1c54295b1'", format!("{}", out));
|
||||
assert_eq!(out, Uuid::from("b19bc00b-aa98-486c-ae37-c8e1c54295b1"));
|
||||
assert_eq!(out, Uuid::try_from("b19bc00b-aa98-486c-ae37-c8e1c54295b1").unwrap());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::err::Error;
|
|||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub async fn clear(&mut self) -> Result<(), Error> {
|
||||
pub fn clear(&mut self) -> Result<(), Error> {
|
||||
*self = Value::None;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ mod tests {
|
|||
async fn clear_value() {
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::None;
|
||||
val.clear().await.unwrap();
|
||||
val.clear().unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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 _;
|
||||
|
@ -53,7 +54,7 @@ pub(super) struct SerializeFunction {
|
|||
}
|
||||
|
||||
enum Inner {
|
||||
Cast(Option<String>, Option<Value>),
|
||||
Cast(Option<Kind>, Option<Value>),
|
||||
Normal(Option<String>, Option<Vec<Value>>),
|
||||
Custom(Option<String>, Option<Vec<Value>>),
|
||||
Script(Option<Script>, Option<Vec<Value>>),
|
||||
|
@ -68,17 +69,15 @@ impl serde::ser::SerializeTupleVariant for SerializeFunction {
|
|||
T: Serialize + ?Sized,
|
||||
{
|
||||
match (self.index, &mut self.inner) {
|
||||
(
|
||||
0,
|
||||
Inner::Cast(ref mut var, _)
|
||||
| Inner::Normal(ref mut var, _)
|
||||
| Inner::Custom(ref mut var, _),
|
||||
) => {
|
||||
(0, Inner::Normal(ref mut var, _) | Inner::Custom(ref mut var, _)) => {
|
||||
*var = Some(value.serialize(ser::string::Serializer.wrap())?);
|
||||
}
|
||||
(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,6 +1,9 @@
|
|||
pub(super) mod vec;
|
||||
|
||||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::Kind;
|
||||
use ser::Serializer as _;
|
||||
use serde::ser::Error as _;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
@ -14,7 +17,7 @@ impl ser::Serializer for Serializer {
|
|||
type SerializeSeq = Impossible<Kind, Error>;
|
||||
type SerializeTuple = Impossible<Kind, Error>;
|
||||
type SerializeTupleStruct = Impossible<Kind, Error>;
|
||||
type SerializeTupleVariant = Impossible<Kind, Error>;
|
||||
type SerializeTupleVariant = SerializeKindTuple;
|
||||
type SerializeMap = Impossible<Kind, Error>;
|
||||
type SerializeStruct = Impossible<Kind, Error>;
|
||||
type SerializeStructVariant = Impossible<Kind, Error>;
|
||||
|
@ -30,7 +33,6 @@ impl ser::Serializer for Serializer {
|
|||
) -> Result<Self::Ok, Error> {
|
||||
match variant {
|
||||
"Any" => Ok(Kind::Any),
|
||||
"Array" => Ok(Kind::Array),
|
||||
"Bool" => Ok(Kind::Bool),
|
||||
"Bytes" => Ok(Kind::Bytes),
|
||||
"Datetime" => Ok(Kind::Datetime),
|
||||
|
@ -40,7 +42,9 @@ impl ser::Serializer for Serializer {
|
|||
"Int" => Ok(Kind::Int),
|
||||
"Number" => Ok(Kind::Number),
|
||||
"Object" => Ok(Kind::Object),
|
||||
"Point" => Ok(Kind::Point),
|
||||
"String" => Ok(Kind::String),
|
||||
"Uuid" => Ok(Kind::Uuid),
|
||||
variant => Err(Error::custom(format!("unexpected unit variant `{name}::{variant}`"))),
|
||||
}
|
||||
}
|
||||
|
@ -59,18 +63,88 @@ impl ser::Serializer for Serializer {
|
|||
match variant {
|
||||
"Record" => Ok(Kind::Record(value.serialize(ser::table::vec::Serializer.wrap())?)),
|
||||
"Geometry" => Ok(Kind::Geometry(value.serialize(ser::string::vec::Serializer.wrap())?)),
|
||||
"Option" => Ok(Kind::Option(Box::new(value.serialize(Serializer.wrap())?))),
|
||||
"Either" => Ok(Kind::Either(value.serialize(vec::Serializer.wrap())?)),
|
||||
variant => {
|
||||
Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
match variant {
|
||||
"Set" => Ok(SerializeKindTuple {
|
||||
variant,
|
||||
..Default::default()
|
||||
}),
|
||||
"Array" => Ok(SerializeKindTuple {
|
||||
variant,
|
||||
..Default::default()
|
||||
}),
|
||||
variant => Err(Error::custom(format!("unexpected tuple variant `{name}::{variant}`"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct SerializeKindTuple {
|
||||
index: usize,
|
||||
variant: &'static str,
|
||||
kind: Option<Kind>,
|
||||
num: Option<u64>,
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTupleVariant for SerializeKindTuple {
|
||||
type Ok = Kind;
|
||||
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(Serializer.wrap())?);
|
||||
}
|
||||
1 => {
|
||||
self.num = value.serialize(ser::primitive::u64::opt::Serializer.wrap())?;
|
||||
}
|
||||
index => {
|
||||
let variant = self.variant;
|
||||
return Err(Error::custom(format!("unexpected `Kind::{variant}` index `{index}`")));
|
||||
}
|
||||
}
|
||||
self.index += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
let variant = self.variant;
|
||||
let kind = match self.kind {
|
||||
Some(kind) => kind,
|
||||
_ => {
|
||||
return Err(Error::custom("`Kind::{variant}` missing required value(s)"));
|
||||
}
|
||||
};
|
||||
match variant {
|
||||
"Set" => Ok(Kind::Set(Box::new(kind), self.num)),
|
||||
"Array" => Ok(Kind::Array(Box::new(kind), self.num)),
|
||||
_ => Err(Error::custom("unknown tuple variant `Kind::{variant}`")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::sql::serde::serialize_internal;
|
||||
use ser::Serializer as _;
|
||||
|
||||
#[test]
|
||||
fn any() {
|
||||
|
@ -79,13 +153,6 @@ mod tests {
|
|||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array() {
|
||||
let kind = Kind::Array;
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool() {
|
||||
let kind = Kind::Bool;
|
||||
|
@ -149,6 +216,13 @@ mod tests {
|
|||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn point() {
|
||||
let kind = Kind::Point;
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string() {
|
||||
let kind = Kind::String;
|
||||
|
@ -156,6 +230,13 @@ mod tests {
|
|||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uuid() {
|
||||
let kind = Kind::Uuid;
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record() {
|
||||
let kind = Kind::Record(Default::default());
|
||||
|
@ -177,4 +258,44 @@ mod tests {
|
|||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option() {
|
||||
let kind = Kind::Option(Box::new(Default::default()));
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn either() {
|
||||
let kind = Kind::Either(Default::default());
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
|
||||
let kind = Kind::Either(vec![Default::default()]);
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set() {
|
||||
let kind = Kind::Set(Box::new(Default::default()), None);
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
|
||||
let kind = Kind::Set(Box::new(Default::default()), Some(Default::default()));
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array() {
|
||||
let kind = Kind::Array(Box::new(Default::default()), None);
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
|
||||
let kind = Kind::Array(Box::new(Default::default()), Some(Default::default()));
|
||||
let serialized = serialize_internal(|| kind.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(kind, serialized);
|
||||
}
|
||||
}
|
||||
|
|
78
lib/src/sql/value/serde/ser/kind/vec.rs
Normal file
78
lib/src/sql/value/serde/ser/kind/vec.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::Kind;
|
||||
use ser::Serializer as _;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
pub struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = Vec<Kind>;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = SerializeKindVec;
|
||||
type SerializeTuple = Impossible<Vec<Kind>, Error>;
|
||||
type SerializeTupleStruct = Impossible<Vec<Kind>, Error>;
|
||||
type SerializeTupleVariant = Impossible<Vec<Kind>, Error>;
|
||||
type SerializeMap = Impossible<Vec<Kind>, Error>;
|
||||
type SerializeStruct = Impossible<Vec<Kind>, Error>;
|
||||
type SerializeStructVariant = Impossible<Vec<Kind>, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "a `Vec<Kind>`";
|
||||
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
|
||||
Ok(SerializeKindVec(Vec::with_capacity(len.unwrap_or_default())))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_struct<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self.wrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SerializeKindVec(Vec<Kind>);
|
||||
|
||||
impl serde::ser::SerializeSeq for SerializeKindVec {
|
||||
type Ok = Vec<Kind>;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
self.0.push(value.serialize(ser::kind::Serializer.wrap())?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::sql::serde::serialize_internal;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let vec: Vec<Kind> = Vec::new();
|
||||
let serialized = serialize_internal(|| vec.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(vec, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec() {
|
||||
let vec = vec![Kind::default()];
|
||||
let serialized = serialize_internal(|| vec.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(vec, serialized);
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ impl ser::Serializer for Serializer {
|
|||
fn serialize_i128(self, value: i128) -> Result<Self::Ok, Error> {
|
||||
match BigDecimal::from_i128(value) {
|
||||
Some(decimal) => Ok(decimal.into()),
|
||||
None => Err(Error::TryFromError(value.to_string(), "BigDecimal")),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ impl ser::Serializer for Serializer {
|
|||
fn serialize_u128(self, value: u128) -> Result<Self::Ok, Error> {
|
||||
match BigDecimal::from_u128(value) {
|
||||
Some(decimal) => Ok(decimal.into()),
|
||||
None => Err(Error::TryFromError(value.to_string(), "BigDecimal")),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,3 +3,5 @@ pub mod f64;
|
|||
pub mod i64;
|
||||
pub mod u32;
|
||||
pub mod u64;
|
||||
|
||||
mod opt;
|
||||
|
|
1
lib/src/sql/value/serde/ser/primitive/opt/mod.rs
Normal file
1
lib/src/sql/value/serde/ser/primitive/opt/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod u64;
|
55
lib/src/sql/value/serde/ser/primitive/opt/u64.rs
Normal file
55
lib/src/sql/value/serde/ser/primitive/opt/u64.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
pub struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = Option<u64>;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Option<u64>, Error>;
|
||||
type SerializeTuple = Impossible<Option<u64>, Error>;
|
||||
type SerializeTupleStruct = Impossible<Option<u64>, Error>;
|
||||
type SerializeTupleVariant = Impossible<Option<u64>, Error>;
|
||||
type SerializeMap = Impossible<Option<u64>, Error>;
|
||||
type SerializeStruct = Impossible<Option<u64>, Error>;
|
||||
type SerializeStructVariant = Impossible<Option<u64>, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an `Option<u64>`";
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
Ok(Some(value.serialize(ser::primitive::u64::Serializer.wrap())?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::sql::serde::serialize_internal;
|
||||
use ser::Serializer as _;
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
let option: Option<u64> = None;
|
||||
let serialized = serialize_internal(|| option.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn some() {
|
||||
let option = Some(u64::default());
|
||||
let serialized = serialize_internal(|| option.serialize(Serializer.wrap())).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
pub use super::opt::u64 as opt;
|
||||
|
||||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use serde::ser::Impossible;
|
||||
|
|
|
@ -99,7 +99,7 @@ impl ser::Serializer for Serializer {
|
|||
fn serialize_i128(self, value: i128) -> Result<Self::Ok, Error> {
|
||||
match BigDecimal::from_i128(value) {
|
||||
Some(decimal) => Ok(decimal.into()),
|
||||
None => Err(Error::TryFromError(value.to_string(), "BigDecimal")),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ impl ser::Serializer for Serializer {
|
|||
fn serialize_u128(self, value: u128) -> Result<Self::Ok, Error> {
|
||||
match BigDecimal::from_u128(value) {
|
||||
Some(decimal) => Ok(decimal.into()),
|
||||
None => Err(Error::TryFromError(value.to_string(), "BigDecimal")),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -810,7 +810,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn function() {
|
||||
let function = Box::new(Function::Cast("foo".to_owned(), Default::default()));
|
||||
let function = Box::new(Function::Cast(Default::default(), Default::default()));
|
||||
let value = to_value(&function).unwrap();
|
||||
let expected = Value::Function(function);
|
||||
assert_eq!(value, expected);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -32,7 +32,7 @@ mod tests {
|
|||
let res = version(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(out, Version(Datetime::from("2020-01-01T00:00:00Z")));
|
||||
assert_eq!(out, Version(Datetime::try_from("2020-01-01T00:00:00Z").unwrap()));
|
||||
assert_eq!("VERSION '2020-01-01T00:00:00Z'", format!("{}", out));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ async fn clear_transaction_cache_table() -> Result<(), Error> {
|
|||
#[tokio::test]
|
||||
async fn clear_transaction_cache_field() -> Result<(), Error> {
|
||||
let sql = "
|
||||
DEFINE FIELD test ON person TYPE string VALUE 'test';
|
||||
DEFINE FIELD test ON person TYPE option<string> VALUE 'test';
|
||||
BEGIN;
|
||||
UPDATE person:one CONTENT { x: 0 };
|
||||
SELECT * FROM person;
|
||||
|
|
|
@ -11,15 +11,17 @@ async fn field_definition_value_assert_failure() -> Result<(), Error> {
|
|||
DEFINE TABLE person SCHEMAFULL;
|
||||
DEFINE FIELD age ON person TYPE number ASSERT $value > 0;
|
||||
DEFINE FIELD email ON person TYPE string ASSERT is::email($value);
|
||||
DEFINE FIELD name ON person TYPE string VALUE $value OR 'No name';
|
||||
DEFINE FIELD name ON person TYPE option<string> VALUE $value OR 'No name';
|
||||
CREATE person:test SET email = 'info@surrealdb.com', other = 'ignore';
|
||||
CREATE person:test SET email = 'info@surrealdb.com', other = 'ignore', age = NONE;
|
||||
CREATE person:test SET email = 'info@surrealdb.com', other = 'ignore', age = NULL;
|
||||
CREATE person:test SET email = 'info@surrealdb.com', other = 'ignore', age = 0;
|
||||
CREATE person:test SET email = 'info@surrealdb.com', other = 'ignore', age = 13;
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||
assert_eq!(res.len(), 7);
|
||||
assert_eq!(res.len(), 9);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
|
@ -36,21 +38,40 @@ async fn field_definition_value_assert_failure() -> Result<(), Error> {
|
|||
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 field must conform to: $value > 0"
|
||||
Some(e) if e.to_string() == "Found NONE for field `age`, with record `person:test`, but expected a number"
|
||||
));
|
||||
//
|
||||
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 field must conform to: $value > 0"
|
||||
Some(e) if e.to_string() == "Found NONE for field `age`, with record `person:test`, but expected a number"
|
||||
));
|
||||
//
|
||||
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 field must conform to: $value > 0"
|
||||
Some(e) if e.to_string() == "Found NULL for field `age`, with record `person:test`, but expected a number"
|
||||
));
|
||||
//
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
age: 13,
|
||||
email: 'info@surrealdb.com',
|
||||
id: person:test,
|
||||
name: 'No name',
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -60,7 +81,7 @@ async fn field_definition_value_assert_success() -> Result<(), Error> {
|
|||
DEFINE TABLE person SCHEMAFULL;
|
||||
DEFINE FIELD age ON person TYPE number ASSERT $value > 0;
|
||||
DEFINE FIELD email ON person TYPE string ASSERT is::email($value);
|
||||
DEFINE FIELD name ON person TYPE string VALUE $value OR 'No name';
|
||||
DEFINE FIELD name ON person TYPE option<string> VALUE $value OR 'No name';
|
||||
CREATE person:test SET email = 'info@surrealdb.com', other = 'ignore', age = 22;
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
|
|
|
@ -21,9 +21,11 @@ async fn function_array_all() -> Result<(), Error> {
|
|||
let val = Value::True;
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::False;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::False;
|
||||
|
@ -48,9 +50,11 @@ async fn function_array_any() -> Result<(), Error> {
|
|||
let val = Value::False;
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::False;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::True;
|
||||
|
@ -75,9 +79,11 @@ async fn function_array_combine() -> Result<(), Error> {
|
|||
let val = Value::parse("[]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[ [1,2], [1,3], [2,2], [2,3] ]");
|
||||
|
@ -102,9 +108,11 @@ async fn function_array_complement() -> Result<(), Error> {
|
|||
let val = Value::parse("[]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[1,2]");
|
||||
|
@ -129,9 +137,11 @@ async fn function_array_concat() -> Result<(), Error> {
|
|||
let val = Value::parse("[]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[1,2,3,4,3,4,5,6]");
|
||||
|
@ -156,10 +166,11 @@ async fn function_array_difference() -> Result<(), Error> {
|
|||
let val = Value::parse("[]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[1,2,5,6]");
|
||||
assert_eq!(tmp, val);
|
||||
|
@ -183,9 +194,11 @@ async fn function_array_distinct() -> Result<(), Error> {
|
|||
let val = Value::parse("[]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[1,2,3,4]");
|
||||
|
@ -211,9 +224,11 @@ async fn function_array_flatten() -> Result<(), Error> {
|
|||
let val = Value::parse("[]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[1,2,3,4]");
|
||||
|
@ -242,9 +257,11 @@ async fn function_array_group() -> Result<(), Error> {
|
|||
let val = Value::parse("[]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[1,2,3,4,5,6]");
|
||||
|
@ -301,9 +318,11 @@ async fn function_array_intersect() -> Result<(), Error> {
|
|||
let val = Value::parse("[]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[3,4]");
|
||||
|
@ -328,9 +347,11 @@ async fn function_array_max() -> Result<(), Error> {
|
|||
let val = Value::None;
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("'text'");
|
||||
|
@ -355,9 +376,11 @@ async fn function_array_min() -> Result<(), Error> {
|
|||
let val = Value::None;
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("1");
|
||||
|
@ -414,9 +437,11 @@ async fn function_array_union() -> Result<(), Error> {
|
|||
let val = Value::parse("[]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::None;
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[1,2,6,3,4,5]");
|
||||
|
|
195
lib/tests/typing.rs
Normal file
195
lib/tests/typing.rs
Normal file
|
@ -0,0 +1,195 @@
|
|||
mod parse;
|
||||
use parse::Parse;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::err::Error;
|
||||
use surrealdb::kvs::Datastore;
|
||||
use surrealdb::sql::Value;
|
||||
|
||||
#[tokio::test]
|
||||
async fn strict_typing_inline() -> Result<(), Error> {
|
||||
let sql = "
|
||||
UPDATE person:test SET age = <int> NONE;
|
||||
UPDATE person:test SET age = <int> '18';
|
||||
UPDATE person:test SET enabled = <bool | int> NONE;
|
||||
UPDATE person:test SET enabled = <bool | int> true;
|
||||
UPDATE person:test SET name = <string> 'Tobie Morgan Hitchcock';
|
||||
UPDATE person:test SET scores = <set<float>> [1,1,2,2,3,3,4,4,5,5];
|
||||
UPDATE person:test SET scores = <array<float>> [1,1,2,2,3,3,4,4,5,5];
|
||||
UPDATE person:test SET scores = <set<float, 5>> [1,1,2,2,3,3,4,4,5,5];
|
||||
UPDATE person:test SET scores = <array<float, 5>> [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");
|
||||
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||
assert_eq!(res.len(), 9);
|
||||
//
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
id: person:test,
|
||||
age: 18,
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
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"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
id: person:test,
|
||||
age: 18,
|
||||
enabled: true,
|
||||
}
|
||||
]",
|
||||
);
|
||||
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',
|
||||
}
|
||||
]",
|
||||
);
|
||||
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, 2.0, 3.0, 4.0, 5.0],
|
||||
}
|
||||
]",
|
||||
);
|
||||
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, 3.0, 4.0, 4.0, 5.0, 5.0],
|
||||
}
|
||||
]",
|
||||
);
|
||||
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, 2.0, 3.0, 4.0, 5.0],
|
||||
}
|
||||
]",
|
||||
);
|
||||
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);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn strict_typing_defined() -> Result<(), Error> {
|
||||
let sql = "
|
||||
DEFINE FIELD age ON person TYPE int;
|
||||
DEFINE FIELD enabled ON person TYPE bool | int;
|
||||
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];
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||
assert_eq!(res.len(), 8);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
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 int"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(e) if e.to_string() == "Found NONE for field `enabled`, with record `person:test`, but expected a bool | int"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(e) if e.to_string() == "Found NONE for field `name`, with record `person:test`, but expected a string"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
id: person:test,
|
||||
age: 18,
|
||||
enabled: true,
|
||||
name: 'Tobie Morgan Hitchcock',
|
||||
scores: [1.0, 2.0, 3.0, 4.0, 5.0],
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
|
@ -22,9 +22,9 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
match (ns, db, sc) {
|
||||
(Some(ns), Some(db), Some(sc)) => {
|
||||
// Process the provided values
|
||||
let ns = ns.to_strand().as_string();
|
||||
let db = db.to_strand().as_string();
|
||||
let sc = sc.to_strand().as_string();
|
||||
let ns = ns.to_raw_string();
|
||||
let db = db.to_raw_string();
|
||||
let sc = sc.to_raw_string();
|
||||
// Attempt to signin to specified scope
|
||||
super::signin::sc(session, ns, db, sc, vars).await
|
||||
}
|
||||
|
@ -37,10 +37,10 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
// There is a username and password
|
||||
(Some(user), Some(pass)) => {
|
||||
// Process the provided values
|
||||
let ns = ns.to_strand().as_string();
|
||||
let db = db.to_strand().as_string();
|
||||
let user = user.to_strand().as_string();
|
||||
let pass = pass.to_strand().as_string();
|
||||
let ns = ns.to_raw_string();
|
||||
let db = db.to_raw_string();
|
||||
let user = user.to_raw_string();
|
||||
let pass = pass.to_raw_string();
|
||||
// Attempt to signin to database
|
||||
super::signin::db(session, ns, db, user, pass).await
|
||||
}
|
||||
|
@ -57,9 +57,9 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
// There is a username and password
|
||||
(Some(user), Some(pass)) => {
|
||||
// Process the provided values
|
||||
let ns = ns.to_strand().as_string();
|
||||
let user = user.to_strand().as_string();
|
||||
let pass = pass.to_strand().as_string();
|
||||
let ns = ns.to_raw_string();
|
||||
let user = user.to_raw_string();
|
||||
let pass = pass.to_raw_string();
|
||||
// Attempt to signin to namespace
|
||||
super::signin::ns(session, ns, user, pass).await
|
||||
}
|
||||
|
@ -76,8 +76,8 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
// There is a username and password
|
||||
(Some(user), Some(pass)) => {
|
||||
// Process the provided values
|
||||
let user = user.to_strand().as_string();
|
||||
let pass = pass.to_strand().as_string();
|
||||
let user = user.to_raw_string();
|
||||
let pass = pass.to_raw_string();
|
||||
// Attempt to signin to namespace
|
||||
super::signin::su(session, user, pass).await
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ pub async fn signup(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
match (ns, db, sc) {
|
||||
(Some(ns), Some(db), Some(sc)) => {
|
||||
// Process the provided values
|
||||
let ns = ns.to_strand().as_string();
|
||||
let db = db.to_strand().as_string();
|
||||
let sc = sc.to_strand().as_string();
|
||||
let ns = ns.to_raw_string();
|
||||
let db = db.to_raw_string();
|
||||
let sc = sc.to_raw_string();
|
||||
// Attempt to signin to specified scope
|
||||
super::signup::sc(session, ns, db, sc, vars).await
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue