Feature: Add type::range
function (#3748)
Co-authored-by: Rushmore Mushambi <rushmore@webenchanter.com>
This commit is contained in:
parent
b03aeca08c
commit
6375bd45a6
7 changed files with 132 additions and 1 deletions
|
@ -321,6 +321,7 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Va
|
||||||
"type::string" => r#type::string,
|
"type::string" => r#type::string,
|
||||||
"type::table" => r#type::table,
|
"type::table" => r#type::table,
|
||||||
"type::thing" => r#type::thing,
|
"type::thing" => r#type::thing,
|
||||||
|
"type::range" => r#type::range,
|
||||||
"type::is::array" => r#type::is::array,
|
"type::is::array" => r#type::is::array,
|
||||||
"type::is::bool" => r#type::is::bool,
|
"type::is::bool" => r#type::is::bool,
|
||||||
"type::is::bytes" => r#type::is::bytes,
|
"type::is::bytes" => r#type::is::bytes,
|
||||||
|
|
|
@ -25,5 +25,6 @@ impl_module_def!(
|
||||||
"regex" => run,
|
"regex" => run,
|
||||||
"string" => run,
|
"string" => run,
|
||||||
"table" => run,
|
"table" => run,
|
||||||
"thing" => run
|
"thing" => run,
|
||||||
|
"range" => run
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::ops::Bound;
|
||||||
|
|
||||||
use crate::ctx::Context;
|
use crate::ctx::Context;
|
||||||
use crate::dbs::{Options, Transaction};
|
use crate::dbs::{Options, Transaction};
|
||||||
use crate::doc::CursorDoc;
|
use crate::doc::CursorDoc;
|
||||||
|
@ -5,6 +7,7 @@ use crate::err::Error;
|
||||||
use crate::sql::table::Table;
|
use crate::sql::table::Table;
|
||||||
use crate::sql::thing::Thing;
|
use crate::sql::thing::Thing;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
|
use crate::sql::{Id, Range, Strand};
|
||||||
use crate::syn;
|
use crate::syn;
|
||||||
|
|
||||||
pub fn bool((val,): (Value,)) -> Result<Value, Error> {
|
pub fn bool((val,): (Value,)) -> Result<Value, Error> {
|
||||||
|
@ -128,6 +131,93 @@ pub fn thing((arg1, arg2): (Value, Option<Value>)) -> Result<Value, Error> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn range(args: Vec<Value>) -> Result<Value, Error> {
|
||||||
|
if args.len() > 4 || args.is_empty() {
|
||||||
|
return Err(Error::InvalidArguments {
|
||||||
|
name: "type::range".to_owned(),
|
||||||
|
message: "Expected atleast 1 and at most 4 arguments".to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut args = args.into_iter();
|
||||||
|
|
||||||
|
// Unwrap will never trigger since length is checked above.
|
||||||
|
let id = args.next().unwrap().as_string();
|
||||||
|
let start = args.next().and_then(|x| match x {
|
||||||
|
Value::Thing(v) => Some(v.id),
|
||||||
|
Value::Array(v) => Some(v.into()),
|
||||||
|
Value::Object(v) => Some(v.into()),
|
||||||
|
Value::Number(v) => Some(v.into()),
|
||||||
|
Value::Null | Value::None => None,
|
||||||
|
v => Some(Id::from(v.as_string())),
|
||||||
|
});
|
||||||
|
let end = args.next().and_then(|x| match x {
|
||||||
|
Value::Thing(v) => Some(v.id),
|
||||||
|
Value::Array(v) => Some(v.into()),
|
||||||
|
Value::Object(v) => Some(v.into()),
|
||||||
|
Value::Number(v) => Some(v.into()),
|
||||||
|
Value::Null | Value::None => None,
|
||||||
|
v => Some(Id::from(v.as_string())),
|
||||||
|
});
|
||||||
|
let (begin, end) = if let Some(x) = args.next() {
|
||||||
|
let Value::Object(x) = x else {
|
||||||
|
return Err(Error::ConvertTo {
|
||||||
|
from: x,
|
||||||
|
into: "object".to_owned(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let begin = if let Some(x) = x.get("begin") {
|
||||||
|
let start = start.ok_or_else(|| Error::InvalidArguments {
|
||||||
|
name: "type::range".to_string(),
|
||||||
|
message: "Can't define an inclusion for begin if there is no begin bound"
|
||||||
|
.to_string(),
|
||||||
|
})?;
|
||||||
|
match x {
|
||||||
|
Value::Strand(Strand(x)) if x == "included" => Bound::Included(start),
|
||||||
|
Value::Strand(Strand(x)) if x == "excluded" => Bound::Excluded(start),
|
||||||
|
x => {
|
||||||
|
return Err(Error::ConvertTo {
|
||||||
|
from: x.clone(),
|
||||||
|
into: r#""included" | "excluded""#.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
start.map(Bound::Included).unwrap_or(Bound::Unbounded)
|
||||||
|
};
|
||||||
|
let end = if let Some(x) = x.get("end") {
|
||||||
|
let end = end.ok_or_else(|| Error::InvalidArguments {
|
||||||
|
name: "type::range".to_string(),
|
||||||
|
message: "Can't define an inclusion for end if there is no end bound".to_string(),
|
||||||
|
})?;
|
||||||
|
match x {
|
||||||
|
Value::Strand(Strand(x)) if x == "included" => Bound::Included(end),
|
||||||
|
Value::Strand(Strand(x)) if x == "excluded" => Bound::Excluded(end),
|
||||||
|
x => {
|
||||||
|
return Err(Error::ConvertTo {
|
||||||
|
from: x.clone(),
|
||||||
|
into: r#""included" | "excluded""#.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
end.map(Bound::Excluded).unwrap_or(Bound::Unbounded)
|
||||||
|
};
|
||||||
|
(begin, end)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start.map(Bound::Included).unwrap_or(Bound::Unbounded),
|
||||||
|
end.map(Bound::Excluded).unwrap_or(Bound::Unbounded),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Range {
|
||||||
|
tb: id,
|
||||||
|
beg: begin,
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub mod is {
|
pub mod is {
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::table::Table;
|
use crate::sql::table::Table;
|
||||||
|
|
|
@ -458,6 +458,7 @@ pub(crate) fn builtin_name(i: &str) -> IResult<&str, BuiltinName<&str>, ParseErr
|
||||||
string => { fn },
|
string => { fn },
|
||||||
table => { fn },
|
table => { fn },
|
||||||
thing => { fn },
|
thing => { fn },
|
||||||
|
range => { fn },
|
||||||
is => {
|
is => {
|
||||||
array => { fn },
|
array => { fn },
|
||||||
r#bool = "bool" => { fn },
|
r#bool = "bool" => { fn },
|
||||||
|
|
|
@ -314,6 +314,7 @@ pub(crate) static PATHS: phf::Map<UniCase<&'static str>, PathKind> = phf_map! {
|
||||||
UniCase::ascii("type::string") => PathKind::Function,
|
UniCase::ascii("type::string") => PathKind::Function,
|
||||||
UniCase::ascii("type::table") => PathKind::Function,
|
UniCase::ascii("type::table") => PathKind::Function,
|
||||||
UniCase::ascii("type::thing") => PathKind::Function,
|
UniCase::ascii("type::thing") => PathKind::Function,
|
||||||
|
UniCase::ascii("type::range") => PathKind::Function,
|
||||||
UniCase::ascii("type::is::array") => PathKind::Function,
|
UniCase::ascii("type::is::array") => PathKind::Function,
|
||||||
UniCase::ascii("type::is::bool") => PathKind::Function,
|
UniCase::ascii("type::is::bool") => PathKind::Function,
|
||||||
UniCase::ascii("type::is::bytes") => PathKind::Function,
|
UniCase::ascii("type::is::bytes") => PathKind::Function,
|
||||||
|
|
|
@ -393,6 +393,7 @@
|
||||||
"type::string("
|
"type::string("
|
||||||
"type::table("
|
"type::table("
|
||||||
"type::thing("
|
"type::thing("
|
||||||
|
"type::range("
|
||||||
"vector::add("
|
"vector::add("
|
||||||
"vector::angle("
|
"vector::angle("
|
||||||
"vector::cross("
|
"vector::cross("
|
||||||
|
|
|
@ -5702,6 +5702,42 @@ async fn function_type_thing() -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn function_type_range() -> Result<(), Error> {
|
||||||
|
let sql = r#"
|
||||||
|
RETURN type::range('person');
|
||||||
|
RETURN type::range('person',1);
|
||||||
|
RETURN type::range('person',null,10);
|
||||||
|
RETURN type::range('person',1,10);
|
||||||
|
RETURN type::range('person',1,10, { begin: "excluded", end: "included"});
|
||||||
|
"#;
|
||||||
|
let dbs = new_ds().await?;
|
||||||
|
let ses = Session::owner().with_ns("test").with_db("test");
|
||||||
|
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||||
|
assert_eq!(res.len(), 5);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("person:..");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("person:1..");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("person:..10");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("person:1..10");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("person:1>..=10");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn function_vector_add() -> Result<(), Error> {
|
async fn function_vector_add() -> Result<(), Error> {
|
||||||
test_queries(
|
test_queries(
|
||||||
|
|
Loading…
Reference in a new issue