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::sql::value::Value;
use crate::sql::{Number, Strand};
pub enum Args {
None,
Any,
One,
Two,
Three,
NoneOne,
NoneTwo,
NoneOneTwo,
OneTwo,
/// Implemented by types that are commonly used, in a certain way, as arguments.
pub trait FromArg: Sized {
/// Potentially fallible conversion from a Value to an argument. Errors will be propagated
/// to the caller, although it is also possible to return a none/null Value.
fn from_arg(arg: Value) -> Result<Self, Error>;
}
pub fn check(
ctx: &Context,
name: &str,
args: Vec<Value>,
size: Args,
func: fn(&Context, Vec<Value>) -> Result<Value, Error>,
) -> Result<Value, Error> {
match size {
Args::None => match args.len() {
0 => func(ctx, args),
_ => Err(Error::InvalidArguments {
name: name.to_owned(),
message: String::from("The function does not expect any arguments."),
}),
},
Args::One => match args.len() {
1 => func(ctx, args),
_ => Err(Error::InvalidArguments {
name: name.to_owned(),
message: String::from("The function expects 1 argument."),
}),
},
Args::Two => match args.len() {
2 => func(ctx, args),
_ => Err(Error::InvalidArguments {
name: name.to_owned(),
message: String::from("The function expects 2 arguments."),
}),
},
Args::Three => match args.len() {
3 => func(ctx, args),
_ => Err(Error::InvalidArguments {
name: name.to_owned(),
message: String::from("The function expects 3 arguments."),
}),
},
Args::NoneOne => match args.len() {
0 | 1 => func(ctx, args),
_ => Err(Error::InvalidArguments {
name: name.to_owned(),
message: String::from("The function expects 0 or 1 arguments."),
}),
},
Args::NoneTwo => match args.len() {
0 | 2 => func(ctx, args),
_ => Err(Error::InvalidArguments {
name: name.to_owned(),
message: String::from("The function expects 0 or 2 arguments."),
}),
},
Args::NoneOneTwo => match args.len() {
0 | 1 | 2 => func(ctx, args),
_ => Err(Error::InvalidArguments {
name: name.to_owned(),
message: String::from("The function expects 0, 1, or 2 arguments."),
}),
},
Args::OneTwo => match args.len() {
1 | 2 => func(ctx, args),
_ => Err(Error::InvalidArguments {
name: name.to_owned(),
message: String::from("The function expects 1 or 2 arguments."),
}),
},
Args::Any => func(ctx, args),
impl FromArg for Value {
fn from_arg(arg: Value) -> Result<Self, Error> {
Ok(arg)
}
}
impl FromArg for String {
fn from_arg(arg: Value) -> Result<Self, Error> {
Ok(arg.as_string())
}
}
impl FromArg for Strand {
fn from_arg(arg: Value) -> Result<Self, Error> {
Ok(arg.as_strand())
}
}
impl FromArg for Number {
fn from_arg(arg: Value) -> Result<Self, Error> {
Ok(arg.as_number())
}
}
impl FromArg for f64 {
fn from_arg(arg: Value) -> Result<Self, Error> {
Ok(arg.as_float())
}
}
impl FromArg for i64 {
fn from_arg(arg: Value) -> Result<Self, Error> {
Ok(arg.as_int())
}
}
impl FromArg for usize {
fn from_arg(arg: Value) -> Result<Self, Error> {
Ok(arg.as_int() as usize)
}
}
pub trait FromArgs: Sized {
/// Convert a collection of argument values into a certain argument format, failing if there are
/// too many or too few arguments, or if one of the arguments could not be converted.
fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error>;
}
// Take ownership of the raw arguments collection, and assume responsibility of validating the
// number of arguments and converting them as necessary.
impl FromArgs for Vec<Value> {
fn from_args(_name: &str, args: Vec<Value>) -> Result<Self, Error> {
Ok(args)
}
}
// Some functions take a fixed number of arguments.
// The len must match the number of type idents that follow.
macro_rules! impl_tuple {
($len:expr, $( $T:ident ),*) => {
impl<$($T:FromArg),*> FromArgs for ($($T,)*) {
#[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 {
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::sql::array::Combine;
use crate::sql::array::Concat;
@ -8,9 +7,9 @@ use crate::sql::array::Union;
use crate::sql::array::Uniq;
use crate::sql::value::Value;
pub fn concat(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.remove(0) {
Value::Array(v) => match args.remove(0) {
pub fn concat((left, right): (Value, Value)) -> Result<Value, Error> {
match left {
Value::Array(v) => match right {
Value::Array(w) => Ok(v.concat(w).into()),
_ => 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> {
match args.remove(0) {
Value::Array(v) => match args.remove(0) {
pub fn combine((left, right): (Value, Value)) -> Result<Value, Error> {
match left {
Value::Array(v) => match right {
Value::Array(w) => Ok(v.combine(w).into()),
_ => 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> {
match args.remove(0) {
Value::Array(v) => match args.remove(0) {
pub fn difference((left, right): (Value, Value)) -> Result<Value, Error> {
match left {
Value::Array(v) => match right {
Value::Array(w) => Ok(v.difference(w).into()),
_ => 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> {
match args.remove(0) {
pub fn distinct((arg,): (Value,)) -> Result<Value, Error> {
match arg {
Value::Array(v) => Ok(v.uniq().into()),
_ => Ok(Value::None),
}
}
pub fn intersect(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.remove(0) {
Value::Array(v) => match args.remove(0) {
pub fn intersect((left, right): (Value, Value)) -> Result<Value, Error> {
match left {
Value::Array(v) => match right {
Value::Array(w) => Ok(v.intersect(w).into()),
_ => Ok(Value::None),
},
@ -55,59 +54,49 @@ pub fn intersect(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
}
}
pub fn len(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.remove(0) {
pub fn len((arg,): (Value,)) -> Result<Value, Error> {
match arg {
Value::Array(v) => Ok(v.len().into()),
_ => Ok(Value::None),
}
}
pub fn sort(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
2 => match args.remove(0) {
Value::Array(mut v) => match args.remove(0) {
// If "asc", sort ascending
Value::Strand(s) if s.as_str() == "asc" => {
v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into())
}
// If "desc", sort descending
Value::Strand(s) if s.as_str() == "desc" => {
v.sort_unstable_by(|a, b| b.cmp(a));
Ok(v.into())
}
// If true, sort ascending
Value::True => {
v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into())
}
// If false, sort descending
Value::False => {
v.sort_unstable_by(|a, b| b.cmp(a));
Ok(v.into())
}
// Sort ascending by default
_ => {
v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into())
}
},
v => Ok(v),
},
1 => match args.remove(0) {
Value::Array(mut v) => {
pub fn sort((array, order): (Value, Option<Value>)) -> Result<Value, Error> {
match array {
Value::Array(mut v) => match order {
// If "asc", sort ascending
Some(Value::Strand(s)) if s.as_str() == "asc" => {
v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into())
}
// If "desc", sort descending
Some(Value::Strand(s)) if s.as_str() == "desc" => {
v.sort_unstable_by(|a, b| b.cmp(a));
Ok(v.into())
}
// If true, sort ascending
Some(Value::True) => {
v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into())
}
// If false, sort descending
Some(Value::False) => {
v.sort_unstable_by(|a, b| b.cmp(a));
Ok(v.into())
}
// Sort ascending by default
_ => {
v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into())
}
v => Ok(v),
},
_ => unreachable!(),
v => Ok(v),
}
}
pub fn union(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.remove(0) {
Value::Array(v) => match args.remove(0) {
pub fn union((left, right): (Value, Value)) -> Result<Value, Error> {
match left {
Value::Array(v) => match right {
Value::Array(w) => Ok(v.union(w).into()),
_ => Ok(Value::None),
},
@ -117,12 +106,11 @@ pub fn union(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
pub mod sort {
use crate::ctx::Context;
use crate::err::Error;
use crate::sql::value::Value;
pub fn asc(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.remove(0) {
pub fn asc((array,): (Value,)) -> Result<Value, Error> {
match array {
Value::Array(mut v) => {
v.sort_unstable_by(|a, b| a.cmp(b));
Ok(v.into())
@ -131,8 +119,8 @@ pub mod sort {
}
}
pub fn desc(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.remove(0) {
pub fn desc((array,): (Value,)) -> Result<Value, Error> {
match array {
Value::Array(mut v) => {
v.sort_unstable_by(|a, b| b.cmp(a));
Ok(v.into())

View file

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

View file

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

View file

@ -1,4 +1,3 @@
use crate::ctx::Context;
use crate::err::Error;
use crate::sql::geometry::Geometry;
use crate::sql::value::Value;
@ -7,8 +6,8 @@ use geo::algorithm::bearing::Bearing;
use geo::algorithm::centroid::Centroid;
use geo::algorithm::haversine_distance::HaversineDistance;
pub fn area(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.remove(0) {
pub fn area((arg,): (Value,)) -> Result<Value, Error> {
match arg {
Value::Geometry(v) => match v {
Geometry::Point(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> {
match args.remove(0) {
Value::Geometry(Geometry::Point(v)) => match args.remove(0) {
pub fn bearing((a, b): (Value, Value)) -> Result<Value, Error> {
match a {
Value::Geometry(Geometry::Point(v)) => match b {
Value::Geometry(Geometry::Point(w)) => Ok(v.bearing(w).into()),
_ => 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> {
match args.remove(0) {
pub fn centroid((arg,): (Value,)) -> Result<Value, Error> {
match arg {
Value::Geometry(v) => match v {
Geometry::Point(v) => Ok(v.centroid().into()),
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> {
match args.remove(0) {
Value::Geometry(Geometry::Point(v)) => match args.remove(0) {
pub fn distance((from, to): (Value, Value)) -> Result<Value, Error> {
match from {
Value::Geometry(Geometry::Point(v)) => match to {
Value::Geometry(Geometry::Point(w)) => Ok(v.haversine_distance(&w).into()),
_ => Ok(Value::None),
},
@ -81,34 +80,29 @@ pub fn distance(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
pub mod hash {
use crate::ctx::Context;
use crate::err::Error;
use crate::fnc::util::geo;
use crate::sql::geometry::Geometry;
use crate::sql::value::Value;
pub fn encode(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
2 => match args.remove(0) {
Value::Geometry(Geometry::Point(v)) => match args.remove(0).as_int() {
l if l > 0 && l <= 12 => Ok(geo::encode(v, l as usize).into()),
_ => Err(Error::InvalidArguments {
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."),
}),
},
_ => Ok(Value::None),
},
1 => match args.remove(0) {
Value::Geometry(Geometry::Point(v)) => Ok(geo::encode(v, 12).into()),
_ => Ok(Value::None),
},
_ => unreachable!(),
}
pub fn encode((point, len): (Value, Option<usize>)) -> Result<Value, Error> {
let len = match len {
Some(len) if (1..=12).contains(&len) => len,
None => 12,
_ => return Err(Error::InvalidArguments {
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."),
})
};
Ok(match point {
Value::Geometry(Geometry::Point(v)) => geo::encode(v, len).into(),
_ => Value::None,
})
}
pub fn decode(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.remove(0) {
pub fn decode((arg,): (Value,)) -> Result<Value, Error> {
match arg {
Value::Strand(v) => Ok(geo::decode(v).into()),
_ => Ok(Value::None),
}

View file

@ -1,234 +1,156 @@
use crate::ctx::Context;
use crate::err::Error;
use crate::sql::value::Value;
use crate::sql::Strand;
#[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)
}
#[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)
}
#[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)
}
#[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)
}
#[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)
}
#[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)
}
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")]
pub async fn head(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
2 => match args.remove(0) {
Value::Strand(uri) => match args.remove(0) {
Value::Object(opt) => crate::fnc::util::http::head(uri, opt).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::head"),
message: String::from("The second argument should be an object."),
}),
},
_ => Err(Error::InvalidArguments {
pub async fn head((uri, opts): (Value, Option<Value>)) -> Result<Value, Error> {
let uri = try_as_uri("http::head", uri)?;
let opts = match opts {
Some(Value::Object(opts)) => Some(opts),
None => None,
Some(_) => {
return 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."),
}),
}
message: String::from("The second argument should be an object."),
})
}
};
crate::fnc::util::http::head(uri, opts).await
}
#[cfg(feature = "http")]
pub async fn get(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
2 => match args.remove(0) {
Value::Strand(uri) => match args.remove(0) {
Value::Object(opt) => crate::fnc::util::http::get(uri, opt).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::get"),
message: String::from("The second argument should be an object."),
}),
},
_ => Err(Error::InvalidArguments {
pub async fn get((uri, opts): (Value, Option<Value>)) -> Result<Value, Error> {
let uri = try_as_uri("http::get", uri)?;
let opts = match opts {
Some(Value::Object(opts)) => Some(opts),
None => None,
Some(_) => {
return 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."),
}),
}
message: String::from("The second argument should be an object."),
})
}
};
crate::fnc::util::http::get(uri, opts).await
}
#[cfg(feature = "http")]
pub async fn put(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
3 => match (args.remove(0), args.remove(0)) {
(Value::Strand(uri), val) => match args.remove(0) {
Value::Object(opts) => crate::fnc::util::http::put(uri, val, opts).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::put"),
message: String::from("The third argument should be an object."),
}),
},
_ => Err(Error::InvalidArguments {
pub async fn put((uri, body, opts): (Value, Option<Value>, Option<Value>)) -> Result<Value, Error> {
let uri = try_as_uri("http::put", uri)?;
let opts = match opts {
Some(Value::Object(opts)) => Some(opts),
None => None,
Some(_) => {
return 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."),
}),
}
message: String::from("The third argument should be an object."),
})
}
};
crate::fnc::util::http::put(uri, body.unwrap_or(Value::Null), opts).await
}
#[cfg(feature = "http")]
pub async fn post(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
3 => match (args.remove(0), args.remove(0)) {
(Value::Strand(uri), val) => match args.remove(0) {
Value::Object(opts) => crate::fnc::util::http::post(uri, val, opts).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::post"),
message: String::from("The third argument should be an object."),
}),
},
_ => Err(Error::InvalidArguments {
pub async fn post(
(uri, body, opts): (Value, Option<Value>, Option<Value>),
) -> Result<Value, Error> {
let uri = try_as_uri("http::post", uri)?;
let opts = match opts {
Some(Value::Object(opts)) => Some(opts),
None => None,
Some(_) => {
return 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."),
}),
}
message: String::from("The third argument should be an object."),
})
}
};
crate::fnc::util::http::post(uri, body.unwrap_or(Value::Null), opts).await
}
#[cfg(feature = "http")]
pub async fn patch(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
3 => match (args.remove(0), args.remove(0)) {
(Value::Strand(uri), val) => match args.remove(0) {
Value::Object(opts) => crate::fnc::util::http::patch(uri, val, opts).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::patch"),
message: String::from("The third argument should be an object."),
}),
},
_ => Err(Error::InvalidArguments {
pub async fn patch(
(uri, body, opts): (Value, Option<Value>, Option<Value>),
) -> Result<Value, Error> {
let uri = try_as_uri("http::patch", uri)?;
let opts = match opts {
Some(Value::Object(opts)) => Some(opts),
None => None,
Some(_) => {
return 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."),
}),
}
message: String::from("The third argument should be an object."),
})
}
};
crate::fnc::util::http::patch(uri, body.unwrap_or(Value::Null), opts).await
}
#[cfg(feature = "http")]
pub async fn delete(_: &Context<'_>, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
2 => match args.remove(0) {
Value::Strand(uri) => match args.remove(0) {
Value::Object(opt) => crate::fnc::util::http::delete(uri, opt).await,
_ => Err(Error::InvalidArguments {
name: String::from("http::delete"),
message: String::from("The second argument should be an object."),
}),
},
_ => Err(Error::InvalidArguments {
pub async fn delete((uri, opts): (Value, Option<Value>)) -> Result<Value, Error> {
let uri = try_as_uri("http::delete", uri)?;
let opts = match opts {
Some(Value::Object(opts)) => Some(opts),
None => None,
Some(_) => {
return 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."),
}),
}
message: String::from("The second argument should be an object."),
})
}
};
crate::fnc::util::http::delete(uri, opts).await
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,4 @@
use crate::cnf::ID_CHARS;
use crate::ctx::Context;
use crate::err::Error;
use crate::sql::datetime::Datetime;
use crate::sql::uuid::Uuid;
@ -9,15 +8,15 @@ use rand::distributions::Alphanumeric;
use rand::prelude::IteratorRandom;
use rand::Rng;
pub fn rand(_: &Context, _: Vec<Value>) -> Result<Value, Error> {
pub fn rand(_: ()) -> Result<Value, Error> {
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())
}
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() {
0 => Value::None,
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> {
match args.len() {
2 => {
let min = args.remove(0).as_float();
match args.remove(0).as_float() {
max if max < min => Ok(rand::thread_rng().gen_range(max..=min).into()),
max => Ok(rand::thread_rng().gen_range(min..=max).into()),
}
pub fn float((range,): (Option<(f64, f64)>,)) -> Result<Value, Error> {
Ok(if let Some((min, max)) = range {
if max < min {
rand::thread_rng().gen_range(max..=min)
} else {
rand::thread_rng().gen_range(min..=max)
}
0 => Ok(rand::random::<f64>().into()),
_ => unreachable!(),
} else {
rand::random::<f64>()
}
.into())
}
pub fn guid(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
1 => {
// Only need 53 to uniquely identify all atoms in observable universe.
const LIMIT: usize = 64;
let len = args.remove(0).as_int() as usize;
if len > LIMIT {
Err(Error::InvalidArguments {
name: String::from("rand::guid"),
message: format!("The maximum length of a GUID is {}.", LIMIT),
})
} else {
Ok(nanoid!(len, &ID_CHARS).into())
}
pub fn guid((len,): (Option<usize>,)) -> Result<Value, Error> {
// Only need 53 to uniquely identify all atoms in observable universe.
const LIMIT: usize = 64;
let len = match len {
Some(len) if len <= LIMIT => len,
None => 20,
_ => {
return Err(Error::InvalidArguments {
name: String::from("rand::guid"),
message: format!("The maximum length of a GUID is {}.", LIMIT),
})
}
0 => Ok(nanoid!(20, &ID_CHARS).into()),
_ => unreachable!(),
}
};
Ok(nanoid!(len, &ID_CHARS).into())
}
pub fn int(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
2 => {
let min = args.remove(0).as_int();
match args.remove(0).as_int() {
max if max < min => Ok(rand::thread_rng().gen_range(max..=min).into()),
max => Ok(rand::thread_rng().gen_range(min..=max).into()),
}
pub fn int((range,): (Option<(i64, i64)>,)) -> Result<Value, Error> {
Ok(if let Some((min, max)) = range {
if max < min {
rand::thread_rng().gen_range(max..=min)
} else {
rand::thread_rng().gen_range(min..=max)
}
0 => Ok(rand::random::<i64>().into()),
_ => unreachable!(),
} else {
rand::random::<i64>()
}
.into())
}
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.
const LIMIT: i64 = 2i64.pow(16);
match args.len() {
2 => match args.remove(0).as_int() {
min if (0..=LIMIT).contains(&min) => match args.remove(0).as_int() {
max if min <= max && max <= LIMIT => Ok(rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(rand::thread_rng().gen_range(min as usize..=max as usize))
.map(char::from)
.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 {
let len = if let Some((min, max)) = arg1.zip(arg2) {
match min {
min if (0..=LIMIT).contains(&min) => match max {
max if min <= max && max <= LIMIT => 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),
_ => return Err(Error::InvalidArguments {
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),
}),
},
_ => Err(Error::InvalidArguments {
_ => return Err(Error::InvalidArguments {
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),
}),
},
1 => match args.remove(0).as_int() {
x if (0..=LIMIT).contains(&x) => Ok(rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(x as usize)
.map(char::from)
.collect::<String>()
.into()),
_ => Err(Error::InvalidArguments {
}
} else if let Some(len) = arg1 {
if (0..=LIMIT).contains(&len) {
len as usize
} else {
return Err(Error::InvalidArguments {
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),
}),
},
0 => Ok(rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect::<String>()
.into()),
_ => unreachable!(),
}
});
}
} else {
32
};
Ok(rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(len)
.map(char::from)
.collect::<String>()
.into())
}
pub fn time(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
match args.len() {
2 => {
let min = args.remove(0).as_int();
match args.remove(0).as_int() {
max if max < min => {
let i = rand::thread_rng().gen_range(max..=min);
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 time((range,): (Option<(i64, i64)>,)) -> Result<Value, Error> {
let i = if let Some((min, max)) = range {
let range = if max < min {
max..=min
} else {
min..=max
};
rand::thread_rng().gen_range(range)
} else {
rand::random::<i32>() as i64
};
Ok(Datetime::from(i).into())
}
pub fn uuid(_: &Context, _: Vec<Value>) -> Result<Value, Error> {
pub fn uuid(_: ()) -> Result<Value, Error> {
Ok(Uuid::new().into())
}

View file

@ -10,34 +10,34 @@ use crate::sql::paths::SD;
use crate::sql::paths::TK;
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()
}
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()
}
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()
}
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()
}
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()
}
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()
}
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()
}
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()
}

View file

@ -1,41 +1,37 @@
use crate::ctx::Context;
use crate::err::Error;
use crate::fnc::util::string;
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())
}
pub fn ends_with(_: &Context, args: Vec<Value>) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap();
let [val, chr] = args.map(Value::as_string);
pub fn ends_with((val, chr): (String, String)) -> Result<Value, Error> {
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 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
// https://github.com/rust-lang/rust/issues/79524
let val = args.collect::<Vec<_>>().join(&chr);
Ok(val.into())
}
pub fn length(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
let val = args.remove(0).as_string();
let num = val.chars().count() as i64;
pub fn length((string,): (String,)) -> Result<Value, Error> {
let num = string.chars().count() as i64;
Ok(num.into())
}
pub fn lowercase(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
Ok(args.remove(0).as_string().to_lowercase().into())
pub fn lowercase((string,): (String,)) -> Result<Value, Error> {
Ok(string.to_lowercase().into())
}
pub fn repeat(_: &Context, args: Vec<Value>) -> 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;
pub fn repeat((val, num): (String, usize)) -> Result<Value, Error> {
const LIMIT: usize = 2usize.pow(20);
if val.len().saturating_mul(num) > LIMIT {
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> {
let args: [Value; 3] = args.try_into().unwrap();
let [val, old, new] = args.map(Value::as_string);
pub fn replace((val, old, new): (String, String, String)) -> Result<Value, Error> {
Ok(val.replace(&old, &new).into())
}
pub fn reverse(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
Ok(args.remove(0).as_string().chars().rev().collect::<String>().into())
pub fn reverse((string,): (String,)) -> Result<Value, Error> {
Ok(string.chars().rev().collect::<String>().into())
}
pub fn slice(_: &Context, args: Vec<Value>) -> Result<Value, Error> {
let [val_arg, beg_arg, lim_arg]: [Value; 3] = args.try_into().unwrap();
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 slice((val, beg, lim): (String, usize, usize)) -> Result<Value, Error> {
Ok(val.chars().skip(beg).take(lim).collect::<String>().into())
}
pub fn slug(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
Ok(string::slug(&args.remove(0).as_string()).into())
pub fn slug((string,): (String,)) -> Result<Value, Error> {
Ok(string::slug(&string).into())
}
pub fn split(_: &Context, args: Vec<Value>) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap();
let [val, chr] = args.map(Value::as_string);
let val = val.split(&chr).collect::<Vec<&str>>();
Ok(val.into())
pub fn split((val, chr): (String, String)) -> Result<Value, Error> {
Ok(val.split(&chr).collect::<Vec<&str>>().into())
}
pub fn starts_with(_: &Context, args: Vec<Value>) -> Result<Value, Error> {
let args: [Value; 2] = args.try_into().unwrap();
let [val, chr] = args.map(Value::as_string);
pub fn starts_with((val, chr): (String, String)) -> Result<Value, Error> {
Ok(val.starts_with(&chr).into())
}
pub fn trim(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
Ok(args.remove(0).as_string().trim().into())
pub fn trim((string,): (String,)) -> Result<Value, Error> {
Ok(string.trim().into())
}
pub fn uppercase(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
Ok(args.remove(0).as_string().to_uppercase().into())
pub fn uppercase((string,): (String,)) -> Result<Value, Error> {
Ok(string.to_uppercase().into())
}
pub fn words(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
Ok(args.remove(0).as_string().split_whitespace().collect::<Vec<&str>>().into())
pub fn words((string,): (String,)) -> Result<Value, Error> {
Ok(string.split_whitespace().collect::<Vec<&str>>().into())
}

View file

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

View file

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