Refactor function dispatch (#125)

This commit is contained in:
Finn Bear 2022-09-20 17:57:33 -07:00 committed by GitHub
parent ffeb56fc7e
commit de7d9299fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 970 additions and 1047 deletions

View file

@ -1,83 +1,215 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use crate::sql::{Number, Strand};
pub enum Args { /// Implemented by types that are commonly used, in a certain way, as arguments.
None, pub trait FromArg: Sized {
Any, /// Potentially fallible conversion from a Value to an argument. Errors will be propagated
One, /// to the caller, although it is also possible to return a none/null Value.
Two, fn from_arg(arg: Value) -> Result<Self, Error>;
Three,
NoneOne,
NoneTwo,
NoneOneTwo,
OneTwo,
} }
pub fn check( impl FromArg for Value {
ctx: &Context, fn from_arg(arg: Value) -> Result<Self, Error> {
name: &str, Ok(arg)
args: Vec<Value>, }
size: Args, }
func: fn(&Context, Vec<Value>) -> Result<Value, Error>,
) -> Result<Value, Error> { impl FromArg for String {
match size { fn from_arg(arg: Value) -> Result<Self, Error> {
Args::None => match args.len() { Ok(arg.as_string())
0 => func(ctx, args), }
_ => Err(Error::InvalidArguments { }
name: name.to_owned(),
message: String::from("The function does not expect any arguments."), impl FromArg for Strand {
}), fn from_arg(arg: Value) -> Result<Self, Error> {
}, Ok(arg.as_strand())
Args::One => match args.len() { }
1 => func(ctx, args), }
_ => Err(Error::InvalidArguments {
name: name.to_owned(), impl FromArg for Number {
message: String::from("The function expects 1 argument."), fn from_arg(arg: Value) -> Result<Self, Error> {
}), Ok(arg.as_number())
}, }
Args::Two => match args.len() { }
2 => func(ctx, args),
_ => Err(Error::InvalidArguments { impl FromArg for f64 {
name: name.to_owned(), fn from_arg(arg: Value) -> Result<Self, Error> {
message: String::from("The function expects 2 arguments."), Ok(arg.as_float())
}), }
}, }
Args::Three => match args.len() {
3 => func(ctx, args), impl FromArg for i64 {
_ => Err(Error::InvalidArguments { fn from_arg(arg: Value) -> Result<Self, Error> {
name: name.to_owned(), Ok(arg.as_int())
message: String::from("The function expects 3 arguments."), }
}), }
},
Args::NoneOne => match args.len() { impl FromArg for usize {
0 | 1 => func(ctx, args), fn from_arg(arg: Value) -> Result<Self, Error> {
_ => Err(Error::InvalidArguments { Ok(arg.as_int() as usize)
name: name.to_owned(), }
message: String::from("The function expects 0 or 1 arguments."), }
}),
}, pub trait FromArgs: Sized {
Args::NoneTwo => match args.len() { /// Convert a collection of argument values into a certain argument format, failing if there are
0 | 2 => func(ctx, args), /// too many or too few arguments, or if one of the arguments could not be converted.
_ => Err(Error::InvalidArguments { fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error>;
name: name.to_owned(), }
message: String::from("The function expects 0 or 2 arguments."),
}), // Take ownership of the raw arguments collection, and assume responsibility of validating the
}, // number of arguments and converting them as necessary.
Args::NoneOneTwo => match args.len() { impl FromArgs for Vec<Value> {
0 | 1 | 2 => func(ctx, args), fn from_args(_name: &str, args: Vec<Value>) -> Result<Self, Error> {
_ => Err(Error::InvalidArguments { Ok(args)
name: name.to_owned(), }
message: String::from("The function expects 0, 1, or 2 arguments."), }
}),
}, // Some functions take a fixed number of arguments.
Args::OneTwo => match args.len() { // The len must match the number of type idents that follow.
1 | 2 => func(ctx, args), macro_rules! impl_tuple {
_ => Err(Error::InvalidArguments { ($len:expr, $( $T:ident ),*) => {
name: name.to_owned(), impl<$($T:FromArg),*> FromArgs for ($($T,)*) {
message: String::from("The function expects 1 or 2 arguments."), #[allow(non_snake_case)]
}), fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
}, let [$($T),*]: [Value; $len] = args.try_into().map_err(|_| Error::InvalidArguments {
Args::Any => func(ctx, args), name: name.to_owned(),
// This match will be optimized away.
message: match $len {
0 => String::from("Expected no arguments."),
1 => String::from("Expected 1 argument."),
_ => format!("Expected {} arguments.", $len),
}
})?;
Ok(($($T::from_arg($T)?,)*))
}
}
}
}
// It is possible to add larger sequences to support higher quantities of fixed arguments.
impl_tuple!(0,);
impl_tuple!(1, A);
impl_tuple!(2, A, B);
impl_tuple!(3, A, B, C);
// Some functions take a single, optional argument, or no arguments at all.
impl<A: FromArg> FromArgs for (Option<A>,) {
fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
let err = || Error::InvalidArguments {
name: name.to_owned(),
message: String::from("Expected 0 or 1 arguments."),
};
let mut args = args.into_iter();
let a = match args.next() {
Some(a) => Some(A::from_arg(a)?),
None => None,
};
if args.next().is_some() {
// Too many.
return Err(err());
}
Ok((a,))
}
}
// Some functions take 1 or 2 arguments, so the second argument is optional.
impl<A: FromArg, B: FromArg> FromArgs for (A, Option<B>) {
fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
let err = || Error::InvalidArguments {
name: name.to_owned(),
message: String::from("Expected 1 or 2 arguments."),
};
let mut args = args.into_iter();
let a = A::from_arg(args.next().ok_or_else(err)?)?;
let b = match args.next() {
Some(b) => Some(B::from_arg(b)?),
None => None,
};
if args.next().is_some() {
// Too many.
return Err(err());
}
Ok((a, b))
}
}
// Some functions take 0, 1, or 2 arguments, so both arguments are optional.
// It is safe to assume that, if the first argument is None, the second argument will also be None.
impl<A: FromArg, B: FromArg> FromArgs for (Option<A>, Option<B>) {
fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
let err = || Error::InvalidArguments {
name: name.to_owned(),
message: String::from("Expected 0, 1, or 2 arguments."),
};
let mut args = args.into_iter();
let a = match args.next() {
Some(a) => Some(A::from_arg(a)?),
None => None,
};
let b = match args.next() {
Some(b) => Some(B::from_arg(b)?),
None => None,
};
if args.next().is_some() {
// Too many.
return Err(err());
}
Ok((a, b))
}
}
// Some functions optionally take 2 arguments, or don't take any at all.
impl<A: FromArg, B: FromArg> FromArgs for (Option<(A, B)>,) {
fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
let err = || Error::InvalidArguments {
name: name.to_owned(),
message: String::from("Expected 0 or 2 arguments."),
};
let mut args = args.into_iter();
let a = match args.next() {
Some(a) => Some(A::from_arg(a)?),
None => None,
};
let b = match args.next() {
Some(b) => Some(B::from_arg(b)?),
None => None,
};
if a.is_some() != b.is_some() || args.next().is_some() {
// One argument, or too many arguments.
return Err(err());
}
Ok((a.zip(b),))
}
}
// Some functions take 1, 2, or 3 arguments. It is safe to assume that, if the second argument is
// None, the third argument will also be None.
impl<A: FromArg, B: FromArg, C: FromArg> FromArgs for (A, Option<B>, Option<C>) {
fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
let err = || Error::InvalidArguments {
name: name.to_owned(),
message: String::from("Expected 1, 2, or 3 arguments."),
};
let mut args = args.into_iter();
let a = A::from_arg(args.next().ok_or_else(err)?)?;
let b = match args.next() {
Some(b) => Some(B::from_arg(b)?),
None => None,
};
let c = match args.next() {
Some(c) => Some(C::from_arg(c)?),
None => None,
};
if args.next().is_some() {
// Too many.
return Err(err());
}
Ok((a, b, c))
} }
} }

View file

@ -1,4 +1,3 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::array::Combine; use crate::sql::array::Combine;
use crate::sql::array::Concat; use crate::sql::array::Concat;
@ -8,9 +7,9 @@ use crate::sql::array::Union;
use crate::sql::array::Uniq; use crate::sql::array::Uniq;
use crate::sql::value::Value; use crate::sql::value::Value;
pub fn concat(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn concat((left, right): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match left {
Value::Array(v) => match args.remove(0) { Value::Array(v) => match right {
Value::Array(w) => Ok(v.concat(w).into()), Value::Array(w) => Ok(v.concat(w).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
}, },
@ -18,9 +17,9 @@ pub fn concat(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn combine(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn combine((left, right): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match left {
Value::Array(v) => match args.remove(0) { Value::Array(v) => match right {
Value::Array(w) => Ok(v.combine(w).into()), Value::Array(w) => Ok(v.combine(w).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
}, },
@ -28,9 +27,9 @@ pub fn combine(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn difference(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn difference((left, right): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match left {
Value::Array(v) => match args.remove(0) { Value::Array(v) => match right {
Value::Array(w) => Ok(v.difference(w).into()), Value::Array(w) => Ok(v.difference(w).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
}, },
@ -38,16 +37,16 @@ pub fn difference(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn distinct(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn distinct((arg,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match arg {
Value::Array(v) => Ok(v.uniq().into()), Value::Array(v) => Ok(v.uniq().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn intersect(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn intersect((left, right): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match left {
Value::Array(v) => match args.remove(0) { Value::Array(v) => match right {
Value::Array(w) => Ok(v.intersect(w).into()), Value::Array(w) => Ok(v.intersect(w).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
}, },
@ -55,34 +54,33 @@ pub fn intersect(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn len(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn len((arg,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match arg {
Value::Array(v) => Ok(v.len().into()), Value::Array(v) => Ok(v.len().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn sort(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn sort((array, order): (Value, Option<Value>)) -> Result<Value, Error> {
match args.len() { match array {
2 => match args.remove(0) { Value::Array(mut v) => match order {
Value::Array(mut v) => match args.remove(0) {
// If "asc", sort ascending // If "asc", sort ascending
Value::Strand(s) if s.as_str() == "asc" => { Some(Value::Strand(s)) if s.as_str() == "asc" => {
v.sort_unstable_by(|a, b| a.cmp(b)); v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into()) Ok(v.into())
} }
// If "desc", sort descending // If "desc", sort descending
Value::Strand(s) if s.as_str() == "desc" => { Some(Value::Strand(s)) if s.as_str() == "desc" => {
v.sort_unstable_by(|a, b| b.cmp(a)); v.sort_unstable_by(|a, b| b.cmp(a));
Ok(v.into()) Ok(v.into())
} }
// If true, sort ascending // If true, sort ascending
Value::True => { Some(Value::True) => {
v.sort_unstable_by(|a, b| a.cmp(b)); v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into()) Ok(v.into())
} }
// If false, sort descending // If false, sort descending
Value::False => { Some(Value::False) => {
v.sort_unstable_by(|a, b| b.cmp(a)); v.sort_unstable_by(|a, b| b.cmp(a));
Ok(v.into()) Ok(v.into())
} }
@ -93,21 +91,12 @@ pub fn sort(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
}, },
v => Ok(v), v => Ok(v),
},
1 => match args.remove(0) {
Value::Array(mut v) => {
v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into())
}
v => Ok(v),
},
_ => unreachable!(),
} }
} }
pub fn union(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn union((left, right): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match left {
Value::Array(v) => match args.remove(0) { Value::Array(v) => match right {
Value::Array(w) => Ok(v.union(w).into()), Value::Array(w) => Ok(v.union(w).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
}, },
@ -117,12 +106,11 @@ pub fn union(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
pub mod sort { pub mod sort {
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
pub fn asc(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn asc((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(mut v) => { Value::Array(mut v) => {
v.sort_unstable_by(|a, b| a.cmp(b)); v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into()) Ok(v.into())
@ -131,8 +119,8 @@ pub mod sort {
} }
} }
pub fn desc(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn desc((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(mut v) => { Value::Array(mut v) => {
v.sort_unstable_by(|a, b| b.cmp(a)); v.sort_unstable_by(|a, b| b.cmp(a));
Ok(v.into()) Ok(v.into())

View file

@ -1,14 +1,11 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
pub fn count(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn count((arg,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { Ok(arg
1 => match args.remove(0) { .map(|val| match val {
Value::Array(v) => Ok(v.iter().filter(|v| v.is_truthy()).count().into()), Value::Array(v) => v.iter().filter(|v| v.is_truthy()).count().into(),
v => Ok((v.is_truthy() as i64).into()), v => (v.is_truthy() as i64).into(),
}, })
0 => Ok(1.into()), .unwrap_or_else(|| 1.into()))
_ => unreachable!(),
}
} }

View file

@ -1,4 +1,3 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use md5::Digest; use md5::Digest;
@ -7,33 +6,33 @@ use sha1::Sha1;
use sha2::Sha256; use sha2::Sha256;
use sha2::Sha512; use sha2::Sha512;
pub fn md5(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn md5((arg,): (String,)) -> Result<Value, Error> {
let mut hasher = Md5::new(); let mut hasher = Md5::new();
hasher.update(args.remove(0).as_string().as_str()); hasher.update(arg.as_str());
let val = hasher.finalize(); let val = hasher.finalize();
let val = format!("{:x}", val); let val = format!("{:x}", val);
Ok(val.into()) Ok(val.into())
} }
pub fn sha1(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn sha1((arg,): (String,)) -> Result<Value, Error> {
let mut hasher = Sha1::new(); let mut hasher = Sha1::new();
hasher.update(args.remove(0).as_string().as_str()); hasher.update(arg.as_str());
let val = hasher.finalize(); let val = hasher.finalize();
let val = format!("{:x}", val); let val = format!("{:x}", val);
Ok(val.into()) Ok(val.into())
} }
pub fn sha256(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn sha256((arg,): (String,)) -> Result<Value, Error> {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(args.remove(0).as_string().as_str()); hasher.update(arg.as_str());
let val = hasher.finalize(); let val = hasher.finalize();
let val = format!("{:x}", val); let val = format!("{:x}", val);
Ok(val.into()) Ok(val.into())
} }
pub fn sha512(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn sha512((arg,): (String,)) -> Result<Value, Error> {
let mut hasher = Sha512::new(); let mut hasher = Sha512::new();
hasher.update(args.remove(0).as_string().as_str()); hasher.update(arg.as_str());
let val = hasher.finalize(); let val = hasher.finalize();
let val = format!("{:x}", val); let val = format!("{:x}", val);
Ok(val.into()) Ok(val.into())
@ -81,7 +80,6 @@ macro_rules! bounded_verify_password {
pub mod argon2 { pub mod argon2 {
use super::COST_ALLOWANCE; use super::COST_ALLOWANCE;
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use argon2::{ use argon2::{
@ -90,9 +88,7 @@ pub mod argon2 {
}; };
use rand::rngs::OsRng; use rand::rngs::OsRng;
pub fn cmp(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn cmp((hash, pass): (String, String)) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap();
let [hash, pass] = args.map(Value::as_string);
type Params<'a> = <Argon2<'a> as PasswordHasher>::Params; type Params<'a> = <Argon2<'a> as PasswordHasher>::Params;
Ok(PasswordHash::new(&hash) Ok(PasswordHash::new(&hash)
.ok() .ok()
@ -107,9 +103,8 @@ pub mod argon2 {
.into()) .into())
} }
pub fn gen(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn gen((pass,): (String,)) -> Result<Value, Error> {
let algo = Argon2::default(); let algo = Argon2::default();
let pass = args.remove(0).as_string();
let salt = SaltString::generate(&mut OsRng); let salt = SaltString::generate(&mut OsRng);
let hash = algo.hash_password(pass.as_ref(), salt.as_ref()).unwrap().to_string(); let hash = algo.hash_password(pass.as_ref(), salt.as_ref()).unwrap().to_string();
Ok(hash.into()) Ok(hash.into())
@ -119,7 +114,6 @@ pub mod argon2 {
pub mod pbkdf2 { pub mod pbkdf2 {
use super::COST_ALLOWANCE; use super::COST_ALLOWANCE;
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use pbkdf2::{ use pbkdf2::{
@ -128,9 +122,7 @@ pub mod pbkdf2 {
}; };
use rand::rngs::OsRng; use rand::rngs::OsRng;
pub fn cmp(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn cmp((hash, pass): (String, String)) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap();
let [hash, pass] = args.map(Value::as_string);
type Params = <Pbkdf2 as PasswordHasher>::Params; type Params = <Pbkdf2 as PasswordHasher>::Params;
Ok(PasswordHash::new(&hash) Ok(PasswordHash::new(&hash)
.ok() .ok()
@ -147,8 +139,7 @@ pub mod pbkdf2 {
.into()) .into())
} }
pub fn gen(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn gen((pass,): (String,)) -> Result<Value, Error> {
let pass = args.remove(0).as_string();
let salt = SaltString::generate(&mut OsRng); let salt = SaltString::generate(&mut OsRng);
let hash = Pbkdf2.hash_password(pass.as_ref(), salt.as_ref()).unwrap().to_string(); let hash = Pbkdf2.hash_password(pass.as_ref(), salt.as_ref()).unwrap().to_string();
Ok(hash.into()) Ok(hash.into())
@ -157,7 +148,6 @@ pub mod pbkdf2 {
pub mod scrypt { pub mod scrypt {
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use rand::rngs::OsRng; use rand::rngs::OsRng;
@ -166,9 +156,7 @@ pub mod scrypt {
Scrypt, Scrypt,
}; };
pub fn cmp(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn cmp((hash, pass): (String, String)) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap();
let [hash, pass] = args.map(Value::as_string);
type Params = <Scrypt as PasswordHasher>::Params; type Params = <Scrypt as PasswordHasher>::Params;
Ok(PasswordHash::new(&hash) Ok(PasswordHash::new(&hash)
.ok() .ok()
@ -184,8 +172,7 @@ pub mod scrypt {
.into()) .into())
} }
pub fn gen(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn gen((pass,): (String,)) -> Result<Value, Error> {
let pass = args.remove(0).as_string();
let salt = SaltString::generate(&mut OsRng); let salt = SaltString::generate(&mut OsRng);
let hash = Scrypt.hash_password(pass.as_ref(), salt.as_ref()).unwrap().to_string(); let hash = Scrypt.hash_password(pass.as_ref(), salt.as_ref()).unwrap().to_string();
Ok(hash.into()) Ok(hash.into())
@ -194,19 +181,15 @@ pub mod scrypt {
pub mod bcrypt { pub mod bcrypt {
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use bcrypt; use bcrypt;
pub fn cmp(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn cmp((hash, pass): (String, String)) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap();
let [hash, pass] = args.map(Value::as_string);
Ok(bcrypt::verify(pass, &hash).unwrap_or(false).into()) Ok(bcrypt::verify(pass, &hash).unwrap_or(false).into())
} }
pub fn gen(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn gen((pass,): (String,)) -> Result<Value, Error> {
let pass = args.remove(0).as_string();
let hash = bcrypt::hash(pass, bcrypt::DEFAULT_COST).unwrap(); let hash = bcrypt::hash(pass, bcrypt::DEFAULT_COST).unwrap();
Ok(hash.into()) Ok(hash.into())
} }

View file

@ -1,4 +1,3 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::geometry::Geometry; use crate::sql::geometry::Geometry;
use crate::sql::value::Value; use crate::sql::value::Value;
@ -7,8 +6,8 @@ use geo::algorithm::bearing::Bearing;
use geo::algorithm::centroid::Centroid; use geo::algorithm::centroid::Centroid;
use geo::algorithm::haversine_distance::HaversineDistance; use geo::algorithm::haversine_distance::HaversineDistance;
pub fn area(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn area((arg,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match arg {
Value::Geometry(v) => match v { Value::Geometry(v) => match v {
Geometry::Point(v) => Ok(v.signed_area().into()), Geometry::Point(v) => Ok(v.signed_area().into()),
Geometry::Line(v) => Ok(v.signed_area().into()), Geometry::Line(v) => Ok(v.signed_area().into()),
@ -24,9 +23,9 @@ pub fn area(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn bearing(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn bearing((a, b): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match a {
Value::Geometry(Geometry::Point(v)) => match args.remove(0) { Value::Geometry(Geometry::Point(v)) => match b {
Value::Geometry(Geometry::Point(w)) => Ok(v.bearing(w).into()), Value::Geometry(Geometry::Point(w)) => Ok(v.bearing(w).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
}, },
@ -34,8 +33,8 @@ pub fn bearing(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn centroid(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn centroid((arg,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match arg {
Value::Geometry(v) => match v { Value::Geometry(v) => match v {
Geometry::Point(v) => Ok(v.centroid().into()), Geometry::Point(v) => Ok(v.centroid().into()),
Geometry::Line(v) => match v.centroid() { Geometry::Line(v) => match v.centroid() {
@ -69,9 +68,9 @@ pub fn centroid(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn distance(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn distance((from, to): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match from {
Value::Geometry(Geometry::Point(v)) => match args.remove(0) { Value::Geometry(Geometry::Point(v)) => match to {
Value::Geometry(Geometry::Point(w)) => Ok(v.haversine_distance(&w).into()), Value::Geometry(Geometry::Point(w)) => Ok(v.haversine_distance(&w).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
}, },
@ -81,34 +80,29 @@ pub fn distance(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
pub mod hash { pub mod hash {
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::fnc::util::geo; use crate::fnc::util::geo;
use crate::sql::geometry::Geometry; use crate::sql::geometry::Geometry;
use crate::sql::value::Value; use crate::sql::value::Value;
pub fn encode(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn encode((point, len): (Value, Option<usize>)) -> Result<Value, Error> {
match args.len() { let len = match len {
2 => match args.remove(0) { Some(len) if (1..=12).contains(&len) => len,
Value::Geometry(Geometry::Point(v)) => match args.remove(0).as_int() { None => 12,
l if l > 0 && l <= 12 => Ok(geo::encode(v, l as usize).into()), _ => return Err(Error::InvalidArguments {
_ => Err(Error::InvalidArguments {
name: String::from("geo::encode"), name: String::from("geo::encode"),
message: String::from("The second argument must be an integer greater than 0 and less than or equal to 12."), message: String::from("The second argument must be an integer greater than 0 and less than or equal to 12."),
}), })
}, };
_ => Ok(Value::None),
}, Ok(match point {
1 => match args.remove(0) { Value::Geometry(Geometry::Point(v)) => geo::encode(v, len).into(),
Value::Geometry(Geometry::Point(v)) => Ok(geo::encode(v, 12).into()), _ => Value::None,
_ => Ok(Value::None), })
},
_ => unreachable!(),
}
} }
pub fn decode(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn decode((arg,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match arg {
Value::Strand(v) => Ok(geo::decode(v).into()), Value::Strand(v) => Ok(geo::decode(v).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }

View file

@ -1,234 +1,156 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use crate::sql::Strand;
#[cfg(not(feature = "http"))] #[cfg(not(feature = "http"))]
pub async fn head(_: &Context<'_>, _: Vec<Value>) -> Result<Value, Error> { pub async fn head((_, _): (Value, Option<Value>)) -> Result<Value, Error> {
Err(Error::HttpDisabled) Err(Error::HttpDisabled)
} }
#[cfg(not(feature = "http"))] #[cfg(not(feature = "http"))]
pub async fn get(_: &Context<'_>, _: Vec<Value>) -> Result<Value, Error> { pub async fn get((_, _): (Value, Option<Value>)) -> Result<Value, Error> {
Err(Error::HttpDisabled) Err(Error::HttpDisabled)
} }
#[cfg(not(feature = "http"))] #[cfg(not(feature = "http"))]
pub async fn put(_: &Context<'_>, _: Vec<Value>) -> Result<Value, Error> { pub async fn put((_, _, _): (Value, Option<Value>, Option<Value>)) -> Result<Value, Error> {
Err(Error::HttpDisabled) Err(Error::HttpDisabled)
} }
#[cfg(not(feature = "http"))] #[cfg(not(feature = "http"))]
pub async fn post(_: &Context<'_>, _: Vec<Value>) -> Result<Value, Error> { pub async fn post((_, _, _): (Value, Option<Value>, Option<Value>)) -> Result<Value, Error> {
Err(Error::HttpDisabled) Err(Error::HttpDisabled)
} }
#[cfg(not(feature = "http"))] #[cfg(not(feature = "http"))]
pub async fn patch(_: &Context<'_>, _: Vec<Value>) -> Result<Value, Error> { pub async fn patch((_, _, _): (Value, Option<Value>, Option<Value>)) -> Result<Value, Error> {
Err(Error::HttpDisabled) Err(Error::HttpDisabled)
} }
#[cfg(not(feature = "http"))] #[cfg(not(feature = "http"))]
pub async fn delete(_: &Context<'_>, _: Vec<Value>) -> Result<Value, Error> { pub async fn delete((_, _): (Value, Option<Value>)) -> Result<Value, Error> {
Err(Error::HttpDisabled) Err(Error::HttpDisabled)
} }
fn try_as_uri(fn_name: &str, value: Value) -> Result<Strand, Error> {
match value {
Value::Strand(uri) => Ok(uri),
_ => Err(Error::InvalidArguments {
name: fn_name.to_owned(),
// Assumption is that URI is first argument.
message: String::from("The first argument should be a string."),
}),
}
}
#[cfg(feature = "http")] #[cfg(feature = "http")]
pub async fn head(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> { pub async fn head((uri, opts): (Value, Option<Value>)) -> Result<Value, Error> {
match args.len() { let uri = try_as_uri("http::head", uri)?;
2 => match args.remove(0) {
Value::Strand(uri) => match args.remove(0) { let opts = match opts {
Value::Object(opt) => crate::fnc::util::http::head(uri, opt).await, Some(Value::Object(opts)) => Some(opts),
_ => Err(Error::InvalidArguments { None => None,
Some(_) => {
return Err(Error::InvalidArguments {
name: String::from("http::head"), name: String::from("http::head"),
message: String::from("The second argument should be an object."), message: String::from("The second argument should be an object."),
}), })
},
_ => Err(Error::InvalidArguments {
name: String::from("http::head"),
message: String::from("The first argument should be a string."),
}),
},
1 => match args.remove(0) {
Value::Strand(uri) => crate::fnc::util::http::head(uri, None).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::head"),
message: String::from("The first argument should be a string."),
}),
},
_ => Err(Error::InvalidArguments {
name: String::from("http::head"),
message: String::from("The function expects 1 or 2 arguments."),
}),
} }
};
crate::fnc::util::http::head(uri, opts).await
} }
#[cfg(feature = "http")] #[cfg(feature = "http")]
pub async fn get(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> { pub async fn get((uri, opts): (Value, Option<Value>)) -> Result<Value, Error> {
match args.len() { let uri = try_as_uri("http::get", uri)?;
2 => match args.remove(0) {
Value::Strand(uri) => match args.remove(0) { let opts = match opts {
Value::Object(opt) => crate::fnc::util::http::get(uri, opt).await, Some(Value::Object(opts)) => Some(opts),
_ => Err(Error::InvalidArguments { None => None,
Some(_) => {
return Err(Error::InvalidArguments {
name: String::from("http::get"), name: String::from("http::get"),
message: String::from("The second argument should be an object."), message: String::from("The second argument should be an object."),
}), })
},
_ => Err(Error::InvalidArguments {
name: String::from("http::get"),
message: String::from("The first argument should be a string."),
}),
},
1 => match args.remove(0) {
Value::Strand(uri) => crate::fnc::util::http::get(uri, None).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::get"),
message: String::from("The first argument should be a string."),
}),
},
_ => Err(Error::InvalidArguments {
name: String::from("http::get"),
message: String::from("The function expects 1 or 2 arguments."),
}),
} }
};
crate::fnc::util::http::get(uri, opts).await
} }
#[cfg(feature = "http")] #[cfg(feature = "http")]
pub async fn put(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> { pub async fn put((uri, body, opts): (Value, Option<Value>, Option<Value>)) -> Result<Value, Error> {
match args.len() { let uri = try_as_uri("http::put", uri)?;
3 => match (args.remove(0), args.remove(0)) {
(Value::Strand(uri), val) => match args.remove(0) { let opts = match opts {
Value::Object(opts) => crate::fnc::util::http::put(uri, val, opts).await, Some(Value::Object(opts)) => Some(opts),
_ => Err(Error::InvalidArguments { None => None,
Some(_) => {
return Err(Error::InvalidArguments {
name: String::from("http::put"), name: String::from("http::put"),
message: String::from("The third argument should be an object."), message: String::from("The third argument should be an object."),
}), })
},
_ => Err(Error::InvalidArguments {
name: String::from("http::put"),
message: String::from("The first argument should be a string."),
}),
},
2 => match (args.remove(0), args.remove(0)) {
(Value::Strand(uri), val) => crate::fnc::util::http::put(uri, val, None).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::put"),
message: String::from("The first argument should be a string."),
}),
},
1 => match args.remove(0) {
Value::Strand(uri) => crate::fnc::util::http::put(uri, Value::Null, None).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::put"),
message: String::from("The first argument should be a string."),
}),
},
_ => Err(Error::InvalidArguments {
name: String::from("http::put"),
message: String::from("The function expects 1, 2, or 3 arguments."),
}),
} }
};
crate::fnc::util::http::put(uri, body.unwrap_or(Value::Null), opts).await
} }
#[cfg(feature = "http")] #[cfg(feature = "http")]
pub async fn post(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> { pub async fn post(
match args.len() { (uri, body, opts): (Value, Option<Value>, Option<Value>),
3 => match (args.remove(0), args.remove(0)) { ) -> Result<Value, Error> {
(Value::Strand(uri), val) => match args.remove(0) { let uri = try_as_uri("http::post", uri)?;
Value::Object(opts) => crate::fnc::util::http::post(uri, val, opts).await,
_ => Err(Error::InvalidArguments { let opts = match opts {
Some(Value::Object(opts)) => Some(opts),
None => None,
Some(_) => {
return Err(Error::InvalidArguments {
name: String::from("http::post"), name: String::from("http::post"),
message: String::from("The third argument should be an object."), message: String::from("The third argument should be an object."),
}), })
},
_ => Err(Error::InvalidArguments {
name: String::from("http::post"),
message: String::from("The first argument should be a string."),
}),
},
2 => match (args.remove(0), args.remove(0)) {
(Value::Strand(uri), val) => crate::fnc::util::http::post(uri, val, None).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::post"),
message: String::from("The first argument should be a string."),
}),
},
1 => match args.remove(0) {
Value::Strand(uri) => crate::fnc::util::http::post(uri, Value::Null, None).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::post"),
message: String::from("The first argument should be a string."),
}),
},
_ => Err(Error::InvalidArguments {
name: String::from("http::post"),
message: String::from("The function expects 1, 2, or 3 arguments."),
}),
} }
};
crate::fnc::util::http::post(uri, body.unwrap_or(Value::Null), opts).await
} }
#[cfg(feature = "http")] #[cfg(feature = "http")]
pub async fn patch(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> { pub async fn patch(
match args.len() { (uri, body, opts): (Value, Option<Value>, Option<Value>),
3 => match (args.remove(0), args.remove(0)) { ) -> Result<Value, Error> {
(Value::Strand(uri), val) => match args.remove(0) { let uri = try_as_uri("http::patch", uri)?;
Value::Object(opts) => crate::fnc::util::http::patch(uri, val, opts).await,
_ => Err(Error::InvalidArguments { let opts = match opts {
Some(Value::Object(opts)) => Some(opts),
None => None,
Some(_) => {
return Err(Error::InvalidArguments {
name: String::from("http::patch"), name: String::from("http::patch"),
message: String::from("The third argument should be an object."), message: String::from("The third argument should be an object."),
}), })
},
_ => Err(Error::InvalidArguments {
name: String::from("http::patch"),
message: String::from("The first argument should be a string."),
}),
},
2 => match (args.remove(0), args.remove(0)) {
(Value::Strand(uri), val) => crate::fnc::util::http::patch(uri, val, None).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::patch"),
message: String::from("The first argument should be a string."),
}),
},
1 => match args.remove(0) {
Value::Strand(uri) => crate::fnc::util::http::patch(uri, Value::Null, None).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::patch"),
message: String::from("The first argument should be a string."),
}),
},
_ => Err(Error::InvalidArguments {
name: String::from("http::patch"),
message: String::from("The function expects 1, 2, or 3 arguments."),
}),
} }
};
crate::fnc::util::http::patch(uri, body.unwrap_or(Value::Null), opts).await
} }
#[cfg(feature = "http")] #[cfg(feature = "http")]
pub async fn delete(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> { pub async fn delete((uri, opts): (Value, Option<Value>)) -> Result<Value, Error> {
match args.len() { let uri = try_as_uri("http::delete", uri)?;
2 => match args.remove(0) {
Value::Strand(uri) => match args.remove(0) { let opts = match opts {
Value::Object(opt) => crate::fnc::util::http::delete(uri, opt).await, Some(Value::Object(opts)) => Some(opts),
_ => Err(Error::InvalidArguments { None => None,
Some(_) => {
return Err(Error::InvalidArguments {
name: String::from("http::delete"), name: String::from("http::delete"),
message: String::from("The second argument should be an object."), message: String::from("The second argument should be an object."),
}), })
},
_ => Err(Error::InvalidArguments {
name: String::from("http::delete"),
message: String::from("The first argument should be a string."),
}),
},
1 => match args.remove(0) {
Value::Strand(uri) => crate::fnc::util::http::delete(uri, None).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::delete"),
message: String::from("The first argument should be a string."),
}),
},
_ => Err(Error::InvalidArguments {
name: String::from("http::delete"),
message: String::from("The function expects 1 or 2 arguments."),
}),
} }
};
crate::fnc::util::http::delete(uri, opts).await
} }

View file

@ -1,4 +1,3 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -11,58 +10,58 @@ use uuid::Uuid;
#[rustfmt::skip] static LONGITUDE_RE: Lazy<Regex> = Lazy::new(|| Regex::new("^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$").unwrap()); #[rustfmt::skip] static LONGITUDE_RE: Lazy<Regex> = Lazy::new(|| Regex::new("^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$").unwrap());
#[inline] #[inline]
pub fn alphanum(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn alphanum((arg,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().chars().all(char::is_alphanumeric).into()) Ok(arg.chars().all(char::is_alphanumeric).into())
} }
#[inline] #[inline]
pub fn alpha(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn alpha((arg,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().chars().all(char::is_alphabetic).into()) Ok(arg.chars().all(char::is_alphabetic).into())
} }
#[inline] #[inline]
pub fn ascii(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn ascii((arg,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().is_ascii().into()) Ok(arg.is_ascii().into())
} }
#[inline] #[inline]
pub fn domain(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn domain((arg,): (String,)) -> Result<Value, Error> {
Ok(addr::parse_domain_name(args.remove(0).as_string().as_str()).is_ok().into()) Ok(addr::parse_domain_name(arg.as_str()).is_ok().into())
} }
#[inline] #[inline]
pub fn email(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn email((arg,): (String,)) -> Result<Value, Error> {
Ok(addr::parse_email_address(args.remove(0).as_string().as_str()).is_ok().into()) Ok(addr::parse_email_address(arg.as_str()).is_ok().into())
} }
#[inline] #[inline]
pub fn hexadecimal(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn hexadecimal((arg,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().chars().all(|x| char::is_ascii_hexdigit(&x)).into()) Ok(arg.chars().all(|x| char::is_ascii_hexdigit(&x)).into())
} }
#[inline] #[inline]
pub fn latitude(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn latitude((arg,): (String,)) -> Result<Value, Error> {
Ok(LATITUDE_RE.is_match(args.remove(0).as_string().as_str()).into()) Ok(LATITUDE_RE.is_match(arg.as_str()).into())
} }
#[inline] #[inline]
pub fn longitude(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn longitude((arg,): (String,)) -> Result<Value, Error> {
Ok(LONGITUDE_RE.is_match(args.remove(0).as_string().as_str()).into()) Ok(LONGITUDE_RE.is_match(arg.as_str()).into())
} }
#[inline] #[inline]
pub fn numeric(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn numeric((arg,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().chars().all(char::is_numeric).into()) Ok(arg.chars().all(char::is_numeric).into())
} }
#[inline] #[inline]
pub fn semver(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn semver((arg,): (String,)) -> Result<Value, Error> {
Ok(Version::parse(args.remove(0).as_string().as_str()).is_ok().into()) Ok(Version::parse(arg.as_str()).is_ok().into())
} }
#[inline] #[inline]
pub fn uuid(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn uuid((arg,): (Value,)) -> Result<Value, Error> {
Ok(match args.remove(0) { Ok(match arg {
Value::Strand(v) => Uuid::parse_str(v.as_string().as_str()).is_ok().into(), Value::Strand(v) => Uuid::parse_str(v.as_string().as_str()).is_ok().into(),
Value::Uuid(_) => true.into(), Value::Uuid(_) => true.into(),
_ => false.into(), _ => false.into(),
@ -75,104 +74,104 @@ mod tests {
#[test] #[test]
fn alphanum() { fn alphanum() {
let value = super::alphanum(&Default::default(), vec!["abc123".into()]).unwrap(); let value = super::alphanum((String::from("abc123"),)).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let value = super::alphanum(&Default::default(), vec!["y%*".into()]).unwrap(); let value = super::alphanum((String::from("y%*"),)).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn alpha() { fn alpha() {
let value = super::alpha(&Default::default(), vec!["abc".into()]).unwrap(); let value = super::alpha((String::from("abc"),)).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let value = super::alpha(&Default::default(), vec!["1234".into()]).unwrap(); let value = super::alpha((String::from("1234"),)).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn ascii() { fn ascii() {
let value = super::ascii(&Default::default(), vec!["abc".into()]).unwrap(); let value = super::ascii((String::from("abc"),)).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let value = super::ascii(&Default::default(), vec!["中国".into()]).unwrap(); let value = super::ascii((String::from("中国"),)).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn domain() { fn domain() {
let value = super::domain(&Default::default(), vec!["食狮.中国".into()]).unwrap(); let value = super::domain((String::from("食狮.中国"),)).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let value = super::domain(&Default::default(), vec!["example-.com".into()]).unwrap(); let value = super::domain((String::from("example-.com"),)).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn email() { fn email() {
let input = vec!["user@[fd79:cdcb:38cc:9dd:f686:e06d:32f3:c123]".into()]; let input = (String::from("user@[fd79:cdcb:38cc:9dd:f686:e06d:32f3:c123]"),);
let value = super::email(&Default::default(), input).unwrap(); let value = super::email(input).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let input = vec!["john..doe@example.com".into()]; let input = (String::from("john..doe@example.com"),);
let value = super::email(&Default::default(), input).unwrap(); let value = super::email(input).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn hexadecimal() { fn hexadecimal() {
let value = super::hexadecimal(&Default::default(), vec!["00FF00".into()]).unwrap(); let value = super::hexadecimal((String::from("00FF00"),)).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let value = super::hexadecimal(&Default::default(), vec!["SurrealDB".into()]).unwrap(); let value = super::hexadecimal((String::from("SurrealDB"),)).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn latitude() { fn latitude() {
let value = super::latitude(&Default::default(), vec!["-0.118092".into()]).unwrap(); let value = super::latitude((String::from("-0.118092"),)).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let value = super::latitude(&Default::default(), vec![12345.into()]).unwrap(); let value = super::latitude((String::from("12345"),)).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn longitude() { fn longitude() {
let value = super::longitude(&Default::default(), vec!["51.509865".into()]).unwrap(); let value = super::longitude((String::from("51.509865"),)).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let value = super::longitude(&Default::default(), vec![12345.into()]).unwrap(); let value = super::longitude((String::from("12345"),)).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn numeric() { fn numeric() {
let value = super::numeric(&Default::default(), vec![12345.into()]).unwrap(); let value = super::numeric((String::from("12345"),)).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let value = super::numeric(&Default::default(), vec!["abcde".into()]).unwrap(); let value = super::numeric((String::from("abcde"),)).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn semver() { fn semver() {
let value = super::semver(&Default::default(), vec!["1.0.0".into()]).unwrap(); let value = super::semver((String::from("1.0.0"),)).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let value = super::semver(&Default::default(), vec!["1.0".into()]).unwrap(); let value = super::semver((String::from("1.0"),)).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
#[test] #[test]
fn uuid() { fn uuid() {
let input = vec!["123e4567-e89b-12d3-a456-426614174000".into()]; let input = (String::from("123e4567-e89b-12d3-a456-426614174000").into(),);
let value = super::uuid(&Default::default(), input).unwrap(); let value = super::uuid(input).unwrap();
assert_eq!(value, Value::True); assert_eq!(value, Value::True);
let input = vec!["foo-bar".into()]; let input = (String::from("foo-bar").into(),);
let value = super::uuid(&Default::default(), input).unwrap(); let value = super::uuid(input).unwrap();
assert_eq!(value, Value::False); assert_eq!(value, Value::False);
} }
} }

View file

@ -1,4 +1,3 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::fnc::util::math::bottom::Bottom; use crate::fnc::util::math::bottom::Bottom;
use crate::fnc::util::math::deviation::Deviation; use crate::fnc::util::math::deviation::Deviation;
@ -16,48 +15,45 @@ use crate::fnc::util::math::variance::Variance;
use crate::sql::number::Number; use crate::sql::number::Number;
use crate::sql::value::Value; use crate::sql::value::Value;
pub fn abs(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn abs((arg,): (Number,)) -> Result<Value, Error> {
Ok(args.remove(0).as_number().abs().into()) Ok(arg.abs().into())
} }
pub fn bottom(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn bottom((array, c): (Value, i64)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => { Value::Array(v) => Ok(v.as_numbers().bottom(c).into()),
let c = args.remove(0).as_int();
Ok(v.as_numbers().bottom(c).into())
}
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn ceil(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn ceil((arg,): (Number,)) -> Result<Value, Error> {
Ok(args.remove(0).as_number().ceil().into()) Ok(arg.ceil().into())
} }
pub fn fixed(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn fixed((v, p): (Number, i64)) -> Result<Value, Error> {
let v = args.remove(0); if p > 0 {
match args.remove(0).as_int() { Ok(v.fixed(p as usize).into())
p if p > 0 => Ok(v.as_number().fixed(p as usize).into()), } else {
_ => Err(Error::InvalidArguments { Err(Error::InvalidArguments {
name: String::from("math::fixed"), name: String::from("math::fixed"),
message: String::from("The second argument must be an integer greater than 0."), message: String::from("The second argument must be an integer greater than 0."),
}), })
} }
} }
pub fn floor(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn floor((arg,): (Number,)) -> Result<Value, Error> {
Ok(args.remove(0).as_number().floor().into()) Ok(arg.floor().into())
} }
pub fn interquartile(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn interquartile((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().interquartile().into()), Value::Array(v) => Ok(v.as_numbers().interquartile().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn max(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn max((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => match v.as_numbers().into_iter().max() { Value::Array(v) => match v.as_numbers().into_iter().max() {
Some(v) => Ok(v.into()), Some(v) => Ok(v.into()),
None => Ok(Value::None), None => Ok(Value::None),
@ -66,8 +62,8 @@ pub fn max(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn mean(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn mean((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => match v.is_empty() { Value::Array(v) => match v.is_empty() {
true => Ok(Value::None), true => Ok(Value::None),
false => Ok(v.as_numbers().mean().into()), false => Ok(v.as_numbers().mean().into()),
@ -76,8 +72,8 @@ pub fn mean(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn median(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn median((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => match v.is_empty() { Value::Array(v) => match v.is_empty() {
true => Ok(Value::None), true => Ok(Value::None),
false => Ok(v.as_numbers().median().into()), false => Ok(v.as_numbers().median().into()),
@ -86,15 +82,15 @@ pub fn median(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn midhinge(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn midhinge((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().midhinge().into()), Value::Array(v) => Ok(v.as_numbers().midhinge().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn min(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn min((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => match v.as_numbers().into_iter().min() { Value::Array(v) => match v.as_numbers().into_iter().min() {
Some(v) => Ok(v.into()), Some(v) => Ok(v.into()),
None => Ok(Value::None), None => Ok(Value::None),
@ -103,85 +99,82 @@ pub fn min(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn mode(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn mode((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().mode().into()), Value::Array(v) => Ok(v.as_numbers().mode().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn nearestrank(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn nearestrank((array, n): (Value, Number)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().nearestrank(args.remove(0).as_number()).into()), Value::Array(v) => Ok(v.as_numbers().nearestrank(n).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn percentile(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn percentile((array, n): (Value, Number)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().percentile(args.remove(0).as_number()).into()), Value::Array(v) => Ok(v.as_numbers().percentile(n).into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn product(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn product((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().into_iter().product::<Number>().into()), Value::Array(v) => Ok(v.as_numbers().into_iter().product::<Number>().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn round(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn round((arg,): (Number,)) -> Result<Value, Error> {
Ok(args.remove(0).as_number().round().into()) Ok(arg.round().into())
} }
pub fn spread(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn spread((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().spread().into()), Value::Array(v) => Ok(v.as_numbers().spread().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn sqrt(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn sqrt((arg,): (Number,)) -> Result<Value, Error> {
match args.remove(0).as_number() { Ok(match arg {
v if v >= Number::Int(0) => Ok(v.sqrt().into()), v if v >= Number::Int(0) => v.sqrt().into(),
_ => Ok(Value::None), _ => Value::None,
} })
} }
pub fn stddev(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn stddev((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().deviation().into()), Value::Array(v) => Ok(v.as_numbers().deviation().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn sum(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn sum((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().into_iter().sum::<Number>().into()), Value::Array(v) => Ok(v.as_numbers().into_iter().sum::<Number>().into()),
v => Ok(v.as_number().into()), v => Ok(v.as_number().into()),
} }
} }
pub fn top(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn top((array, c): (Value, i64)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => { Value::Array(v) => Ok(v.as_numbers().top(c).into()),
let c = args.remove(0).as_int();
Ok(v.as_numbers().top(c).into())
}
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn trimean(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn trimean((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().trimean().into()), Value::Array(v) => Ok(v.as_numbers().trimean().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn variance(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn variance((array,): (Value,)) -> Result<Value, Error> {
match args.remove(0) { match array {
Value::Array(v) => Ok(v.as_numbers().variance().into()), Value::Array(v) => Ok(v.as_numbers().variance().into()),
_ => Ok(Value::None), _ => Ok(Value::None),
} }

View file

@ -1,16 +1,15 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
pub fn id(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn id((arg,): (Value,)) -> Result<Value, Error> {
Ok(match args.remove(0) { Ok(match arg {
Value::Thing(v) => v.id.into(), Value::Thing(v) => v.id.into(),
_ => Value::None, _ => Value::None,
}) })
} }
pub fn tb(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn tb((arg,): (Value,)) -> Result<Value, Error> {
Ok(match args.remove(0) { Ok(match arg {
Value::Thing(v) => v.tb.into(), Value::Thing(v) => v.tb.into(),
_ => Value::None, _ => Value::None,
}) })

View file

@ -1,6 +1,5 @@
use crate::ctx::Context; use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::fnc::args::Args;
use crate::sql::value::Value; use crate::sql::value::Value;
pub mod args; pub mod args;
@ -24,185 +23,177 @@ pub mod time;
pub mod r#type; pub mod r#type;
pub mod util; 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> { pub async fn run(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Value, Error> {
match name { macro_rules! dispatch {
v if v.starts_with("http") => { ($name: ident, $args: ident, $($function_name: literal => $($function_path: ident)::+ $(($ctx_arg: expr))* $(.$await:tt)*,)+) => {
// HTTP functions are asynchronous {
asynchronous(ctx, name, args).await match $name {
} $($function_name => $($function_path)::+($($ctx_arg,)* args::FromArgs::from_args($name, $args)?)$(.$await)*,)+
_ => { _ => unreachable!()
// Other functions are synchronous }
synchronous(ctx, name, args) }
} }
} }
}
// Attempts to run a synchronous function // Each function is specified by its name (a string literal) followed by its path. The path
pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Value, Error> { // may be followed by one parenthesized argument, e.g. ctx, which is passed to the function
match name { // before the remainder of the arguments. The path may be followed by `.await` to signify that
// it is `async`.
dispatch!(
name,
args,
"array::combine" => array::combine,
"array::concat" => array::concat,
"array::difference" => array::difference,
"array::distinct" => array::distinct,
"array::intersect" => array::intersect,
"array::len" => array::len,
"array::sort" => array::sort,
"array::union" => array::union,
"array::sort::asc" => array::sort::asc,
"array::sort::desc" => array::sort::desc,
// //
"array::combine" => args::check(ctx, name, args, Args::Two, array::combine), "count" => count::count,
"array::concat" => args::check(ctx, name, args, Args::Two, array::concat),
"array::difference" => args::check(ctx, name, args, Args::Two, array::difference),
"array::distinct" => args::check(ctx, name, args, Args::One, array::distinct),
"array::intersect" => args::check(ctx, name, args, Args::Two, array::intersect),
"array::len" => args::check(ctx, name, args, Args::One, array::len),
"array::sort" => args::check(ctx, name, args, Args::OneTwo, array::sort),
"array::union" => args::check(ctx, name, args, Args::Two, array::union),
"array::sort::asc" => args::check(ctx, name, args, Args::One, array::sort::asc),
"array::sort::desc" => args::check(ctx, name, args, Args::One, array::sort::desc),
// //
"count" => args::check(ctx, name, args, Args::NoneOne, count::count), "crypto::md5" => crypto::md5,
"crypto::sha1" => crypto::sha1,
"crypto::sha256" => crypto::sha256,
"crypto::sha512" => crypto::sha512,
"crypto::argon2::compare" => crypto::argon2::cmp,
"crypto::argon2::generate" => crypto::argon2::gen,
"crypto::bcrypt::compare" => crypto::bcrypt::cmp,
"crypto::bcrypt::generate" => crypto::bcrypt::gen,
"crypto::pbkdf2::compare" => crypto::pbkdf2::cmp,
"crypto::pbkdf2::generate" => crypto::pbkdf2::gen,
"crypto::scrypt::compare" => crypto::scrypt::cmp,
"crypto::scrypt::generate" => crypto::scrypt::gen,
// //
"crypto::md5" => args::check(ctx, name, args, Args::One, crypto::md5), "geo::area" => geo::area,
"crypto::sha1" => args::check(ctx, name, args, Args::One, crypto::sha1), "geo::bearing" => geo::bearing,
"crypto::sha256" => args::check(ctx, name, args, Args::One, crypto::sha256), "geo::centroid" => geo::centroid,
"crypto::sha512" => args::check(ctx, name, args, Args::One, crypto::sha512), "geo::distance" => geo::distance,
"crypto::argon2::compare" => args::check(ctx, name, args, Args::Two, crypto::argon2::cmp), "geo::hash::decode" => geo::hash::decode,
"crypto::argon2::generate" => args::check(ctx, name, args, Args::One, crypto::argon2::gen), "geo::hash::encode" => geo::hash::encode,
"crypto::pbkdf2::compare" => args::check(ctx, name, args, Args::Two, crypto::pbkdf2::cmp),
"crypto::pbkdf2::generate" => args::check(ctx, name, args, Args::One, crypto::pbkdf2::gen),
"crypto::scrypt::compare" => args::check(ctx, name, args, Args::Two, crypto::scrypt::cmp),
"crypto::scrypt::generate" => args::check(ctx, name, args, Args::One, crypto::scrypt::gen),
"crypto::bcrypt::compare" => args::check(ctx, name, args, Args::Two, crypto::bcrypt::cmp),
"crypto::bcrypt::generate" => args::check(ctx, name, args, Args::One, crypto::bcrypt::gen),
// //
"geo::area" => args::check(ctx, name, args, Args::One, geo::area), "http::head" => http::head.await,
"geo::bearing" => args::check(ctx, name, args, Args::Two, geo::bearing), "http::get" => http::get.await,
"geo::centroid" => args::check(ctx, name, args, Args::One, geo::centroid), "http::put" => http::put.await,
"geo::distance" => args::check(ctx, name, args, Args::Two, geo::distance), "http::post" => http::post.await,
"geo::hash::decode" => args::check(ctx, name, args, Args::One, geo::hash::decode), "http::patch" => http::patch.await,
"geo::hash::encode" => args::check(ctx, name, args, Args::OneTwo, geo::hash::encode), "http::delete" => http::delete.await,
// //
"is::alphanum" => args::check(ctx, name, args, Args::One, is::alphanum), "is::alphanum" => is::alphanum,
"is::alpha" => args::check(ctx, name, args, Args::One, is::alpha), "is::alpha" => is::alpha,
"is::ascii" => args::check(ctx, name, args, Args::One, is::ascii), "is::ascii" => is::ascii,
"is::domain" => args::check(ctx, name, args, Args::One, is::domain), "is::domain" => is::domain,
"is::email" => args::check(ctx, name, args, Args::One, is::email), "is::email" => is::email,
"is::hexadecimal" => args::check(ctx, name, args, Args::One, is::hexadecimal), "is::hexadecimal" => is::hexadecimal,
"is::latitude" => args::check(ctx, name, args, Args::One, is::latitude), "is::latitude" => is::latitude,
"is::longitude" => args::check(ctx, name, args, Args::One, is::longitude), "is::longitude" => is::longitude,
"is::numeric" => args::check(ctx, name, args, Args::One, is::numeric), "is::numeric" => is::numeric,
"is::semver" => args::check(ctx, name, args, Args::One, is::semver), "is::semver" => is::semver,
"is::uuid" => args::check(ctx, name, args, Args::One, is::uuid), "is::uuid" => is::uuid,
// //
"math::abs" => args::check(ctx, name, args, Args::One, math::abs), "math::abs" => math::abs,
"math::bottom" => args::check(ctx, name, args, Args::Two, math::bottom), "math::bottom" => math::bottom,
"math::ceil" => args::check(ctx, name, args, Args::One, math::ceil), "math::ceil" => math::ceil,
"math::fixed" => args::check(ctx, name, args, Args::Two, math::fixed), "math::fixed" => math::fixed,
"math::floor" => args::check(ctx, name, args, Args::One, math::floor), "math::floor" => math::floor,
"math::interquartile" => args::check(ctx, name, args, Args::One, math::interquartile), "math::interquartile" => math::interquartile,
"math::max" => args::check(ctx, name, args, Args::One, math::max), "math::max" => math::max,
"math::mean" => args::check(ctx, name, args, Args::One, math::mean), "math::mean" => math::mean,
"math::median" => args::check(ctx, name, args, Args::One, math::median), "math::median" => math::median,
"math::midhinge" => args::check(ctx, name, args, Args::One, math::midhinge), "math::midhinge" => math::midhinge,
"math::min" => args::check(ctx, name, args, Args::One, math::min), "math::min" => math::min,
"math::mode" => args::check(ctx, name, args, Args::One, math::mode), "math::mode" => math::mode,
"math::nearestrank" => args::check(ctx, name, args, Args::Two, math::nearestrank), "math::nearestrank" => math::nearestrank,
"math::percentile" => args::check(ctx, name, args, Args::Two, math::percentile), "math::percentile" => math::percentile,
"math::product" => args::check(ctx, name, args, Args::One, math::product), "math::product" => math::product,
"math::round" => args::check(ctx, name, args, Args::One, math::round), "math::roun" => math::round,
"math::spread" => args::check(ctx, name, args, Args::One, math::spread), "math::spread" => math::spread,
"math::sqrt" => args::check(ctx, name, args, Args::One, math::sqrt), "math::sqrt" => math::sqrt,
"math::stddev" => args::check(ctx, name, args, Args::One, math::stddev), "math::stddev" => math::stddev,
"math::sum" => args::check(ctx, name, args, Args::One, math::sum), "math::sum" => math::sum,
"math::top" => args::check(ctx, name, args, Args::Two, math::top), "math::top" => math::top,
"math::trimean" => args::check(ctx, name, args, Args::One, math::trimean), "math::trimean" => math::trimean,
"math::variance" => args::check(ctx, name, args, Args::One, math::variance), "math::variance" => math::variance,
// //
"meta::id" => args::check(ctx, name, args, Args::One, meta::id), "meta::id" => meta::id,
"meta::table" => args::check(ctx, name, args, Args::One, meta::tb), "meta::table" => meta::tb,
"meta::tb" => args::check(ctx, name, args, Args::One, meta::tb), "meta::tb" => meta::tb,
// //
"parse::email::host" => args::check(ctx, name, args, Args::One, parse::email::host), "parse::email::host" => parse::email::host,
"parse::email::user" => args::check(ctx, name, args, Args::One, parse::email::user), "parse::email::user" => parse::email::user,
"parse::url::domain" => args::check(ctx, name, args, Args::One, parse::url::domain), "parse::url::domain" => parse::url::domain,
"parse::url::fragment" => args::check(ctx, name, args, Args::One, parse::url::fragment), "parse::url::fragment" => parse::url::fragment,
"parse::url::host" => args::check(ctx, name, args, Args::One, parse::url::host), "parse::url::host" => parse::url::host,
"parse::url::path" => args::check(ctx, name, args, Args::One, parse::url::path), "parse::url::path" => parse::url::path,
"parse::url::port" => args::check(ctx, name, args, Args::One, parse::url::port), "parse::url::port" => parse::url::port,
"parse::url::query" => args::check(ctx, name, args, Args::One, parse::url::query), "parse::url::query" => parse::url::query,
// //
"rand::bool" => args::check(ctx, name, args, Args::None, rand::bool), "rand::bool" => rand::bool,
"rand::enum" => args::check(ctx, name, args, Args::Any, rand::r#enum), "rand::enum" => rand::r#enum,
"rand::float" => args::check(ctx, name, args, Args::NoneTwo, rand::float), "rand::float" => rand::float,
"rand::guid" => args::check(ctx, name, args, Args::NoneOne, rand::guid), "rand::guid" => rand::guid,
"rand::int" => args::check(ctx, name, args, Args::NoneTwo, rand::int), "rand::int" => rand::int,
"rand::string" => args::check(ctx, name, args, Args::NoneOneTwo, rand::string), "rand::string" => rand::string,
"rand::time" => args::check(ctx, name, args, Args::NoneTwo, rand::time), "rand::time" => rand::time,
"rand::uuid" => args::check(ctx, name, args, Args::None, rand::uuid), "rand::uuid" => rand::uuid,
"rand" => args::check(ctx, name, args, Args::None, rand::rand), "rand" => rand::rand,
// //
"session::db" => args::check(ctx, name, args, Args::None, session::db), "session::db" => session::db(ctx),
"session::id" => args::check(ctx, name, args, Args::None, session::id), "session::id" => session::id(ctx),
"session::ip" => args::check(ctx, name, args, Args::None, session::ip), "session::ip" => session::ip(ctx),
"session::ns" => args::check(ctx, name, args, Args::None, session::ns), "session::ns" => session::ns(ctx),
"session::origin" => args::check(ctx, name, args, Args::None, session::origin), "session::origin" => session::origin(ctx),
"session::sc" => args::check(ctx, name, args, Args::None, session::sc), "session::sc" => session::sc(ctx),
"session::sd" => args::check(ctx, name, args, Args::None, session::sd), "session::sd" => session::sd(ctx),
"session::token" => args::check(ctx, name, args, Args::None, session::token), "session::token" => session::token(ctx),
// //
"string::concat" => args::check(ctx, name, args, Args::Any, string::concat), "string::concat" => string::concat,
"string::endsWith" => args::check(ctx, name, args, Args::Two, string::ends_with), "string::endsWith" => string::ends_with,
"string::join" => args::check(ctx, name, args, Args::Any, string::join), "string::join" => string::join,
"string::length" => args::check(ctx, name, args, Args::One, string::length), "string::length" => string::length,
"string::lowercase" => args::check(ctx, name, args, Args::One, string::lowercase), "string::lowercase" => string::lowercase,
"string::repeat" => args::check(ctx, name, args, Args::Two, string::repeat), "string::repeat" => string::repeat,
"string::replace" => args::check(ctx, name, args, Args::Three, string::replace), "string::replace" => string::replace,
"string::reverse" => args::check(ctx, name, args, Args::One, string::reverse), "string::reverse" => string::reverse,
"string::slice" => args::check(ctx, name, args, Args::Three, string::slice), "string::slice" => string::slice,
"string::slug" => args::check(ctx, name, args, Args::OneTwo, string::slug), "string::slug" => string::slug,
"string::split" => args::check(ctx, name, args, Args::Two, string::split), "string::split" => string::split,
"string::startsWith" => args::check(ctx, name, args, Args::Two, string::starts_with), "string::startsWith" => string::starts_with,
"string::trim" => args::check(ctx, name, args, Args::One, string::trim), "string::trim" => string::trim,
"string::uppercase" => args::check(ctx, name, args, Args::One, string::uppercase), "string::uppercase" => string::uppercase,
"string::words" => args::check(ctx, name, args, Args::One, string::words), "string::words" => string::words,
// //
"time::day" => args::check(ctx, name, args, Args::NoneOne, time::day), "time::day" => time::day,
"time::floor" => args::check(ctx, name, args, Args::Two, time::floor), "time::floor" => time::floor,
"time::group" => args::check(ctx, name, args, Args::Two, time::group), "time::group" => time::group,
"time::hour" => args::check(ctx, name, args, Args::NoneOne, time::hour), "time::hour" => time::hour,
"time::mins" => args::check(ctx, name, args, Args::NoneOne, time::mins), "time::mins" => time::mins,
"time::month" => args::check(ctx, name, args, Args::NoneOne, time::month), "time::month" => time::month,
"time::nano" => args::check(ctx, name, args, Args::NoneOne, time::nano), "time::nano" => time::nano,
"time::now" => args::check(ctx, name, args, Args::None, time::now), "time::now" => time::now,
"time::round" => args::check(ctx, name, args, Args::Two, time::round), "time::round" => time::round,
"time::secs" => args::check(ctx, name, args, Args::NoneOne, time::secs), "time::secs" => time::secs,
"time::unix" => args::check(ctx, name, args, Args::NoneOne, time::unix), "time::unix" => time::unix,
"time::wday" => args::check(ctx, name, args, Args::NoneOne, time::wday), "time::wday" => time::wday,
"time::week" => args::check(ctx, name, args, Args::NoneOne, time::week), "time::week" => time::week,
"time::yday" => args::check(ctx, name, args, Args::NoneOne, time::yday), "time::yday" => time::yday,
"time::year" => args::check(ctx, name, args, Args::NoneOne, time::year), "time::year" => time::year,
// //
"type::bool" => args::check(ctx, name, args, Args::One, r#type::bool), "type::bool" => r#type::bool,
"type::datetime" => args::check(ctx, name, args, Args::One, r#type::datetime), "type::datetime" => r#type::datetime,
"type::decimal" => args::check(ctx, name, args, Args::One, r#type::decimal), "type::decimal" => r#type::decimal,
"type::duration" => args::check(ctx, name, args, Args::One, r#type::duration), "type::duration" => r#type::duration,
"type::float" => args::check(ctx, name, args, Args::One, r#type::float), "type::float" => r#type::float,
"type::int" => args::check(ctx, name, args, Args::One, r#type::int), "type::int" => r#type::int,
"type::number" => args::check(ctx, name, args, Args::One, r#type::number), "type::number" => r#type::number,
"type::point" => args::check(ctx, name, args, Args::OneTwo, r#type::point), "type::point" => r#type::point,
"type::regex" => args::check(ctx, name, args, Args::One, r#type::regex), "type::regex" => r#type::regex,
"type::string" => args::check(ctx, name, args, Args::One, r#type::string), "type::string" => r#type::string,
"type::table" => args::check(ctx, name, args, Args::One, r#type::table), "type::table" => r#type::table,
"type::thing" => args::check(ctx, name, args, Args::OneTwo, r#type::thing), "type::thing" => r#type::thing,
// )
_ => unreachable!(),
}
}
// Attempts to run an asynchronous function
pub async fn asynchronous(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Value, Error> {
match name {
//
"http::head" => http::head(ctx, args).await,
"http::get" => http::get(ctx, args).await,
"http::put" => http::put(ctx, args).await,
"http::post" => http::post(ctx, args).await,
"http::patch" => http::patch(ctx, args).await,
"http::delete" => http::delete(ctx, args).await,
//
_ => unreachable!(),
}
} }

View file

@ -1,15 +1,12 @@
pub mod email { pub mod email {
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use addr::email::Host; use addr::email::Host;
pub fn host(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn host((string,): (String,)) -> Result<Value, Error> {
// Convert to a String
let val = args.remove(0).as_string();
// Parse the email address // Parse the email address
match addr::parse_email_address(&val) { match addr::parse_email_address(&string) {
// Return the host part // Return the host part
Ok(v) => match v.host() { Ok(v) => match v.host() {
Host::Domain(name) => Ok(name.as_str().into()), Host::Domain(name) => Ok(name.as_str().into()),
@ -19,10 +16,9 @@ pub mod email {
} }
} }
pub fn user(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn user((string,): (String,)) -> Result<Value, Error> {
let val = args.remove(0).as_string();
// Parse the email address // Parse the email address
match addr::parse_email_address(&val) { match addr::parse_email_address(&string) {
// Return the user part // Return the user part
Ok(v) => Ok(v.user().into()), Ok(v) => Ok(v.user().into()),
Err(_) => Ok(Value::None), Err(_) => Ok(Value::None),
@ -33,15 +29,15 @@ pub mod email {
mod tests { mod tests {
#[test] #[test]
fn host() { fn host() {
let input = vec!["john.doe@example.com".into()]; let input = (String::from("john.doe@example.com"),);
let value = super::host(&Default::default(), input).unwrap(); let value = super::host(input).unwrap();
assert_eq!(value, "example.com".into()); assert_eq!(value, "example.com".into());
} }
#[test] #[test]
fn user() { fn user() {
let input = vec!["john.doe@example.com".into()]; let input = (String::from("john.doe@example.com"),);
let value = super::user(&Default::default(), input).unwrap(); let value = super::user(input).unwrap();
assert_eq!(value, "john.doe".into()); assert_eq!(value, "john.doe".into());
} }
} }
@ -49,16 +45,12 @@ pub mod email {
pub mod url { pub mod url {
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::value::Value; use crate::sql::value::Value;
use url::Url; use url::Url;
pub fn domain(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn domain((string,): (String,)) -> Result<Value, Error> {
// Convert to a String match Url::parse(&string) {
let val = args.remove(0).as_string();
// Parse the URL
match Url::parse(&val) {
Ok(v) => match v.domain() { Ok(v) => match v.domain() {
Some(v) => Ok(v.into()), Some(v) => Ok(v.into()),
None => Ok(Value::None), None => Ok(Value::None),
@ -67,11 +59,9 @@ pub mod url {
} }
} }
pub fn fragment(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn fragment((string,): (String,)) -> Result<Value, Error> {
// Convert to a String
let val = args.remove(0).as_string();
// Parse the URL // Parse the URL
match Url::parse(&val) { match Url::parse(&string) {
Ok(v) => match v.fragment() { Ok(v) => match v.fragment() {
Some(v) => Ok(v.into()), Some(v) => Ok(v.into()),
None => Ok(Value::None), None => Ok(Value::None),
@ -80,11 +70,9 @@ pub mod url {
} }
} }
pub fn host(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn host((string,): (String,)) -> Result<Value, Error> {
// Convert to a String
let val = args.remove(0).as_string();
// Parse the URL // Parse the URL
match Url::parse(&val) { match Url::parse(&string) {
Ok(v) => match v.host_str() { Ok(v) => match v.host_str() {
Some(v) => Ok(v.into()), Some(v) => Ok(v.into()),
None => Ok(Value::None), None => Ok(Value::None),
@ -93,21 +81,17 @@ pub mod url {
} }
} }
pub fn path(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn path((string,): (String,)) -> Result<Value, Error> {
// Convert to a String
let val = args.remove(0).as_string();
// Parse the URL // Parse the URL
match Url::parse(&val) { match Url::parse(&string) {
Ok(v) => Ok(v.path().into()), Ok(v) => Ok(v.path().into()),
Err(_) => Ok(Value::None), Err(_) => Ok(Value::None),
} }
} }
pub fn port(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn port((string,): (String,)) -> Result<Value, Error> {
// Convert to a String
let val = args.remove(0).as_string();
// Parse the URL // Parse the URL
match Url::parse(&val) { match Url::parse(&string) {
Ok(v) => match v.port() { Ok(v) => match v.port() {
Some(v) => Ok(v.into()), Some(v) => Ok(v.into()),
None => Ok(Value::None), None => Ok(Value::None),
@ -116,11 +100,9 @@ pub mod url {
} }
} }
pub fn query(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn query((string,): (String,)) -> Result<Value, Error> {
// Convert to a String
let val = args.remove(0).as_string();
// Parse the URL // Parse the URL
match Url::parse(&val) { match Url::parse(&string) {
Ok(v) => match v.query() { Ok(v) => match v.query() {
Some(v) => Ok(v.into()), Some(v) => Ok(v.into()),
None => Ok(Value::None), None => Ok(Value::None),

View file

@ -1,5 +1,4 @@
use crate::cnf::ID_CHARS; use crate::cnf::ID_CHARS;
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::datetime::Datetime; use crate::sql::datetime::Datetime;
use crate::sql::uuid::Uuid; use crate::sql::uuid::Uuid;
@ -9,15 +8,15 @@ use rand::distributions::Alphanumeric;
use rand::prelude::IteratorRandom; use rand::prelude::IteratorRandom;
use rand::Rng; use rand::Rng;
pub fn rand(_: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn rand(_: ()) -> Result<Value, Error> {
Ok(rand::random::<f64>().into()) Ok(rand::random::<f64>().into())
} }
pub fn bool(_: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn bool(_: ()) -> Result<Value, Error> {
Ok(rand::random::<bool>().into()) Ok(rand::random::<bool>().into())
} }
pub fn r#enum(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn r#enum(mut args: Vec<Value>) -> Result<Value, Error> {
Ok(match args.len() { Ok(match args.len() {
0 => Value::None, 0 => Value::None,
1 => match args.remove(0) { 1 => match args.remove(0) {
@ -28,127 +27,104 @@ pub fn r#enum(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
}) })
} }
pub fn float(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn float((range,): (Option<(f64, f64)>,)) -> Result<Value, Error> {
match args.len() { Ok(if let Some((min, max)) = range {
2 => { if max < min {
let min = args.remove(0).as_float(); rand::thread_rng().gen_range(max..=min)
match args.remove(0).as_float() { } else {
max if max < min => Ok(rand::thread_rng().gen_range(max..=min).into()), rand::thread_rng().gen_range(min..=max)
max => Ok(rand::thread_rng().gen_range(min..=max).into()),
} }
} else {
rand::random::<f64>()
} }
0 => Ok(rand::random::<f64>().into()), .into())
_ => unreachable!(),
}
} }
pub fn guid(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn guid((len,): (Option<usize>,)) -> Result<Value, Error> {
match args.len() {
1 => {
// Only need 53 to uniquely identify all atoms in observable universe. // Only need 53 to uniquely identify all atoms in observable universe.
const LIMIT: usize = 64; const LIMIT: usize = 64;
let len = args.remove(0).as_int() as usize;
if len > LIMIT { let len = match len {
Err(Error::InvalidArguments { Some(len) if len <= LIMIT => len,
None => 20,
_ => {
return Err(Error::InvalidArguments {
name: String::from("rand::guid"), name: String::from("rand::guid"),
message: format!("The maximum length of a GUID is {}.", LIMIT), message: format!("The maximum length of a GUID is {}.", LIMIT),
}) })
} else { }
};
Ok(nanoid!(len, &ID_CHARS).into()) Ok(nanoid!(len, &ID_CHARS).into())
}
}
0 => Ok(nanoid!(20, &ID_CHARS).into()),
_ => unreachable!(),
}
} }
pub fn int(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn int((range,): (Option<(i64, i64)>,)) -> Result<Value, Error> {
match args.len() { Ok(if let Some((min, max)) = range {
2 => { if max < min {
let min = args.remove(0).as_int(); rand::thread_rng().gen_range(max..=min)
match args.remove(0).as_int() { } else {
max if max < min => Ok(rand::thread_rng().gen_range(max..=min).into()), rand::thread_rng().gen_range(min..=max)
max => Ok(rand::thread_rng().gen_range(min..=max).into()),
} }
} else {
rand::random::<i64>()
} }
0 => Ok(rand::random::<i64>().into()), .into())
_ => unreachable!(),
}
} }
pub fn string(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn string((arg1, arg2): (Option<i64>, Option<i64>)) -> Result<Value, Error> {
// Limit how much time and bandwidth is spent. // Limit how much time and bandwidth is spent.
const LIMIT: i64 = 2i64.pow(16); const LIMIT: i64 = 2i64.pow(16);
match args.len() {
2 => match args.remove(0).as_int() { let len = if let Some((min, max)) = arg1.zip(arg2) {
min if (0..=LIMIT).contains(&min) => match args.remove(0).as_int() { match min {
max if min <= max && max <= LIMIT => Ok(rand::thread_rng() min if (0..=LIMIT).contains(&min) => match max {
.sample_iter(&Alphanumeric) max if min <= max && max <= LIMIT => rand::thread_rng().gen_range(min as usize..=max as usize),
.take(rand::thread_rng().gen_range(min as usize..=max as usize)) max if max >= 0 && max <= min => rand::thread_rng().gen_range(max as usize..=min as usize),
.map(char::from) _ => return Err(Error::InvalidArguments {
.collect::<String>()
.into()),
max if max >= 0 && max <= min => Ok(rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(rand::thread_rng().gen_range(max as usize..=min as usize))
.map(char::from)
.collect::<String>()
.into()),
_ => Err(Error::InvalidArguments {
name: String::from("rand::string"), name: String::from("rand::string"),
message: format!("To generate a string of between X and Y characters in length, the 2 arguments must be positive numbers and no higher than {}.", LIMIT), message: format!("To generate a string of between X and Y characters in length, the 2 arguments must be positive numbers and no higher than {}.", LIMIT),
}), }),
}, },
_ => Err(Error::InvalidArguments { _ => return Err(Error::InvalidArguments {
name: String::from("rand::string"), name: String::from("rand::string"),
message: format!("To generate a string of between X and Y characters in length, the 2 arguments must be positive numbers and no higher than {}.", LIMIT), message: format!("To generate a string of between X and Y characters in length, the 2 arguments must be positive numbers and no higher than {}.", LIMIT),
}), }),
}, }
1 => match args.remove(0).as_int() { } else if let Some(len) = arg1 {
x if (0..=LIMIT).contains(&x) => Ok(rand::thread_rng() if (0..=LIMIT).contains(&len) {
.sample_iter(&Alphanumeric) len as usize
.take(x as usize) } else {
.map(char::from) return Err(Error::InvalidArguments {
.collect::<String>()
.into()),
_ => Err(Error::InvalidArguments {
name: String::from("rand::string"), name: String::from("rand::string"),
message: format!("To generate a string of X characters in length, the argument must be a positive number and no higher than {}.", LIMIT), message: format!("To generate a string of X characters in length, the argument must be a positive number and no higher than {}.", LIMIT),
}), });
}, }
0 => Ok(rand::thread_rng() } else {
32
};
Ok(rand::thread_rng()
.sample_iter(&Alphanumeric) .sample_iter(&Alphanumeric)
.take(32) .take(len)
.map(char::from) .map(char::from)
.collect::<String>() .collect::<String>()
.into()), .into())
_ => unreachable!(),
}
} }
pub fn time(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn time((range,): (Option<(i64, i64)>,)) -> Result<Value, Error> {
match args.len() { let i = if let Some((min, max)) = range {
2 => { let range = if max < min {
let min = args.remove(0).as_int(); max..=min
match args.remove(0).as_int() { } else {
max if max < min => { min..=max
let i = rand::thread_rng().gen_range(max..=min); };
rand::thread_rng().gen_range(range)
} else {
rand::random::<i32>() as i64
};
Ok(Datetime::from(i).into()) Ok(Datetime::from(i).into())
}
max => {
let i = rand::thread_rng().gen_range(min..=max);
Ok(Datetime::from(i).into())
}
}
}
0 => {
let i = rand::random::<i32>();
Ok(Datetime::from(i as i64).into())
}
_ => unreachable!(),
}
} }
pub fn uuid(_: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn uuid(_: ()) -> Result<Value, Error> {
Ok(Uuid::new().into()) Ok(Uuid::new().into())
} }

View file

@ -10,34 +10,34 @@ use crate::sql::paths::SD;
use crate::sql::paths::TK; use crate::sql::paths::TK;
use crate::sql::value::Value; use crate::sql::value::Value;
pub fn db(ctx: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn db(ctx: &Context, _: ()) -> Result<Value, Error> {
ctx.value("session").unwrap_or(&Value::None).pick(DB.as_ref()).ok() ctx.value("session").unwrap_or(&Value::None).pick(DB.as_ref()).ok()
} }
pub fn id(ctx: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn id(ctx: &Context, _: ()) -> Result<Value, Error> {
ctx.value("session").unwrap_or(&Value::None).pick(ID.as_ref()).ok() ctx.value("session").unwrap_or(&Value::None).pick(ID.as_ref()).ok()
} }
pub fn ip(ctx: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn ip(ctx: &Context, _: ()) -> Result<Value, Error> {
ctx.value("session").unwrap_or(&Value::None).pick(IP.as_ref()).ok() ctx.value("session").unwrap_or(&Value::None).pick(IP.as_ref()).ok()
} }
pub fn ns(ctx: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn ns(ctx: &Context, _: ()) -> Result<Value, Error> {
ctx.value("session").unwrap_or(&Value::None).pick(NS.as_ref()).ok() ctx.value("session").unwrap_or(&Value::None).pick(NS.as_ref()).ok()
} }
pub fn origin(ctx: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn origin(ctx: &Context, _: ()) -> Result<Value, Error> {
ctx.value("session").unwrap_or(&Value::None).pick(OR.as_ref()).ok() ctx.value("session").unwrap_or(&Value::None).pick(OR.as_ref()).ok()
} }
pub fn sc(ctx: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn sc(ctx: &Context, _: ()) -> Result<Value, Error> {
ctx.value("session").unwrap_or(&Value::None).pick(SC.as_ref()).ok() ctx.value("session").unwrap_or(&Value::None).pick(SC.as_ref()).ok()
} }
pub fn sd(ctx: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn sd(ctx: &Context, _: ()) -> Result<Value, Error> {
ctx.value("session").unwrap_or(&Value::None).pick(SD.as_ref()).ok() ctx.value("session").unwrap_or(&Value::None).pick(SD.as_ref()).ok()
} }
pub fn token(ctx: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn token(ctx: &Context, _: ()) -> Result<Value, Error> {
ctx.value("session").unwrap_or(&Value::None).pick(TK.as_ref()).ok() ctx.value("session").unwrap_or(&Value::None).pick(TK.as_ref()).ok()
} }

View file

@ -1,41 +1,37 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::fnc::util::string; use crate::fnc::util::string;
use crate::sql::value::Value; use crate::sql::value::Value;
pub fn concat(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn concat(args: Vec<Value>) -> Result<Value, Error> {
Ok(args.into_iter().map(|x| x.as_string()).collect::<Vec<_>>().concat().into()) Ok(args.into_iter().map(|x| x.as_string()).collect::<Vec<_>>().concat().into())
} }
pub fn ends_with(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn ends_with((val, chr): (String, String)) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap();
let [val, chr] = args.map(Value::as_string);
Ok(val.ends_with(&chr).into()) Ok(val.ends_with(&chr).into())
} }
pub fn join(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn join(args: Vec<Value>) -> Result<Value, Error> {
let mut args = args.into_iter().map(Value::as_string); let mut args = args.into_iter().map(Value::as_string);
let chr = args.next().unwrap(); let chr = args.next().ok_or(Error::InvalidArguments {
name: String::from("string::join"),
message: String::from("Expected at least one argument"),
})?;
// FIXME: Use intersperse to avoid intermediate allocation once stable // FIXME: Use intersperse to avoid intermediate allocation once stable
// https://github.com/rust-lang/rust/issues/79524 // https://github.com/rust-lang/rust/issues/79524
let val = args.collect::<Vec<_>>().join(&chr); let val = args.collect::<Vec<_>>().join(&chr);
Ok(val.into()) Ok(val.into())
} }
pub fn length(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn length((string,): (String,)) -> Result<Value, Error> {
let val = args.remove(0).as_string(); let num = string.chars().count() as i64;
let num = val.chars().count() as i64;
Ok(num.into()) Ok(num.into())
} }
pub fn lowercase(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn lowercase((string,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().to_lowercase().into()) Ok(string.to_lowercase().into())
} }
pub fn repeat(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn repeat((val, num): (String, usize)) -> Result<Value, Error> {
let [val_arg, num_arg]: [Value; 2] = args.try_into().unwrap();
let val = val_arg.as_string();
let num = num_arg.as_int() as usize;
const LIMIT: usize = 2usize.pow(20); const LIMIT: usize = 2usize.pow(20);
if val.len().saturating_mul(num) > LIMIT { if val.len().saturating_mul(num) > LIMIT {
Err(Error::InvalidArguments { Err(Error::InvalidArguments {
@ -47,50 +43,38 @@ pub fn repeat(_: &Context, args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn replace(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn replace((val, old, new): (String, String, String)) -> Result<Value, Error> {
let args: [Value; 3] = args.try_into().unwrap();
let [val, old, new] = args.map(Value::as_string);
Ok(val.replace(&old, &new).into()) Ok(val.replace(&old, &new).into())
} }
pub fn reverse(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn reverse((string,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().chars().rev().collect::<String>().into()) Ok(string.chars().rev().collect::<String>().into())
} }
pub fn slice(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn slice((val, beg, lim): (String, usize, usize)) -> Result<Value, Error> {
let [val_arg, beg_arg, lim_arg]: [Value; 3] = args.try_into().unwrap(); Ok(val.chars().skip(beg).take(lim).collect::<String>().into())
let val = val_arg.as_string();
let beg = beg_arg.as_int() as usize;
let lim = lim_arg.as_int() as usize;
let val = val.chars().skip(beg).take(lim).collect::<String>();
Ok(val.into())
} }
pub fn slug(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn slug((string,): (String,)) -> Result<Value, Error> {
Ok(string::slug(&args.remove(0).as_string()).into()) Ok(string::slug(&string).into())
} }
pub fn split(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn split((val, chr): (String, String)) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap(); Ok(val.split(&chr).collect::<Vec<&str>>().into())
let [val, chr] = args.map(Value::as_string);
let val = val.split(&chr).collect::<Vec<&str>>();
Ok(val.into())
} }
pub fn starts_with(_: &Context, args: Vec<Value>) -> Result<Value, Error> { pub fn starts_with((val, chr): (String, String)) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap();
let [val, chr] = args.map(Value::as_string);
Ok(val.starts_with(&chr).into()) Ok(val.starts_with(&chr).into())
} }
pub fn trim(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn trim((string,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().trim().into()) Ok(string.trim().into())
} }
pub fn uppercase(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn uppercase((string,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().to_uppercase().into()) Ok(string.to_uppercase().into())
} }
pub fn words(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn words((string,): (String,)) -> Result<Value, Error> {
Ok(args.remove(0).as_string().split_whitespace().collect::<Vec<&str>>().into()) Ok(string.split_whitespace().collect::<Vec<&str>>().into())
} }

View file

@ -1,4 +1,3 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::datetime::Datetime; use crate::sql::datetime::Datetime;
use crate::sql::value::Value; use crate::sql::value::Value;
@ -8,19 +7,19 @@ use chrono::DurationRound;
use chrono::Timelike; use chrono::Timelike;
use chrono::Utc; use chrono::Utc;
pub fn day(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn day((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().day().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.day().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.day().into())
} }
pub fn floor(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn floor((datetime, duration): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match datetime {
Value::Datetime(v) => match args.remove(0) { Value::Datetime(v) => match duration {
Value::Duration(w) => match chrono::Duration::from_std(*w) { Value::Duration(w) => match chrono::Duration::from_std(*w) {
Ok(d) => match v.duration_trunc(d) { Ok(d) => match v.duration_trunc(d) {
Ok(v) => Ok(v.into()), Ok(v) => Ok(v.into()),
@ -34,9 +33,9 @@ pub fn floor(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn group(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn group((datetime, strand): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match datetime {
Value::Datetime(v) => match args.remove(0) { Value::Datetime(v) => match strand {
Value::Strand(g) => match g.as_str() { Value::Strand(g) => match g.as_str() {
"year" => Ok(Utc.ymd(v.year(), 1, 1).and_hms(0, 0, 0).into()), "year" => Ok(Utc.ymd(v.year(), 1, 1).and_hms(0, 0, 0).into()),
"month" => Ok(Utc.ymd(v.year(), v.month(), 1).and_hms(0, 0, 0).into()), "month" => Ok(Utc.ymd(v.year(), v.month(), 1).and_hms(0, 0, 0).into()),
@ -67,53 +66,53 @@ pub fn group(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn hour(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn hour((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().hour().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.hour().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.hour().into())
} }
pub fn mins(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn mins((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().minute().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.minute().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.minute().into())
} }
pub fn month(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn month((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().month().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.month().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.month().into())
} }
pub fn nano(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn nano((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().timestamp_nanos().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.timestamp_nanos().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.timestamp_nanos().into())
} }
pub fn now(_: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn now(_: ()) -> Result<Value, Error> {
Ok(Datetime::default().into()) Ok(Datetime::default().into())
} }
pub fn round(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn round((datetime, duration): (Value, Value)) -> Result<Value, Error> {
match args.remove(0) { match datetime {
Value::Datetime(v) => match args.remove(0) { Value::Datetime(v) => match duration {
Value::Duration(w) => match chrono::Duration::from_std(*w) { Value::Duration(w) => match chrono::Duration::from_std(*w) {
Ok(d) => match v.duration_round(d) { Ok(d) => match v.duration_round(d) {
Ok(v) => Ok(v.into()), Ok(v) => Ok(v.into()),
@ -127,62 +126,62 @@ pub fn round(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
} }
pub fn secs(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn secs((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().second().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.second().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.second().into())
} }
pub fn unix(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn unix((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().timestamp().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.timestamp().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.timestamp().into())
} }
pub fn wday(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn wday((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().weekday().number_from_monday().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.weekday().number_from_monday().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.weekday().number_from_monday().into())
} }
pub fn week(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn week((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().iso_week().week().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.iso_week().week().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.iso_week().week().into())
} }
pub fn yday(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn yday((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().ordinal().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.ordinal().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.ordinal().into())
} }
pub fn year(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn year((datetime,): (Option<Value>,)) -> Result<Value, Error> {
match args.len() { let date = match datetime {
0 => Ok(Utc::now().year().into()), Some(Value::Datetime(v)) => v,
_ => match args.remove(0) { None => Datetime::default(),
Value::Datetime(v) => Ok(v.year().into()), Some(_) => return Ok(Value::None),
_ => Ok(Value::None), };
},
} Ok(date.year().into())
} }

View file

@ -1,120 +1,104 @@
use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::geometry::Geometry; use crate::sql::geometry::Geometry;
use crate::sql::number::Number; use crate::sql::number::Number;
use crate::sql::table::Table; use crate::sql::table::Table;
use crate::sql::thing::Thing; use crate::sql::thing::Thing;
use crate::sql::value::Value; use crate::sql::value::Value;
use crate::sql::Strand;
pub fn bool(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn bool((arg,): (Value,)) -> Result<Value, Error> {
match args.remove(0).is_truthy() { Ok(arg.is_truthy().into())
true => Ok(Value::True), }
false => Ok(Value::False),
pub fn datetime((arg,): (Value,)) -> Result<Value, Error> {
Ok(match arg {
Value::Datetime(_) => arg,
_ => Value::Datetime(arg.as_datetime()),
})
}
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 duration((arg,): (Value,)) -> Result<Value, Error> {
match arg {
Value::Duration(_) => Ok(arg),
_ => Ok(Value::Duration(arg.as_duration())),
} }
} }
pub fn datetime(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn float((arg,): (Value,)) -> Result<Value, Error> {
let val = args.remove(0); match arg {
match val { Value::Number(Number::Float(_)) => Ok(arg),
Value::Datetime(_) => Ok(val), _ => Ok(Value::Number(Number::Float(arg.as_float()))),
_ => Ok(Value::Datetime(val.as_datetime())),
} }
} }
pub fn decimal(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn int((arg,): (Value,)) -> Result<Value, Error> {
let val = args.remove(0); match arg {
match val { Value::Number(Number::Int(_)) => Ok(arg),
Value::Number(Number::Decimal(_)) => Ok(val), _ => Ok(Value::Number(Number::Int(arg.as_int()))),
_ => Ok(Value::Number(Number::Decimal(val.as_decimal()))),
} }
} }
pub fn duration(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn number((arg,): (Value,)) -> Result<Value, Error> {
let val = args.remove(0); match arg {
match val { Value::Number(_) => Ok(arg),
Value::Duration(_) => Ok(val), _ => Ok(Value::Number(arg.as_number())),
_ => Ok(Value::Duration(val.as_duration())),
} }
} }
pub fn float(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn point((arg1, arg2): (Value, Option<Value>)) -> Result<Value, Error> {
let val = args.remove(0); Ok(if let Some(y) = arg2 {
match val { let x = arg1;
Value::Number(Number::Float(_)) => Ok(val), (x.as_float(), y.as_float()).into()
_ => Ok(Value::Number(Number::Float(val.as_float()))), } 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 int(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn regex((arg,): (Value,)) -> Result<Value, Error> {
let val = args.remove(0); match arg {
match val {
Value::Number(Number::Int(_)) => Ok(val),
_ => Ok(Value::Number(Number::Int(val.as_int()))),
}
}
pub fn number(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
let val = args.remove(0);
match val {
Value::Number(_) => Ok(val),
_ => Ok(Value::Number(val.as_number())),
}
}
pub fn point(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
2 => {
let x = args.remove(0);
let y = args.remove(0);
Ok((x.as_float(), y.as_float()).into())
}
1 => match args.remove(0) {
Value::Array(v) if v.len() == 2 => Ok(v.as_point().into()),
Value::Geometry(Geometry::Point(v)) => Ok(v.into()),
_ => Ok(Value::None),
},
_ => unreachable!(),
}
}
pub fn regex(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.remove(0) {
Value::Strand(v) => Ok(Value::Regex(v.as_str().into())), Value::Strand(v) => Ok(Value::Regex(v.as_str().into())),
_ => Ok(Value::None), _ => Ok(Value::None),
} }
} }
pub fn string(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn string((arg,): (Strand,)) -> Result<Value, Error> {
let val = args.remove(0); Ok(arg.into())
match val {
Value::Strand(_) => Ok(val),
_ => Ok(Value::Strand(val.as_strand())),
}
} }
pub fn table(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn table((arg,): (Value,)) -> Result<Value, Error> {
Ok(Value::Table(Table(match args.remove(0) { Ok(Value::Table(Table(match arg {
Value::Thing(t) => t.tb, Value::Thing(t) => t.tb,
v => v.as_string(), v => v.as_string(),
}))) })))
} }
pub fn thing(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> { pub fn thing((arg1, arg2): (Value, Option<Value>)) -> Result<Value, Error> {
match args.len() { Ok(if let Some(arg2) = arg2 {
2 => Ok(Value::Thing(Thing { Value::Thing(Thing {
tb: args.remove(0).as_string(), tb: arg1.as_string(),
id: match args.remove(0) { id: match arg2 {
Value::Thing(v) => v.id, Value::Thing(v) => v.id,
Value::Array(v) => v.into(), Value::Array(v) => v.into(),
Value::Object(v) => v.into(), Value::Object(v) => v.into(),
Value::Number(Number::Int(v)) => v.into(), Value::Number(Number::Int(v)) => v.into(),
v => v.as_string().into(), v => v.as_string().into(),
}, },
})), })
1 => match args.remove(0) { } else {
Value::Thing(v) => Ok(v.into()), match arg1 {
_ => Ok(Value::None), Value::Thing(v) => v.into(),
}, _ => Value::None,
_ => unreachable!(),
} }
})
} }