Skip to content

Commit

Permalink
Fix a bugs in wasm binding, update python bindings for version update. (
Browse files Browse the repository at this point in the history
#25)

* Fixed a bug where has was not searching for accesses correctly

* Fixed another binding issue with wasm
  • Loading branch information
1BADragon authored Nov 15, 2023
1 parent 50aa6d9 commit 421d6d1
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 41 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ wasm-example-release: wasm-binding-release
cd examples/wasm && npm start

.PHONY: all
all: wasm-binding python-binding
all: wasm-binding python-binding
1 change: 1 addition & 0 deletions examples/wasm/src/components/testCases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export const tests: [string, () => any, any][] = [
],
["float", () => cel_eval("3.2 + foo", { foo: 2.1 }), 5.3],
["int", () => cel_eval("3 + foo", { foo: 3 }), BigInt(6)],
["bigint", () => cel_eval("3 + foo", { foo: BigInt(5) }), BigInt(8)],
];
3 changes: 3 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[toolchain]
channel = "nightly"

6 changes: 3 additions & 3 deletions src/bindings/python/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn eval(py: Python<'_>, prog_str: String, bindings: &PyDict) -> PyResult<PyObjec
let mut callables = Vec::new();
for keyobj in bindings.keys().iter() {
let key = keyobj.downcast::<PyString>()?;
let val = bindings.get_item(keyobj).unwrap();
let val = bindings.get_item(keyobj).unwrap().unwrap();

if val.is_callable() {
callables.push((key.to_str()?, CelPyCallable::new(val.into())));
Expand All @@ -35,7 +35,7 @@ fn eval(py: Python<'_>, prog_str: String, bindings: &PyDict) -> PyResult<PyObjec
for keyobj in bindings.keys().iter() {
let key = keyobj.downcast::<PyString>()?;

let val = bindings.get_item(keyobj).unwrap();
let val = bindings.get_item(keyobj).unwrap().unwrap();

if !val.is_callable() {
exec_ctx.bind_param(key.to_str()?, val.extract()?)
Expand Down Expand Up @@ -168,7 +168,7 @@ impl<'source> FromPyObject<'source> for CelValue {
for keyobj in mapobj.keys().iter() {
let key = keyobj.downcast::<PyString>()?.to_string();

map.insert(key, mapobj.get_item(keyobj).unwrap().extract()?);
map.insert(key, mapobj.get_item(keyobj).unwrap().unwrap().extract()?);
}

Ok(map.into())
Expand Down
56 changes: 33 additions & 23 deletions src/bindings/wasm/from_jsvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use wasm_bindgen::{JsCast, JsValue};

use crate::{CelError, CelResult, CelValue};

use super::{log, object_iter::ObjectIterator, values};
use super::{object_iter::ObjectIterator, values};

fn extract_number_value<T: num::cast::FromPrimitive + FromStr>(
obj: &js_sys::Object,
Expand Down Expand Up @@ -50,34 +50,44 @@ fn extract_number_value<T: num::cast::FromPrimitive + FromStr>(
impl TryFrom<JsValue> for CelValue {
type Error = CelError;
fn try_from(value: JsValue) -> Result<Self, Self::Error> {
if value.is_array() {
let mut list: Vec<CelValue> = Vec::new();
if value.is_object() {
if value.is_array() {
let mut list: Vec<CelValue> = Vec::new();

for list_value in values(&value).into_iter() {
list.push(list_value.try_into()?);
}

Ok(CelValue::from_list(list))
} else if value.is_object() {
let obj: js_sys::Object = value.into();
for list_value in values(&value).into_iter() {
list.push(list_value.try_into()?);
}

if obj.has_own_property(&"cel_float".into()) {
Ok(CelValue::from_float(extract_number_value(
&obj,
"cel_float",
)?))
} else if obj.has_own_property(&"cel_int".into()) {
Ok(CelValue::from_int(extract_number_value(&obj, "cel_int")?))
} else if obj.has_own_property(&"cel_uint".into()) {
Ok(CelValue::from_uint(extract_number_value(&obj, "cel_uint")?))
Ok(CelValue::from_list(list))
} else {
let mut map = HashMap::new();
let obj: js_sys::Object = value.into();

if obj.has_own_property(&"cel_float".into()) {
Ok(CelValue::from_float(extract_number_value(
&obj,
"cel_float",
)?))
} else if obj.has_own_property(&"cel_int".into()) {
Ok(CelValue::from_int(extract_number_value(&obj, "cel_int")?))
} else if obj.has_own_property(&"cel_uint".into()) {
Ok(CelValue::from_uint(extract_number_value(&obj, "cel_uint")?))
} else {
let mut map = HashMap::new();

for (key, value) in ObjectIterator::new(obj) {
map.insert(key, value.try_into()?);
for (key, value) in ObjectIterator::new(obj) {
map.insert(key, value.try_into()?);
}

Ok(CelValue::from_map(map))
}
}
} else if value.is_bigint() {
let bigint_val: js_sys::BigInt = value.into();

Ok(CelValue::from_map(map))
let str_val: String = bigint_val.to_string(10).unwrap().into();
match str_val.parse::<i64>() {
Ok(val) => Ok(val.into()),
Err(_) => Err(CelError::value(&format!("{} is invalid for int", str_val))),
}
} else if let Some(numval) = value.dyn_ref::<js_sys::Number>() {
if numval
Expand Down
5 changes: 5 additions & 0 deletions src/bindings/wasm/into_jsvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ impl Into<JsValue> for CelError {
js_sys::Reflect::set(&val, &"type".into(), &"internal".into()).unwrap();
js_sys::Reflect::set(&val, &"msg".into(), &msg.into()).unwrap();
}
CelError::Attribute { parent, field } => {
js_sys::Reflect::set(&val, &"type".into(), &"attribute".into()).unwrap();
js_sys::Reflect::set(&val, &"parent".into(), &parent.into()).unwrap();
js_sys::Reflect::set(&val, &"field".into(), &field.into()).unwrap();
}
};

val.into()
Expand Down
12 changes: 12 additions & 0 deletions src/cel_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub enum CelError {
InvalidOp(String),
Runtime(String),
Binding { symbol: String },
Attribute { parent: String, field: String },

Internal(String),
}
Expand Down Expand Up @@ -53,6 +54,13 @@ impl CelError {
}
}

pub fn attribute(parent_name: &str, field_name: &str) -> CelError {
CelError::Attribute {
parent: parent_name.to_string(),
field: field_name.to_string(),
}
}

pub fn type_string(&self) -> &'static str {
use CelError::*;

Expand All @@ -66,6 +74,7 @@ impl CelError {
Internal(..) => "INTERNAL",
Runtime(_) => "RUNTIME",
Binding { .. } => "BINDING",
Attribute { .. } => "ATTRIBUTE",
}
}
}
Expand All @@ -91,6 +100,9 @@ impl fmt::Display for CelError {
Internal(msg) => write!(f, "{}", msg),
Runtime(msg) => write!(f, "{}", msg),
Binding { symbol } => write!(f, "Symbol not bound: {}", symbol),
Attribute { parent, field } => {
write!(f, "Field {} does not exist on {}", field, parent)
}
}
}
}
2 changes: 1 addition & 1 deletion src/context/bind_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub type RsCelFunction = dyn Fn(CelValue, &[CelValue]) -> CelResult<CelValue>;
/// all arguents passed to the macro are left unresolved bytecode. An additional argument,
/// the Interpreter context, is provided to the macro for bytecode resolution.
pub type RsCelMacro =
dyn for<'a, 'b> Fn(&'a Interpreter<'a>, CelValue, &'b [&'b [ByteCode]]) -> CelResult<CelValue>;
dyn for<'a, 'b> Fn(&'a Interpreter<'a>, CelValue, &[&[ByteCode]]) -> CelResult<CelValue>;

/// Bindings context for a cel evaluation.
///
Expand Down
1 change: 1 addition & 0 deletions src/context/default_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ fn has_impl(ctx: &Interpreter, _this: CelValue, exprlist: &[&[ByteCode]]) -> Cel
Ok(_) => Ok(CelValue::from_bool(true)),
Err(err) => match err {
CelError::Binding { .. } => Ok(CelValue::from_bool(false)),
CelError::Attribute { .. } => Ok(CelValue::from_bool(false)),
_ => Err(err),
},
}
Expand Down
17 changes: 4 additions & 13 deletions src/interp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,16 +345,13 @@ impl<'a> Interpreter<'a> {
match map.get(&index) {
Some(val) => stack.push_val(val.clone()),
None => {
return Err(CelError::value(&format!(
"Object does not contain key \"{}\"",
index
)))
return Err(CelError::attribute("obj", &index));
}
}
}
} else {
return Err(CelError::value(&format!(
"Index operator invalide between {:?} and {:?}",
"Index operator invalid between {:?} and {:?}",
index.as_type(),
obj.as_type()
)));
Expand All @@ -374,10 +371,7 @@ impl<'a> Interpreter<'a> {
value: obj,
}),
Err(_) => {
return Err(CelError::value(&format!(
"field {} does not exist",
ident.as_str()
)))
return Err(CelError::attribute("obj", ident.as_str()));
}
},
}
Expand All @@ -390,10 +384,7 @@ impl<'a> Interpreter<'a> {
value: obj,
});
} else {
return Err(CelError::value(&format!(
"Field {} does not exist",
ident.as_str()
)));
return Err(CelError::attribute("obj", ident.as_str()));
}
} else {
return Err(CelError::Runtime(
Expand Down
21 changes: 21 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ mod test {
BindContext, CelContext, CelValue, Program,
};
use chrono::DateTime;
use serde_json::Value;
use std::{assert, assert_eq, collections::HashMap};
use test_case::test_case;

Expand Down Expand Up @@ -330,6 +331,26 @@ mod test {

assert_eq!(ctx.exec("entry", &exec).unwrap(), "value".into());
}

#[test]
fn test_has_in_reduce() {
let mut ctx = CelContext::new();
let mut exec = BindContext::new();

ctx.add_program_str(
"entry",
"my_list.reduce(curr, next, curr + int(has(next.foo)), 0)",
)
.unwrap();

let obj: CelValue = serde_json::from_str::<Value>("[{\"foo\": 1}, {}, {\"foo\": 1}]")
.unwrap()
.into();

exec.bind_param("my_list", obj.into());

assert_eq!(ctx.exec("entry", &exec).unwrap(), 2.into());
}
}

#[cfg(test)]
Expand Down

0 comments on commit 421d6d1

Please sign in to comment.