feat: rc variable (#246)

* feat: rc variable

* feat: implement variable in engine

* add exports

* fix bindings and improve clone accuracy

* fix tests, fix bindings

* fix fmt

* improve code
This commit is contained in:
stefan-gorules
2024-09-30 18:34:54 +02:00
committed by GitHub
parent 06df117694
commit 91598166ce
55 changed files with 1148 additions and 1399 deletions
+6 -7
View File
@@ -1,15 +1,14 @@
use std::sync::Arc;
use rquickjs::promise::MaybePromise;
use rquickjs::{async_with, AsyncContext, AsyncRuntime, CatchResultExt, Ctx, Module};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::handler::function::error::{FunctionError, FunctionResult, ResultExt};
use crate::handler::function::listener::{RuntimeEvent, RuntimeListener};
use crate::handler::function::module::console::{Console, Log};
use crate::handler::function::module::ModuleLoader;
use crate::handler::function::serde::JsValue;
use rquickjs::promise::MaybePromise;
use rquickjs::{async_with, AsyncContext, AsyncRuntime, CatchResultExt, Ctx, Module};
use serde::{Deserialize, Serialize};
use zen_expression::variable::Variable;
pub struct FunctionConfig {
pub(crate) listeners: Option<Vec<Box<dyn RuntimeListener>>>,
@@ -49,7 +48,7 @@ impl Function {
};
}
return Ok(());
Ok(())
}
async fn dispatch_event(&self, event: RuntimeEvent) -> FunctionResult {
@@ -121,5 +120,5 @@ impl Function {
#[derive(Serialize, Deserialize)]
pub struct HandlerResponse {
pub logs: Vec<Log>,
pub data: Value,
pub data: Variable,
}
+17 -17
View File
@@ -1,3 +1,6 @@
use crate::handler::function::error::ResultExt;
use crate::handler::function::module::export_default;
use crate::handler::function::serde::JsValue;
use reqwest::header::{HeaderMap, HeaderName};
use reqwest::Method;
use rquickjs::module::{Declarations, Exports, ModuleDef};
@@ -5,10 +8,7 @@ use rquickjs::prelude::{Async, Func, Opt};
use rquickjs::{CatchResultExt, Ctx, FromJs, IntoAtom, IntoJs, Object, Value};
use std::str::FromStr;
use std::sync::OnceLock;
use crate::handler::function::error::ResultExt;
use crate::handler::function::module::export_default;
use crate::handler::function::serde::JsValue;
use zen_expression::variable::Variable;
pub(crate) struct HttpResponse<'js> {
data: Value<'js>,
@@ -62,7 +62,7 @@ async fn execute_http<'js>(
)?;
}
let data: serde_json::Value = response.json().await.or_throw(&ctx)?;
let data: Variable = response.json().await.or_throw(&ctx)?;
Ok(HttpResponse {
data: JsValue(data).into_js(&ctx)?,
@@ -91,12 +91,12 @@ impl<'js> FromJs<'js> for HttpConfig {
let value = JsValue::from_js(ctx, value)?;
let str_value = match value.0 {
serde_json::Value::Null => None,
serde_json::Value::Bool(b) => Some(b.to_string()),
serde_json::Value::Number(n) => Some(n.to_string()),
serde_json::Value::String(s) => Some(s),
serde_json::Value::Array(_) => None,
serde_json::Value::Object(_) => None,
Variable::Null => None,
Variable::Bool(b) => Some(b.to_string()),
Variable::Number(n) => Some(n.to_string()),
Variable::String(s) => Some(s.to_string()),
Variable::Array(_) => None,
Variable::Object(_) => None,
};
let key_value = key.to_string()?;
@@ -121,12 +121,12 @@ impl<'js> FromJs<'js> for HttpConfig {
let value = JsValue::from_js(ctx, value)?;
let str_value = match value.0 {
serde_json::Value::Null => None,
serde_json::Value::Bool(b) => Some(b.to_string()),
serde_json::Value::Number(n) => Some(n.to_string()),
serde_json::Value::String(s) => Some(s),
serde_json::Value::Array(_) => None,
serde_json::Value::Object(_) => None,
Variable::Null => None,
Variable::Bool(b) => Some(b.to_string()),
Variable::Number(n) => Some(n.to_string()),
Variable::String(s) => Some(s.to_string()),
Variable::Array(_) => None,
Variable::Object(_) => None,
};
let key = key.to_string()?;
+10 -12
View File
@@ -2,10 +2,6 @@ use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use rquickjs::module::{Declarations, Exports, ModuleDef};
use rquickjs::prelude::{Async, Func, Opt};
use rquickjs::{CatchResultExt, Ctx, Function, Object};
use crate::handler::custom_node_adapter::CustomNodeAdapter;
use crate::handler::function::error::{FunctionResult, ResultExt};
use crate::handler::function::listener::{RuntimeEvent, RuntimeListener};
@@ -13,6 +9,9 @@ use crate::handler::function::module::export_default;
use crate::handler::function::serde::JsValue;
use crate::handler::graph::{DecisionGraph, DecisionGraphConfig};
use crate::loader::DecisionLoader;
use rquickjs::module::{Declarations, Exports, ModuleDef};
use rquickjs::prelude::{Async, Func, Opt};
use rquickjs::{CatchResultExt, Ctx, Function, Object};
pub(crate) struct ZenListener<Loader, Adapter> {
pub loader: Arc<Loader>,
@@ -68,11 +67,10 @@ impl<Loader: DecisionLoader + 'static, Adapter: CustomNodeAdapter + 'static> Run
})
.or_throw(&ctx)?;
let response =
sub_tree.evaluate(&context.0).await.or_throw(&ctx)?;
return rquickjs::Result::Ok(JsValue(
serde_json::to_value(response).or_throw(&ctx)?,
));
let response = sub_tree.evaluate(context.0).await.or_throw(&ctx)?;
let k = serde_json::to_value(response).or_throw(&ctx)?.into();
return rquickjs::Result::Ok(JsValue(k));
}
},
)),
@@ -89,7 +87,7 @@ fn evaluate_expression<'js>(
expression: String,
context: JsValue,
) -> rquickjs::Result<JsValue> {
let s = zen_expression::evaluate_expression(expression.as_str(), &context.0).or_throw(&ctx)?;
let s = zen_expression::evaluate_expression(expression.as_str(), context.0).or_throw(&ctx)?;
Ok(JsValue(s))
}
@@ -99,8 +97,8 @@ fn evaluate_unary_expression<'js>(
expression: String,
context: JsValue,
) -> rquickjs::Result<bool> {
let s = zen_expression::evaluate_unary_expression(expression.as_str(), &context.0)
.or_throw(&ctx)?;
let s =
zen_expression::evaluate_unary_expression(expression.as_str(), context.0).or_throw(&ctx)?;
Ok(s)
}
+59 -52
View File
@@ -1,84 +1,87 @@
use crate::handler::function::error::ResultExt;
use itertools::Itertools;
use ahash::{HashMap, HashMapExt};
use rquickjs::{Ctx, FromJs, IntoAtom, IntoJs, Type, Value as QValue};
use serde_json::{json, Map, Number, Value};
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::Decimal;
use serde_json::json;
use std::rc::Rc;
use zen_expression::variable::Variable;
#[derive(Debug)]
pub(crate) struct JsValue(pub(crate) Value);
pub(crate) struct JsValue(pub(crate) Variable);
impl<'js> FromJs<'js> for JsValue {
fn from_js(ctx: &Ctx<'js>, v: QValue<'js>) -> rquickjs::Result<Self> {
let computed_value = match v.type_of() {
Type::Uninitialized | Type::Undefined | Type::Null => Value::Null,
Type::Bool => Value::Bool(v.as_bool().or_throw_msg(ctx, "failed to convert to bool")?),
Type::Int => Value::Number(Number::from(
Type::Uninitialized | Type::Undefined | Type::Null => Variable::Null,
Type::Bool => {
Variable::Bool(v.as_bool().or_throw_msg(ctx, "failed to convert to bool")?)
}
Type::Int => Variable::Number(Decimal::from(
v.as_int().or_throw_msg(ctx, "failed to convert to int")?,
)),
Type::BigInt => Value::Number(Number::from(
v.as_big_int()
Type::BigInt => Variable::Number(Decimal::from(
v.into_big_int()
.map(|b| b.clone().to_i64().ok())
.flatten()
.or_throw_msg(ctx, "failed to convert to number")?,
)),
Type::Float => Value::Number(
v.as_float()
.map(|n| Number::from_f64(n))
.flatten()
.or_throw_msg(ctx, "failed to convert to number")?,
Type::Float => Variable::Number(
Decimal::try_from(
v.as_float()
.or_throw_msg(ctx, "failed to convert to number")?,
)
.or_throw_msg(ctx, "failed to convert to number")?,
),
Type::String => Value::String(
v.as_string()
Type::String => Variable::String(
v.into_string()
.map(|s| s.to_string().ok())
.flatten()
.map(|s| Rc::from(s.as_str()))
.or_throw_msg(ctx, "failed to convert to string")?,
),
Type::Array => {
let arr = v
.as_array()
.into_array()
.or_throw_msg(ctx, "failed to convert to array")?;
let js_arr: Vec<Value> = arr
.iter::<QValue>()
.map_ok(|n| JsValue::from_js(ctx, n.clone()).map(|js_val| js_val.0))
.flatten()
.try_collect()
.or_throw(ctx)?;
let mut js_arr = Vec::with_capacity(arr.len());
for x in arr.into_iter() {
js_arr.push(JsValue::from_js(ctx, x.or_throw(ctx)?).or_throw(ctx)?.0)
}
Value::Array(js_arr)
Variable::from_array(js_arr)
}
Type::Object => {
let object = v
.as_object()
.into_object()
.or_throw_msg(ctx, "failed to convert to object")?;
let js_object: Map<String, Value> = object
.props::<String, QValue>()
.map_ok(|(key, value)| {
JsValue::from_js(ctx, value.clone()).map(|js_val| (key, js_val.0))
})
.flatten()
.try_collect()
.or_throw(ctx)?;
let mut js_object = HashMap::with_capacity(object.len());
for p in object.props() {
let (k, v) = p.or_throw(ctx)?;
js_object.insert(k, JsValue::from_js(ctx, v).or_throw(ctx)?.0);
}
Value::Object(js_object)
Variable::from_object(js_object)
}
Type::Exception => {
let exception = v
.as_exception()
.into_exception()
.or_throw_msg(ctx, "failed to convert to exception")?;
let message = exception.message().unwrap_or_default();
let description = exception.to_string();
json!({ "message": message, "description": description })
json!({ "message": message, "description": description }).into()
}
Type::Function => json!("[Function]"),
Type::Module => json!("[Module]"),
Type::Constructor => json!("[Constructor]"),
Type::Symbol => json!("[Symbol]"),
Type::Unknown => json!("[Unknown]"),
Type::Function => json!("[Function]").into(),
Type::Module => json!("[Module]").into(),
Type::Constructor => json!("[Constructor]").into(),
Type::Symbol => json!("[Symbol]").into(),
Type::Unknown => json!("[Unknown]").into(),
Type::Promise => {
let promise = v.as_promise().or_throw(ctx)?;
let promise = v.into_promise().or_throw(ctx)?;
let val: JsValue = promise.finish()?;
val.0
}
@@ -91,26 +94,30 @@ impl<'js> FromJs<'js> for JsValue {
impl<'js> IntoJs<'js> for JsValue {
fn into_js(self, ctx: &Ctx<'js>) -> rquickjs::Result<QValue<'js>> {
let res = match self.0 {
Value::Null => QValue::new_null(ctx.clone()),
Value::Bool(b) => QValue::new_bool(ctx.clone(), b),
Value::Number(n) => QValue::new_number(
Variable::Null => QValue::new_null(ctx.clone()),
Variable::Bool(b) => QValue::new_bool(ctx.clone(), b),
Variable::Number(n) => QValue::new_number(
ctx.clone(),
n.as_f64()
n.to_f64()
.or_throw_msg(ctx, "failed to convert float to number")?,
),
Value::String(str) => str.into_js(ctx)?,
Value::Array(arr) => {
Variable::String(str) => str.into_js(ctx)?,
Variable::Array(a) => {
let qarr = rquickjs::Array::new(ctx.clone())?;
for (idx, item) in arr.into_iter().enumerate() {
qarr.set(idx, JsValue(item))?;
let arr = a.borrow();
for (idx, item) in arr.iter().enumerate() {
qarr.set(idx, JsValue(item.clone()))?;
}
qarr.into_value()
}
Value::Object(map) => {
Variable::Object(o) => {
let qmap = rquickjs::Object::new(ctx.clone())?;
for (key, value) in map.into_iter() {
qmap.set(key.into_atom(ctx)?, JsValue(value))?;
let obj = o.borrow();
for (key, value) in obj.iter() {
qmap.set(key.into_atom(ctx)?, JsValue(value.clone()))?;
}
qmap.into_value()