Enable ability to pass arguments to JavaScript functions
This commit is contained in:
parent
3f4a144ef0
commit
219f4a54ed
3 changed files with 84 additions and 61 deletions
|
@ -105,16 +105,20 @@ impl Class for JsRecord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(ctx: &Context, doc: Option<&Value>, src: &str) -> Result<Value, Error> {
|
pub fn run(ctx: &Context, src: &str, arg: Vec<Value>, doc: Option<&Value>) -> Result<Value, Error> {
|
||||||
let _ = ctx.check()?;
|
let _ = ctx.check()?;
|
||||||
// Create an execution context
|
// Create an execution context
|
||||||
let mut ctx = Boa::default();
|
let mut ctx = Boa::default();
|
||||||
// Retrieve the current document
|
// Convert the arguments to JavaScript
|
||||||
let obj = doc.map_or(JsValue::Undefined, JsValue::from);
|
let args = JsValue::from(Value::from(arg));
|
||||||
|
// Convert the current document to JavaScript
|
||||||
|
let this = doc.map_or(JsValue::Undefined, JsValue::from);
|
||||||
// Create the main function structure
|
// Create the main function structure
|
||||||
let src = format!("(function() {{ {} }}).call(document)", src);
|
let src = format!("(function() {{ {} }}).apply(self, args)", src);
|
||||||
// Register the current document as a global object
|
// Register the current document as a global object
|
||||||
ctx.register_global_property("document", obj, Attribute::default());
|
ctx.register_global_property("self", this, Attribute::default());
|
||||||
|
// Register the current document as a global object
|
||||||
|
ctx.register_global_property("args", args, Attribute::default());
|
||||||
// Register the JsDuration type as a global class
|
// Register the JsDuration type as a global class
|
||||||
ctx.register_global_class::<JsDuration>().unwrap();
|
ctx.register_global_class::<JsDuration>().unwrap();
|
||||||
// Register the JsRecord type as a global class
|
// Register the JsRecord type as a global class
|
||||||
|
@ -130,6 +134,12 @@ pub fn run(ctx: &Context, doc: Option<&Value>, src: &str) -> Result<Value, Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Value> for JsValue {
|
||||||
|
fn from(v: Value) -> Self {
|
||||||
|
JsValue::from(&v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&Datetime> for Date {
|
impl From<&Datetime> for Date {
|
||||||
fn from(v: &Datetime) -> Self {
|
fn from(v: &Datetime) -> Self {
|
||||||
let mut obj = Self::default();
|
let mut obj = Self::default();
|
||||||
|
|
|
@ -6,12 +6,11 @@ use crate::fnc;
|
||||||
use crate::sql::comment::mightbespace;
|
use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::common::commas;
|
use crate::sql::common::commas;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::script::{script, Script};
|
use crate::sql::script::{script as func, Script};
|
||||||
use crate::sql::value::{single, value, Value};
|
use crate::sql::value::{single, value, Value};
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
use nom::combinator::opt;
|
|
||||||
use nom::multi::separated_list0;
|
use nom::multi::separated_list0;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
@ -20,9 +19,9 @@ use std::fmt;
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Function {
|
pub enum Function {
|
||||||
Future(Value),
|
Future(Value),
|
||||||
Script(Script),
|
|
||||||
Cast(String, Value),
|
Cast(String, Value),
|
||||||
Normal(String, Vec<Value>),
|
Normal(String, Vec<Value>),
|
||||||
|
Script(Script, Vec<Value>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Function {
|
impl PartialOrd for Function {
|
||||||
|
@ -111,32 +110,36 @@ impl Function {
|
||||||
doc: Option<&Value>,
|
doc: Option<&Value>,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
match self {
|
match self {
|
||||||
Function::Cast(s, e) => {
|
Function::Future(v) => match opt.futures {
|
||||||
let a = e.compute(ctx, opt, txn, doc).await?;
|
true => {
|
||||||
fnc::cast::run(ctx, s, a)
|
let v = v.compute(ctx, opt, txn, doc).await?;
|
||||||
|
fnc::future::run(ctx, v)
|
||||||
|
}
|
||||||
|
false => Ok(self.to_owned().into()),
|
||||||
|
},
|
||||||
|
Function::Cast(s, x) => {
|
||||||
|
let v = x.compute(ctx, opt, txn, doc).await?;
|
||||||
|
fnc::cast::run(ctx, s, v)
|
||||||
}
|
}
|
||||||
Function::Normal(s, e) => {
|
Function::Normal(s, x) => {
|
||||||
let mut a: Vec<Value> = Vec::with_capacity(e.len());
|
let mut a: Vec<Value> = Vec::with_capacity(x.len());
|
||||||
for v in e {
|
for v in x {
|
||||||
a.push(v.compute(ctx, opt, txn, doc).await?);
|
a.push(v.compute(ctx, opt, txn, doc).await?);
|
||||||
}
|
}
|
||||||
fnc::run(ctx, s, a).await
|
fnc::run(ctx, s, a).await
|
||||||
}
|
}
|
||||||
Function::Future(e) => match opt.futures {
|
Function::Script(s, x) => {
|
||||||
true => {
|
if cfg!(feature = "scripting") {
|
||||||
let a = e.compute(ctx, opt, txn, doc).await?;
|
let mut a: Vec<Value> = Vec::with_capacity(x.len());
|
||||||
fnc::future::run(ctx, a)
|
for v in x {
|
||||||
|
a.push(v.compute(ctx, opt, txn, doc).await?);
|
||||||
|
}
|
||||||
|
fnc::script::run(ctx, s, a, doc)
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidScript {
|
||||||
|
message: String::from("Embedded functions are not enabled."),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
false => Ok(self.to_owned().into()),
|
|
||||||
},
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
Function::Script(s) => {
|
|
||||||
#[cfg(feature = "scripting")]
|
|
||||||
return fnc::script::run(ctx, doc, s);
|
|
||||||
#[cfg(not(feature = "scripting"))]
|
|
||||||
return Err(Error::InvalidScript {
|
|
||||||
message: String::from("Embedded functions are not enabled."),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,8 +149,13 @@ impl fmt::Display for Function {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Function::Future(ref e) => write!(f, "fn::future -> {{ {} }}", e),
|
Function::Future(ref e) => write!(f, "fn::future -> {{ {} }}", e),
|
||||||
Function::Script(ref s) => write!(f, "fn::script -> {{{}}}", s),
|
|
||||||
Function::Cast(ref s, ref e) => write!(f, "<{}> {}", s, e),
|
Function::Cast(ref s, ref e) => write!(f, "<{}> {}", s, e),
|
||||||
|
Function::Script(ref s, ref e) => write!(
|
||||||
|
f,
|
||||||
|
"fn::script -> ({}) => {{{}}}",
|
||||||
|
e.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", "),
|
||||||
|
s,
|
||||||
|
),
|
||||||
Function::Normal(ref s, ref e) => write!(
|
Function::Normal(ref s, ref e) => write!(
|
||||||
f,
|
f,
|
||||||
"{}({})",
|
"{}({})",
|
||||||
|
@ -159,21 +167,7 @@ impl fmt::Display for Function {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn function(i: &str) -> IResult<&str, Function> {
|
pub fn function(i: &str) -> IResult<&str, Function> {
|
||||||
alt((scripts, future, casts, normal))(i)
|
alt((future, normal, script, cast))(i)
|
||||||
}
|
|
||||||
|
|
||||||
fn scripts(i: &str) -> IResult<&str, Function> {
|
|
||||||
let (i, _) = tag("fn::script")(i)?;
|
|
||||||
let (i, _) = mightbespace(i)?;
|
|
||||||
let (i, _) = char('-')(i)?;
|
|
||||||
let (i, _) = char('>')(i)?;
|
|
||||||
let (i, _) = mightbespace(i)?;
|
|
||||||
let (i, _) = opt(tag("function()"))(i)?;
|
|
||||||
let (i, _) = mightbespace(i)?;
|
|
||||||
let (i, _) = char('{')(i)?;
|
|
||||||
let (i, v) = script(i)?;
|
|
||||||
let (i, _) = char('}')(i)?;
|
|
||||||
Ok((i, Function::Script(v)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn future(i: &str) -> IResult<&str, Function> {
|
fn future(i: &str) -> IResult<&str, Function> {
|
||||||
|
@ -190,7 +184,35 @@ fn future(i: &str) -> IResult<&str, Function> {
|
||||||
Ok((i, Function::Future(v)))
|
Ok((i, Function::Future(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn casts(i: &str) -> IResult<&str, Function> {
|
fn normal(i: &str) -> IResult<&str, Function> {
|
||||||
|
let (i, s) = function_names(i)?;
|
||||||
|
let (i, _) = char('(')(i)?;
|
||||||
|
let (i, _) = mightbespace(i)?;
|
||||||
|
let (i, a) = separated_list0(commas, value)(i)?;
|
||||||
|
let (i, _) = mightbespace(i)?;
|
||||||
|
let (i, _) = char(')')(i)?;
|
||||||
|
Ok((i, Function::Normal(s.to_string(), a)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn script(i: &str) -> IResult<&str, Function> {
|
||||||
|
let (i, _) = tag("fn::script")(i)?;
|
||||||
|
let (i, _) = mightbespace(i)?;
|
||||||
|
let (i, _) = char('-')(i)?;
|
||||||
|
let (i, _) = char('>')(i)?;
|
||||||
|
let (i, _) = mightbespace(i)?;
|
||||||
|
let (i, _) = tag("(")(i)?;
|
||||||
|
let (i, a) = separated_list0(commas, value)(i)?;
|
||||||
|
let (i, _) = tag(")")(i)?;
|
||||||
|
let (i, _) = mightbespace(i)?;
|
||||||
|
let (i, _) = tag("=>")(i)?;
|
||||||
|
let (i, _) = mightbespace(i)?;
|
||||||
|
let (i, _) = char('{')(i)?;
|
||||||
|
let (i, v) = func(i)?;
|
||||||
|
let (i, _) = char('}')(i)?;
|
||||||
|
Ok((i, Function::Script(v, a)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast(i: &str) -> IResult<&str, Function> {
|
||||||
let (i, _) = char('<')(i)?;
|
let (i, _) = char('<')(i)?;
|
||||||
let (i, s) = function_casts(i)?;
|
let (i, s) = function_casts(i)?;
|
||||||
let (i, _) = char('>')(i)?;
|
let (i, _) = char('>')(i)?;
|
||||||
|
@ -199,16 +221,6 @@ fn casts(i: &str) -> IResult<&str, Function> {
|
||||||
Ok((i, Function::Cast(s.to_string(), v)))
|
Ok((i, Function::Cast(s.to_string(), v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normal(i: &str) -> IResult<&str, Function> {
|
|
||||||
let (i, s) = function_names(i)?;
|
|
||||||
let (i, _) = char('(')(i)?;
|
|
||||||
let (i, _) = mightbespace(i)?;
|
|
||||||
let (i, v) = separated_list0(commas, value)(i)?;
|
|
||||||
let (i, _) = mightbespace(i)?;
|
|
||||||
let (i, _) = char(')')(i)?;
|
|
||||||
Ok((i, Function::Normal(s.to_string(), v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_casts(i: &str) -> IResult<&str, &str> {
|
fn function_casts(i: &str) -> IResult<&str, &str> {
|
||||||
alt((
|
alt((
|
||||||
tag("bool"),
|
tag("bool"),
|
||||||
|
@ -495,19 +507,20 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_script_expression() {
|
fn function_script_expression() {
|
||||||
let sql = "fn::script -> { return this.tags.filter(t => { return t.length > 3; }); }";
|
let sql = "fn::script -> () => { return this.tags.filter(t => { return t.length > 3; }); }";
|
||||||
let res = function(sql);
|
let res = function(sql);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"fn::script -> { return this.tags.filter(t => { return t.length > 3; }); }",
|
"fn::script -> () => { return this.tags.filter(t => { return t.length > 3; }); }",
|
||||||
format!("{}", out)
|
format!("{}", out)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out,
|
out,
|
||||||
Function::Script(Script::parse(
|
Function::Script(
|
||||||
" return this.tags.filter(t => { return t.length > 3; }); "
|
Script::parse(" return this.tags.filter(t => { return t.length > 3; }); "),
|
||||||
))
|
vec![]
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -668,7 +668,7 @@ impl Value {
|
||||||
Value::Datetime(v) => v.0.to_string().into(),
|
Value::Datetime(v) => v.0.to_string().into(),
|
||||||
Value::Function(v) => match v.as_ref() {
|
Value::Function(v) => match v.as_ref() {
|
||||||
Function::Future(_) => "fn::future".to_string().into(),
|
Function::Future(_) => "fn::future".to_string().into(),
|
||||||
Function::Script(_) => "fn::script".to_string().into(),
|
Function::Script(_, _) => "fn::script".to_string().into(),
|
||||||
Function::Normal(f, _) => f.to_string().into(),
|
Function::Normal(f, _) => f.to_string().into(),
|
||||||
Function::Cast(_, v) => v.to_idiom(),
|
Function::Cast(_, v) => v.to_idiom(),
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue