Compare commits

...

4 Commits

5 changed files with 215 additions and 367 deletions

291
Cargo.lock generated
View File

@@ -2,35 +2,11 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "antlr4rust" name = "antlr4rust"
version = "0.3.0-beta3" version = "0.3.0-rc1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15e8f291e498f0e86cde1686f8881dab6c2fda0b115214b6a48dd7ec6aa17ac5" checksum = "de666f4d7e892973b2925a32d6a791afd8fc2c712cf697897c0ae044756e88cb"
dependencies = [ dependencies = [
"better_any", "better_any",
"bit-set", "bit-set",
@@ -72,9 +48,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.1" version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
@@ -88,27 +64,17 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
dependencies = [
"shlex",
]
[[package]] [[package]]
name = "cel" name = "cel"
version = "0.11.0" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb40e65631e7ca197aab1540bf50f9484289f2a7e3d93c1db4319766157a8f2" checksum = "ecf9b7d760f0ca3d8fc8e488226230085463d47398235033f81325aa5d9a229d"
dependencies = [ dependencies = [
"antlr4rust", "antlr4rust",
"chrono", "chrono",
"lazy_static", "lazy_static",
"nom", "nom",
"paste", "paste",
"regex",
"serde", "serde",
"thiserror", "thiserror",
] ]
@@ -119,11 +85,8 @@ version = "0.1.0"
dependencies = [ dependencies = [
"cel", "cel",
"chrono", "chrono",
"console_error_panic_hook",
"js-sys", "js-sys",
"serde-wasm-bindgen",
"wasm-bindgen", "wasm-bindgen",
"web-sys",
"wee_alloc", "wee_alloc",
] ]
@@ -135,70 +98,25 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.1" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.41" version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [ dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits", "num-traits",
"serde", "serde",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if 1.0.1",
"wasm-bindgen",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
] ]
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.77" version = "0.3.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
@@ -212,9 +130,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.174" version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@@ -228,9 +146,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.27" version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]] [[package]]
name = "memchr" name = "memchr"
@@ -300,7 +218,7 @@ version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [ dependencies = [
"cfg-if 1.0.1", "cfg-if 1.0.3",
"libc", "libc",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
@@ -315,9 +233,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.95" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -340,40 +258,11 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.21" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
@@ -383,41 +272,33 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.219" version = "1.0.226"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.226"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.219" version = "1.0.226"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.1" version = "1.15.1"
@@ -426,9 +307,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.104" version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -463,15 +344,15 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.17.0" version = "1.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@@ -479,21 +360,22 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819"
dependencies = [ dependencies = [
"cfg-if 1.0.1", "cfg-if 1.0.3",
"once_cell", "once_cell",
"rustversion", "rustversion",
"wasm-bindgen-macro", "wasm-bindgen-macro",
"wasm-bindgen-shared",
] ]
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
@@ -505,9 +387,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -515,9 +397,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -528,23 +410,13 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "web-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "wee_alloc" name = "wee_alloc"
version = "0.4.5" version = "0.4.5"
@@ -579,65 +451,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.6" version = "0.52.6"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cel-rust-wasm" name = "cel-rust-wasm"
version = "0.2.0" version = "0.1.0"
edition = "2024" edition = "2024"
[lib] [lib]
@@ -9,30 +9,14 @@ crate-type = ["cdylib"]
[dependencies] [dependencies]
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
js-sys = "0.3" js-sys = "0.3"
cel = "0.11" cel = { version = "0.11", default-features = false, features = ["chrono"] }
serde-wasm-bindgen = "0.6" chrono = { version = "0.4", default-features = false }
chrono = { version = "0.4", features = ["serde"] }
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default # compared to the default allocator's ~10K. It is slower than the default
# allocator, however. # allocator, however.
wee_alloc = { version = "0.4.5", optional = true } wee_alloc = { version = "0.4.5", optional = true }
[dependencies.web-sys]
version = "0.3"
features = [
"console",
]
[features]
default = ["console_error_panic_hook"]
[profile.release] [profile.release]
# Tell `rustc` to optimize for small code size. # Tell `rustc` to optimize for small code size.
opt-level = "s" opt-level = "s"

View File

@@ -19,13 +19,8 @@ To build and use this:
4. Use the API: 4. Use the API:
```js ```js
// Method 1: Get structured result
const result = evaluate_cel("name + ' is ' + string(age)", {name: "Alice", age: 30});
console.log(result.success, result.result, result.error);
// Method 2: Get string result or error
try { try {
const result = evaluate_cel_string("name + ' is ' + string(age)", {name: "Alice", age: 30}); const result = evaluate_cel("name + ' is ' + string(age)", {name: "Alice", age: 30});
console.log(result); console.log(result);
} catch (error) { } catch (error) {
console.error(error); console.error(error);

View File

@@ -113,8 +113,8 @@
<body> <body>
<h1>CEL-Rust WASM-bindgen Demo</h1> <h1>CEL-Rust WASM-bindgen Demo</h1>
<p> <p>
This demo uses <code>wasm-bindgen</code> to provide a clean JavaScript API This demo uses <code>wasm-bindgen</code> to provide a JavaScript API for
for CEL evaluation. CEL evaluation using <code>cel-rust</code>.
</p> </p>
<div class="container"> <div class="container">
@@ -136,14 +136,9 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
</div> </div>
</div> </div>
<div class="full-width">
<button onclick="evaluate()">Evaluate CEL Expression</button>
<button onclick="evaluateAsString()">Evaluate & Return as String</button>
</div>
<div class="full-width"> <div class="full-width">
<label>Result:</label> <label>Result:</label>
<div id="result" class="result">Click "Evaluate" to see results...</div> <div id="result" class="result">WASM module not loaded yet...</div>
</div> </div>
<div class="examples"> <div class="examples">
@@ -162,9 +157,9 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
<div class="example" onclick="loadExample(this)"> <div class="example" onclick="loadExample(this)">
<h4>Math Operations</h4> <h4>Math Operations</h4>
<code <code
data-expression="(price * quantity) * (1 + tax)" data-expression="(price * double(quantity)) * (1.0 + tax)"
data-context='{"price": 10.50, "quantity": 3, "tax": 0.08}' data-context='{"price": 10.50, "quantity": 3, "tax": 0.08}'
>Expression: (price * quantity) * (1 + tax)</code >Expression: (price * double(quantity)) * (1.0 + tax)</code
> >
<code>Context: {"price": 10.50, "quantity": 3, "tax": 0.08}</code> <code>Context: {"price": 10.50, "quantity": 3, "tax": 0.08}</code>
</div> </div>
@@ -172,9 +167,10 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
<div class="example" onclick="loadExample(this)"> <div class="example" onclick="loadExample(this)">
<h4>List Operations</h4> <h4>List Operations</h4>
<code <code
data-expression="scores.filter(s, s > 90).size() + ' high scores out of ' + string(scores.size())" data-expression="string(scores.filter(s, s > 90).size()) + ' high scores out of ' + string(scores.size())"
data-context='{"scores": [85, 92, 78, 96, 88]}' data-context='{"scores": [85, 92, 78, 96, 88]}'
>Expression: scores.filter(s, s > 90).size() + ' high scores'</code >Expression: string(scores.filter(s, s > 90).size()) + ' high
scores'</code
> >
<code>Context: {"scores": [85, 92, 78, 96, 88]}</code> <code>Context: {"scores": [85, 92, 78, 96, 88]}</code>
</div> </div>
@@ -203,13 +199,22 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
true}}}</code true}}}</code
> >
</div> </div>
<div class="example" onclick="loadExample(this)">
<h4>Time and Date Operations</h4>
<code
data-expression="some_date < now - old ? 'old' : 'recent'"
data-context='{"some_date": "2023-10-01T12:00:00Z", "now": "2023-10-03T12:00:00Z", "old": "24h"}'
>Expression: some_date < now - old ? 'old' : 'recent'</code
>
<code
>Context: {"some_date": "2023-10-01T12:00:00Z", "now":
"2023-10-03T12:00:00Z", "old": "24h"}</code
>
</div> </div>
<script type="module"> <script type="module">
import init, { import init, { evaluate_cel } from "./pkg/cel_rust_wasm.js";
evaluate_cel,
evaluate_cel_string,
} from "./pkg/cel_rust_wasm.js";
let wasmModule; let wasmModule;
@@ -217,12 +222,10 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
wasmModule = await init(); wasmModule = await init();
console.log("WASM module loaded successfully"); console.log("WASM module loaded successfully");
// Make functions globally available // Make function globally available
window.evaluate_cel = evaluate_cel; window.evaluate_cel = evaluate_cel;
window.evaluate_cel_string = evaluate_cel_string;
// Enable the evaluate button setTimeout(evaluate, 0);
document.querySelector("button").disabled = false;
} }
// Initialize WASM when the page loads // Initialize WASM when the page loads
@@ -230,7 +233,6 @@ name + " is " + string(age) + " years old and " + (active ? "active" : "inactive
// Make functions globally available for onclick handlers // Make functions globally available for onclick handlers
window.evaluate = evaluate; window.evaluate = evaluate;
window.evaluateAsString = evaluateAsString;
window.loadExample = loadExample; window.loadExample = loadExample;
</script> </script>
@@ -250,45 +252,19 @@ 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 (type '${typeof result}'): ${JSON.stringify(result, null, 2)}`;
} else {
resultDiv.className = "result error";
resultDiv.textContent = `Error: ${result.error}`;
}
} catch (e) {
resultDiv.className = "result error";
resultDiv.textContent = `JavaScript Error: ${e.message}`;
}
}
function evaluateAsString() { // Log the actual JavaScript object to console for inspection
if (!window.evaluate_cel_string) { console.log("CEL Result:", result);
document.getElementById("result").textContent = console.log("Type:", typeof result);
"WASM module not loaded yet...";
return;
}
const expression = document.getElementById("expression").value;
const contextStr = document.getElementById("context").value;
const resultDiv = document.getElementById("result");
try {
// Parse the context as JavaScript object
const context = contextStr.trim() ? JSON.parse(contextStr) : {};
// Evaluate using the string result version
const result = window.evaluate_cel_string(expression, context);
resultDiv.className = "result success";
resultDiv.textContent = `Success (String): ${result}`;
} catch (e) { } catch (e) {
resultDiv.className = "result error"; resultDiv.className = "result error";
resultDiv.textContent = `Error: ${e}`; resultDiv.textContent = `Error: ${e}`;
console.log("CEL Error:", e);
} }
} }
@@ -301,16 +277,16 @@ 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(evaluate, 0);
} }
// Auto-evaluate on input change // Auto-evaluate on input change
document.getElementById("expression").addEventListener("input", () => { document.getElementById("expression").addEventListener("input", () => {
setTimeout(evaluate, 300); setTimeout(evaluate, 100);
}); });
document.getElementById("context").addEventListener("input", () => { document.getElementById("context").addEventListener("input", () => {
setTimeout(evaluate, 300); setTimeout(evaluate, 100);
}); });
</script> </script>
</body> </body>

View File

@@ -1,4 +1,5 @@
use cel::{Context, Program, Value}; use cel::{Context, Program, Value};
use chrono::Duration as ChronoDuration;
use js_sys; use js_sys;
use std::collections::HashMap; use std::collections::HashMap;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@@ -19,29 +20,57 @@ macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string())) ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
} }
#[wasm_bindgen] /// Parse an ISO 8601 timestamp string into a CEL Timestamp
pub struct CelResult { fn parse_timestamp(timestamp_str: &str) -> Result<Value, String> {
success: bool, Ok(Value::Timestamp(
result: String, timestamp_str
error: Option<String>, .parse()
.map_err(|e| format!("Invalid timestamp format: {}", e))?,
))
} }
#[wasm_bindgen] /// Parse a duration string (e.g., "1h30m", "45s", "2.5h") into a CEL Duration
impl CelResult { fn parse_duration(duration_str: &str) -> Result<Value, String> {
#[wasm_bindgen(getter)] // Parse duration strings like "1h30m45s", "2.5h", "300s", etc.
pub fn success(&self) -> bool { let duration_str = duration_str.trim();
self.success
// Simple parser for common duration formats
let mut total_seconds = 0.0;
let mut current_number = String::new();
for ch in duration_str.chars() {
if ch.is_numeric() || ch == '.' {
current_number.push(ch);
} else {
if !current_number.is_empty() {
let value: f64 = current_number
.parse()
.map_err(|_| format!("Invalid number in duration: {}", current_number))?;
let multiplier = match ch {
's' => 1.0,
'm' => 60.0,
'h' => 3600.0,
'd' => 86400.0,
_ => return Err(format!("Unknown duration unit: {}", ch)),
};
total_seconds += value * multiplier;
current_number.clear();
}
}
} }
#[wasm_bindgen(getter)] // Handle case where string ends with a number (assume seconds)
pub fn result(&self) -> String { if !current_number.is_empty() {
self.result.clone() let value: f64 = current_number
.parse()
.map_err(|_| format!("Invalid number in duration: {}", current_number))?;
total_seconds += value;
} }
#[wasm_bindgen(getter)] let duration = ChronoDuration::seconds(total_seconds as i64);
pub fn error(&self) -> Option<String> { Ok(Value::Duration(duration))
self.error.clone()
}
} }
/// Convert a JavaScript value to a CEL Value recursively /// Convert a JavaScript value to a CEL Value recursively
@@ -58,7 +87,26 @@ fn js_value_to_cel_value(js_val: &JsValue) -> Result<Value, String> {
Ok(Value::Float(n)) Ok(Value::Float(n))
} }
} else if let Some(s) = js_val.as_string() { } else if let Some(s) = js_val.as_string() {
// Check if this string represents a timestamp or duration
if s.contains('T') && (s.contains('Z') || s.contains('+') || s.contains('-')) {
// Looks like an ISO 8601 timestamp
match parse_timestamp(&s) {
Ok(ts) => Ok(ts),
Err(_) => Ok(Value::String(s.into())), // Fall back to string if parsing fails
}
} else if s
.chars()
.any(|c| c == 'h' || c == 'm' || c == 's' || c == 'd')
&& s.chars().any(|c| c.is_numeric())
{
// Looks like a duration string
match parse_duration(&s) {
Ok(dur) => Ok(dur),
Err(_) => Ok(Value::String(s.into())), // Fall back to string if parsing fails
}
} else {
Ok(Value::String(s.into())) Ok(Value::String(s.into()))
}
} else if js_val.is_array() { } else if js_val.is_array() {
// Handle arrays // Handle arrays
let array = js_sys::Array::from(js_val); let array = js_sys::Array::from(js_val);
@@ -94,37 +142,72 @@ fn js_value_to_cel_value(js_val: &JsValue) -> Result<Value, String> {
} }
} }
/// Evaluate a CEL expression with the given context /// Convert a CEL Value to a JavaScript value directly
#[wasm_bindgen] fn cel_value_to_js_value(cel_val: &Value) -> Result<JsValue, String> {
pub fn evaluate_cel(expression: &str, context: &JsValue) -> CelResult { match cel_val {
let result = evaluate_cel_internal(expression, context); Value::Null => Ok(JsValue::NULL),
Value::Bool(b) => Ok(JsValue::from_bool(*b)),
Value::Int(i) => Ok(JsValue::from_f64(*i as f64)),
Value::UInt(u) => Ok(JsValue::from_f64(*u as f64)),
Value::Float(f) => Ok(JsValue::from_f64(*f)),
Value::String(s) => Ok(JsValue::from_str(&s.to_string())),
Value::List(list) => {
let js_array = js_sys::Array::new();
for item in list.iter() {
let js_item = cel_value_to_js_value(item)?;
js_array.push(&js_item);
}
Ok(js_array.into())
}
Value::Map(map) => {
let js_obj = js_sys::Object::new();
for (key, value) in map.map.iter() {
let js_value = cel_value_to_js_value(value)?;
let key_str = key.to_string();
js_sys::Reflect::set(&js_obj, &JsValue::from_str(&key_str), &js_value)
.map_err(|_| "Failed to set object property".to_string())?;
}
Ok(js_obj.into())
}
Value::Timestamp(ts) => Ok(JsValue::from_str(&ts.to_string())),
Value::Duration(dur) => {
// Convert duration back to a readable string format
let total_seconds = dur.as_seconds_f64();
let hours = (total_seconds / 3600.0).floor() as u64;
let minutes = ((total_seconds % 3600.0) / 60.0).floor() as u64;
let seconds = total_seconds % 60.0;
match result { let duration_str = if hours > 0 {
Ok(value) => CelResult { if seconds.fract() == 0.0 {
success: true, format!("{}h{}m{}s", hours, minutes, seconds as u64)
result: format!("{:?}", value), } else {
error: None, format!("{}h{}m{:.3}s", hours, minutes, seconds)
}, }
Err(error) => CelResult { } else if minutes > 0 {
success: false, if seconds.fract() == 0.0 {
result: String::new(), format!("{}m{}s", minutes, seconds as u64)
error: Some(error), } else {
}, format!("{}m{:.3}s", minutes, seconds)
}
} else {
if seconds.fract() == 0.0 {
format!("{}s", seconds as u64)
} else {
format!("{:.3}s", seconds)
}
};
Ok(JsValue::from_str(&duration_str))
}
_ => Ok(JsValue::from_str(&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) => cel_value_to_js_value(&cel_value)
// Convert the result to a reasonable string representation .map_err(|e| JsValue::from_str(&format!("Conversion error: {}", e))),
let result_string = match &cel_value {
Value::String(s) => s.clone(),
_ => format!("{:?}", cel_value).into(),
};
Ok(result_string.to_string())
}
Err(e) => Err(JsValue::from_str(&e)), Err(e) => Err(JsValue::from_str(&e)),
} }
} }
@@ -171,8 +254,5 @@ fn evaluate_cel_internal(expression: &str, context: &JsValue) -> Result<Value, S
/// Initialize the WASM module (optional, for debugging) /// Initialize the WASM module (optional, for debugging)
#[wasm_bindgen(start)] #[wasm_bindgen(start)]
pub fn main() { pub fn main() {
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
console_log!("CEL-Rust WASM module initialized!"); console_log!("CEL-Rust WASM module initialized!");
} }