convert back to JS values
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -115,12 +115,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cel-rust-wasm"
|
name = "cel-rust-wasm"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cel",
|
"cel",
|
||||||
"chrono",
|
"chrono",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"serde",
|
||||||
"serde-wasm-bindgen",
|
"serde-wasm-bindgen",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ crate-type = ["cdylib"]
|
|||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
||||||
cel = "0.11"
|
cel = "0.11"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde-wasm-bindgen = "0.6"
|
serde-wasm-bindgen = "0.6"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
|
||||||
|
|||||||
46
index.html
46
index.html
@@ -137,8 +137,8 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="full-width">
|
<div class="full-width">
|
||||||
<button onclick="evaluate()">Evaluate CEL Expression</button>
|
<button onclick="evaluateAsJs()">Evaluate as JavaScript Value</button>
|
||||||
<button onclick="evaluateAsString()">Evaluate & Return as String</button>
|
<button onclick="evaluateAsDebug()">Evaluate & Show Debug</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="full-width">
|
<div class="full-width">
|
||||||
@@ -208,7 +208,7 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
|
|||||||
<script type="module">
|
<script type="module">
|
||||||
import init, {
|
import init, {
|
||||||
evaluate_cel,
|
evaluate_cel,
|
||||||
evaluate_cel_string,
|
evaluate_cel_debug,
|
||||||
} from "./pkg/cel_rust_wasm.js";
|
} from "./pkg/cel_rust_wasm.js";
|
||||||
|
|
||||||
let wasmModule;
|
let wasmModule;
|
||||||
@@ -219,7 +219,7 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
|
|||||||
|
|
||||||
// Make functions globally available
|
// Make functions globally available
|
||||||
window.evaluate_cel = evaluate_cel;
|
window.evaluate_cel = evaluate_cel;
|
||||||
window.evaluate_cel_string = evaluate_cel_string;
|
window.evaluate_cel_debug = evaluate_cel_debug;
|
||||||
|
|
||||||
// Enable the evaluate button
|
// Enable the evaluate button
|
||||||
document.querySelector("button").disabled = false;
|
document.querySelector("button").disabled = false;
|
||||||
@@ -229,13 +229,13 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
|
|||||||
initWasm().catch(console.error);
|
initWasm().catch(console.error);
|
||||||
|
|
||||||
// Make functions globally available for onclick handlers
|
// Make functions globally available for onclick handlers
|
||||||
window.evaluate = evaluate;
|
window.evaluateAsJs = evaluateAsJs;
|
||||||
window.evaluateAsString = evaluateAsString;
|
window.evaluateAsDebug = evaluateAsDebug;
|
||||||
window.loadExample = loadExample;
|
window.loadExample = loadExample;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function evaluate() {
|
function evaluateAsJs() {
|
||||||
if (!window.evaluate_cel) {
|
if (!window.evaluate_cel) {
|
||||||
document.getElementById("result").textContent =
|
document.getElementById("result").textContent =
|
||||||
"WASM module not loaded yet...";
|
"WASM module not loaded yet...";
|
||||||
@@ -250,24 +250,23 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
|
|||||||
// Parse the context as JavaScript object
|
// Parse the context as JavaScript object
|
||||||
const context = contextStr.trim() ? JSON.parse(contextStr) : {};
|
const context = contextStr.trim() ? JSON.parse(contextStr) : {};
|
||||||
|
|
||||||
// Evaluate using the string result version
|
// Evaluate and get the actual JavaScript value
|
||||||
const result = window.evaluate_cel(expression, context);
|
const result = window.evaluate_cel(expression, context);
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
resultDiv.className = "result success";
|
resultDiv.className = "result success";
|
||||||
resultDiv.textContent = `Success: ${result.result}`;
|
resultDiv.textContent = `Success (JS Value): ${JSON.stringify(result, null, 2)}`;
|
||||||
} else {
|
|
||||||
resultDiv.className = "result error";
|
// Log the actual JavaScript object to console for inspection
|
||||||
resultDiv.textContent = `Error: ${result.error}`;
|
console.log("CEL Result as JavaScript value:", result);
|
||||||
}
|
console.log("Type:", typeof result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
resultDiv.className = "result error";
|
resultDiv.className = "result error";
|
||||||
resultDiv.textContent = `JavaScript Error: ${e.message}`;
|
resultDiv.textContent = `Error: ${e}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function evaluateAsString() {
|
function evaluateAsDebug() {
|
||||||
if (!window.evaluate_cel_string) {
|
if (!window.evaluate_cel_debug) {
|
||||||
document.getElementById("result").textContent =
|
document.getElementById("result").textContent =
|
||||||
"WASM module not loaded yet...";
|
"WASM module not loaded yet...";
|
||||||
return;
|
return;
|
||||||
@@ -281,11 +280,11 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
|
|||||||
// Parse the context as JavaScript object
|
// Parse the context as JavaScript object
|
||||||
const context = contextStr.trim() ? JSON.parse(contextStr) : {};
|
const context = contextStr.trim() ? JSON.parse(contextStr) : {};
|
||||||
|
|
||||||
// Evaluate using the string result version
|
// Evaluate and get debug string
|
||||||
const result = window.evaluate_cel_string(expression, context);
|
const result = window.evaluate_cel_debug(expression, context);
|
||||||
|
|
||||||
resultDiv.className = "result success";
|
resultDiv.className = "result success";
|
||||||
resultDiv.textContent = `Success (String): ${result}`;
|
resultDiv.textContent = `Debug Output: ${result}`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
resultDiv.className = "result error";
|
resultDiv.className = "result error";
|
||||||
resultDiv.textContent = `Error: ${e}`;
|
resultDiv.textContent = `Error: ${e}`;
|
||||||
@@ -301,15 +300,18 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
|
|||||||
document.getElementById("context").value = context;
|
document.getElementById("context").value = context;
|
||||||
|
|
||||||
// Auto-evaluate
|
// Auto-evaluate
|
||||||
setTimeout(evaluate, 100);
|
setTimeout(evaluateAsJs, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-evaluate on input change
|
// Auto-evaluate on input change
|
||||||
document.getElementById("expression").addEventListener("input", () => {
|
document.getElementById("expression").addEventListener("input", () => {
|
||||||
setTimeout(evaluate, 300);
|
setTimeout(evaluateAsJs, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("context").addEventListener("input", () => {
|
document.getElementById("context").addEventListener("input", () => {
|
||||||
|
setTimeout(evaluateAsJs, 300);
|
||||||
|
});
|
||||||
|
addEventListener("input", () => {
|
||||||
setTimeout(evaluate, 300);
|
setTimeout(evaluate, 300);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
98
src/lib.rs
98
src/lib.rs
@@ -1,5 +1,6 @@
|
|||||||
use cel::{Context, Program, Value};
|
use cel::{Context, Program, Value};
|
||||||
use js_sys;
|
use js_sys;
|
||||||
|
use serde::Serialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
@@ -19,31 +20,6 @@ macro_rules! console_log {
|
|||||||
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub struct CelResult {
|
|
||||||
success: bool,
|
|
||||||
result: String,
|
|
||||||
error: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
impl CelResult {
|
|
||||||
#[wasm_bindgen(getter)]
|
|
||||||
pub fn success(&self) -> bool {
|
|
||||||
self.success
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(getter)]
|
|
||||||
pub fn result(&self) -> String {
|
|
||||||
self.result.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(getter)]
|
|
||||||
pub fn error(&self) -> Option<String> {
|
|
||||||
self.error.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a JavaScript value to a CEL Value recursively
|
/// Convert a JavaScript value to a CEL Value recursively
|
||||||
fn js_value_to_cel_value(js_val: &JsValue) -> Result<Value, String> {
|
fn js_value_to_cel_value(js_val: &JsValue) -> Result<Value, String> {
|
||||||
if js_val.is_null() || js_val.is_undefined() {
|
if js_val.is_null() || js_val.is_undefined() {
|
||||||
@@ -94,37 +70,63 @@ fn js_value_to_cel_value(js_val: &JsValue) -> Result<Value, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a CEL expression with the given context
|
// Simple wrapper to make Value serializable for JS conversion
|
||||||
#[wasm_bindgen]
|
#[derive(Serialize)]
|
||||||
pub fn evaluate_cel(expression: &str, context: &JsValue) -> CelResult {
|
#[serde(untagged)]
|
||||||
let result = evaluate_cel_internal(expression, context);
|
enum SerializableValue {
|
||||||
|
Null,
|
||||||
|
Bool(bool),
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
String(String),
|
||||||
|
List(Vec<SerializableValue>),
|
||||||
|
Map(HashMap<String, SerializableValue>),
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
match result {
|
fn cel_value_to_serializable(cel_val: &Value) -> SerializableValue {
|
||||||
Ok(value) => CelResult {
|
match cel_val {
|
||||||
success: true,
|
Value::Null => SerializableValue::Null,
|
||||||
result: format!("{:?}", value),
|
Value::Bool(b) => SerializableValue::Bool(*b),
|
||||||
error: None,
|
Value::Int(i) => SerializableValue::Int(*i),
|
||||||
},
|
Value::UInt(u) => SerializableValue::Int(*u as i64),
|
||||||
Err(error) => CelResult {
|
Value::Float(f) => SerializableValue::Float(*f),
|
||||||
success: false,
|
Value::String(s) => SerializableValue::String(s.to_string()),
|
||||||
result: String::new(),
|
Value::List(_list) => {
|
||||||
error: Some(error),
|
// For now, convert to string representation
|
||||||
},
|
// We can improve this later when we figure out the exact CEL API
|
||||||
|
SerializableValue::Other(format!("{:?}", cel_val))
|
||||||
|
}
|
||||||
|
Value::Map(_map) => {
|
||||||
|
// For now, convert to string representation
|
||||||
|
// We can improve this later when we figure out the exact CEL API
|
||||||
|
SerializableValue::Other(format!("{:?}", cel_val))
|
||||||
|
}
|
||||||
|
_ => SerializableValue::Other(format!("{:?}", cel_val)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a CEL expression and return the result as a string
|
/// Evaluate a CEL expression and return the actual JavaScript value
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn evaluate_cel_string(expression: &str, context: &JsValue) -> Result<String, JsValue> {
|
pub fn evaluate_cel(expression: &str, context: &JsValue) -> Result<JsValue, JsValue> {
|
||||||
match evaluate_cel_internal(expression, context) {
|
match evaluate_cel_internal(expression, context) {
|
||||||
Ok(cel_value) => {
|
Ok(cel_value) => {
|
||||||
// Convert the result to a reasonable string representation
|
// Convert to serializable format and then to JS
|
||||||
let result_string = match &cel_value {
|
let serializable = cel_value_to_serializable(&cel_value);
|
||||||
Value::String(s) => s.clone(),
|
match serde_wasm_bindgen::to_value(&serializable) {
|
||||||
_ => format!("{:?}", cel_value).into(),
|
Ok(js_val) => Ok(js_val),
|
||||||
};
|
Err(e) => Err(JsValue::from_str(&format!("Serialization error: {}", e))),
|
||||||
Ok(result_string.to_string())
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(JsValue::from_str(&e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a CEL expression and return the result as a debug string
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn evaluate_cel_debug(expression: &str, context: &JsValue) -> Result<String, JsValue> {
|
||||||
|
match evaluate_cel_internal(expression, context) {
|
||||||
|
Ok(cel_value) => Ok(format!("{:?}", cel_value)),
|
||||||
Err(e) => Err(JsValue::from_str(&e)),
|
Err(e) => Err(JsValue::from_str(&e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user