Compare commits
6 Commits
f20f1e1c43
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
43503d2f11
|
|||
|
96a4494768
|
|||
|
f73f4c8ae4
|
|||
|
537d64a1b0
|
|||
|
8453d50220
|
|||
|
943fe80e10
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
|
/pkg/
|
||||||
/target/
|
/target/
|
||||||
|
|||||||
210
Cargo.lock
generated
210
Cargo.lock
generated
@@ -2,20 +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]]
|
[[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",
|
||||||
@@ -57,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"
|
||||||
@@ -75,55 +66,57 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[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",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cel_js_example"
|
name = "cel-rust-wasm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cel",
|
"cel",
|
||||||
"serde_json",
|
"chrono",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wee_alloc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.1"
|
version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
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 = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|
||||||
|
|
||||||
[[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",
|
||||||
@@ -137,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"
|
||||||
@@ -153,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"
|
||||||
@@ -163,6 +156,12 @@ version = "2.7.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memory_units"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -219,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",
|
"cfg-if 1.0.3",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@@ -234,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",
|
||||||
]
|
]
|
||||||
@@ -259,46 +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]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
@@ -308,36 +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]]
|
[[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 = "serde_json"
|
|
||||||
version = "1.0.142"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
|
|
||||||
dependencies = [
|
|
||||||
"itoa",
|
|
||||||
"memchr",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.15.1"
|
version = "1.15.1"
|
||||||
@@ -346,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",
|
||||||
@@ -383,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",
|
||||||
@@ -399,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",
|
"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",
|
||||||
@@ -425,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",
|
||||||
@@ -435,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",
|
||||||
@@ -448,13 +410,47 @@ 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 = "wee_alloc"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"libc",
|
||||||
|
"memory_units",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|||||||
28
Cargo.toml
28
Cargo.toml
@@ -1,13 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Example of running cel-rust interpreter in browser"
|
name = "cel-rust-wasm"
|
||||||
edition = "2024"
|
|
||||||
name = "cel_js_example"
|
|
||||||
publish = false
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
[dependencies]
|
|
||||||
cel = "0.11.0"
|
|
||||||
serde_json = "1.0"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
js-sys = "0.3"
|
||||||
|
cel = { version = "0.11", default-features = false, features = ["chrono"] }
|
||||||
|
chrono = { version = "0.4", default-features = false }
|
||||||
|
|
||||||
|
# `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
|
||||||
|
# allocator, however.
|
||||||
|
wee_alloc = { version = "0.4.5", optional = true }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
# Tell `rustc` to optimize for small code size.
|
||||||
|
opt-level = "s"
|
||||||
|
lto = true
|
||||||
|
|||||||
28
README.md
Normal file
28
README.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
To build and use this:
|
||||||
|
|
||||||
|
1. Install wasm-pack (if you haven't already):
|
||||||
|
```bash
|
||||||
|
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Build the WASM module:
|
||||||
|
```bash
|
||||||
|
wasm-pack build --target web --out-dir pkg
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Serve the HTML file using a local HTTP server (required for ES modules):
|
||||||
|
```bash
|
||||||
|
python3 -m http.server 8000
|
||||||
|
# or
|
||||||
|
npx serve .
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Use the API:
|
||||||
|
```js
|
||||||
|
try {
|
||||||
|
const result = evaluate_cel("name + ' is ' + string(age)", {name: "Alice", age: 30});
|
||||||
|
console.log(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1 +0,0 @@
|
|||||||
target/wasm32-unknown-unknown/release/cel_js_example.wasm
|
|
||||||
409
index.html
409
index.html
@@ -1,160 +1,293 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en-US">
|
<html>
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>CEL evaluator</title>
|
<title>CEL-Rust WASM-bindgen Example</title>
|
||||||
<style type="text/css">
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-width {
|
.full-width {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea,
|
||||||
width: 100%;
|
input {
|
||||||
height: 15em;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 5px;
|
font-family: "Courier New", monospace;
|
||||||
padding: 10px;
|
font-size: 14px;
|
||||||
border: 2px solid lightgray;
|
padding: 10px;
|
||||||
font-family: 'Courier New', monospace;
|
border: 2px solid #ddd;
|
||||||
font-size: 14px;
|
border-radius: 4px;
|
||||||
resize: vertical;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#output {
|
textarea {
|
||||||
background-color: #f8f9fa;
|
height: 120px;
|
||||||
height: 10em;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
.result {
|
||||||
font-weight: bold;
|
background-color: #f8f9fa;
|
||||||
display: block;
|
padding: 10px;
|
||||||
margin-bottom: 5px;
|
border-radius: 4px;
|
||||||
color: #333;
|
border: 2px solid #28a745;
|
||||||
}
|
white-space: pre-wrap;
|
||||||
|
font-family: "Courier New", monospace;
|
||||||
|
min-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: #d32f2f;
|
background-color: #f8d7da;
|
||||||
background-color: #ffebee;
|
border-color: #dc3545;
|
||||||
}
|
color: #721c24;
|
||||||
|
}
|
||||||
|
|
||||||
.success {
|
.success {
|
||||||
color: #2e7d32;
|
background-color: #d4edda;
|
||||||
background-color: #e8f5e9;
|
border-color: #28a745;
|
||||||
}
|
color: #155724;
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
label {
|
||||||
color: #1976d2;
|
font-weight: bold;
|
||||||
margin-bottom: 10px;
|
display: block;
|
||||||
}
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
p {
|
button {
|
||||||
color: #666;
|
background-color: #007bff;
|
||||||
line-height: 1.4;
|
color: white;
|
||||||
}
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
button:hover {
|
||||||
color: #1976d2;
|
background-color: #0056b3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.examples {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example {
|
||||||
|
background-color: #f1f3f4;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example:hover {
|
||||||
|
background-color: #e8eaed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example h4 {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
color: #1976d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example code {
|
||||||
|
display: block;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
<body>
|
||||||
<body>
|
<h1>CEL-Rust WASM-bindgen Demo</h1>
|
||||||
<script>
|
|
||||||
function run() {
|
|
||||||
WebAssembly.instantiateStreaming(fetch("cel_js_example.wasm"), {}).then(({ instance }) => {
|
|
||||||
const readString = (offset) => {
|
|
||||||
const memory = instance.exports.memory.buffer;
|
|
||||||
const length = new Uint32Array(memory, offset, 1)[0];
|
|
||||||
const characters = new Uint8Array(memory, offset + 4, length);
|
|
||||||
return new TextDecoder().decode(characters);
|
|
||||||
};
|
|
||||||
|
|
||||||
const readU8 = (offset) => {
|
|
||||||
return new Uint8Array(instance.exports.memory.buffer, offset, 1)[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
const writeString = (s) => {
|
|
||||||
const encoded = new TextEncoder().encode(s.trim());
|
|
||||||
const offset = instance.exports.allocation(4 + encoded.byteLength);
|
|
||||||
const memory = instance.exports.memory.buffer;
|
|
||||||
const uint32s = new Uint32Array(memory, offset, 1);
|
|
||||||
uint32s[0] = encoded.byteLength;
|
|
||||||
const uint8s = new Uint8Array(memory, offset + 4, encoded.byteLength);
|
|
||||||
uint8s.set(encoded);
|
|
||||||
return offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Combine CEL expression and JSON context
|
|
||||||
const expression = document.getElementById("input").value;
|
|
||||||
const context = document.getElementById("context").value;
|
|
||||||
|
|
||||||
// Create a combined input with expression and context separated by a delimiter
|
|
||||||
const combinedInput = expression + "\n---CONTEXT---\n" + context;
|
|
||||||
|
|
||||||
const offset = instance.exports.evaluate(writeString(combinedInput));
|
|
||||||
const ok = readU8(offset) != 0;
|
|
||||||
const result = readString(offset + 4);
|
|
||||||
const output = document.getElementById("output");
|
|
||||||
|
|
||||||
// Style the output based on success/error
|
|
||||||
if (ok) {
|
|
||||||
output.className = "success";
|
|
||||||
output.value = result;
|
|
||||||
} else {
|
|
||||||
output.className = "error";
|
|
||||||
output.value = "ERROR\n" + result;
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
const output = document.getElementById("output");
|
|
||||||
output.className = "error";
|
|
||||||
output.value = "WASM Loading Error: " + error.message;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", function () {
|
|
||||||
document.getElementById("input").addEventListener("input", run, false);
|
|
||||||
document.getElementById("context").addEventListener("input", run, false);
|
|
||||||
run();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h1>CEL evaluator</h1>
|
|
||||||
<p>
|
<p>
|
||||||
Using <a href="https://github.com/cel-rust/cel-rust">cel-rust</a> compiled to WebAssembly.
|
This demo uses <code>wasm-bindgen</code> to provide a JavaScript API for
|
||||||
Change the CEL expression or JSON context to see it update in real-time.
|
CEL evaluation using <code>cel-rust</code>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div>
|
<div>
|
||||||
<label for="input">CEL Expression:</label>
|
<label for="expression">CEL Expression:</label>
|
||||||
<textarea id="input" placeholder="Enter your CEL expression here...">name + " is " + string(age) + " years old"</textarea>
|
<textarea id="expression" placeholder="Enter CEL expression...">
|
||||||
</div>
|
name + " is " + string(age) + " years old and " + (active ? "active" : "inactive")</textarea
|
||||||
|
>
|
||||||
<div>
|
</div>
|
||||||
<label for="context">JSON Context:</label>
|
|
||||||
<textarea id="context" placeholder="Enter JSON context here...">{"name": "Alice", "age": 30, "active": true}</textarea>
|
<div>
|
||||||
</div>
|
<label for="context">Context (as JavaScript object):</label>
|
||||||
|
<textarea
|
||||||
|
id="context"
|
||||||
|
placeholder="Enter context as JavaScript object..."
|
||||||
|
>
|
||||||
|
{"name": "Alice", "age": 30, "active": true, "scores": [95, 87, 92], "profile": {"country": "US", "level": "senior"}}</textarea
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="full-width">
|
<div class="full-width">
|
||||||
<label for="output">Result:</label>
|
<label>Result:</label>
|
||||||
<textarea id="output" readonly="readonly"></textarea>
|
<div id="result" class="result">WASM module not loaded yet...</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
<div class="examples">
|
||||||
|
<h2>Examples (click to try):</h2>
|
||||||
|
|
||||||
|
<div class="example" onclick="loadExample(this)">
|
||||||
|
<h4>Basic String Operations</h4>
|
||||||
|
<code
|
||||||
|
data-expression="'Hello ' + name + '!'"
|
||||||
|
data-context='{"name": "World"}'
|
||||||
|
>Expression: 'Hello ' + name + '!'</code
|
||||||
|
>
|
||||||
|
<code>Context: {"name": "World"}</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="example" onclick="loadExample(this)">
|
||||||
|
<h4>Math Operations</h4>
|
||||||
|
<code
|
||||||
|
data-expression="(price * double(quantity)) * (1.0 + tax)"
|
||||||
|
data-context='{"price": 10.50, "quantity": 3, "tax": 0.08}'
|
||||||
|
>Expression: (price * double(quantity)) * (1.0 + tax)</code
|
||||||
|
>
|
||||||
|
<code>Context: {"price": 10.50, "quantity": 3, "tax": 0.08}</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="example" onclick="loadExample(this)">
|
||||||
|
<h4>List Operations</h4>
|
||||||
|
<code
|
||||||
|
data-expression="string(scores.filter(s, s > 90).size()) + ' high scores out of ' + string(scores.size())"
|
||||||
|
data-context='{"scores": [85, 92, 78, 96, 88]}'
|
||||||
|
>Expression: string(scores.filter(s, s > 90).size()) + ' high
|
||||||
|
scores'</code
|
||||||
|
>
|
||||||
|
<code>Context: {"scores": [85, 92, 78, 96, 88]}</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="example" onclick="loadExample(this)">
|
||||||
|
<h4>Conditional Logic</h4>
|
||||||
|
<code
|
||||||
|
data-expression="age >= 18 ? (age >= 65 ? 'senior' : 'adult') : 'minor'"
|
||||||
|
data-context='{"age": 25}'
|
||||||
|
>Expression: age >= 18 ? (age >= 65 ? 'senior' : 'adult') :
|
||||||
|
'minor'</code
|
||||||
|
>
|
||||||
|
<code>Context: {"age": 25}</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="example" onclick="loadExample(this)">
|
||||||
|
<h4>Map/Object Access</h4>
|
||||||
|
<code
|
||||||
|
data-expression="user.profile.country == 'US' && user.profile.verified"
|
||||||
|
data-context='{"user": {"profile": {"country": "US", "verified": true}}}'
|
||||||
|
>Expression: user.profile.country == 'US' &&
|
||||||
|
user.profile.verified</code
|
||||||
|
>
|
||||||
|
<code
|
||||||
|
>Context: {"user": {"profile": {"country": "US", "verified":
|
||||||
|
true}}}</code
|
||||||
|
>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import init, { evaluate_cel } from "./pkg/cel_rust_wasm.js";
|
||||||
|
|
||||||
|
let wasmModule;
|
||||||
|
|
||||||
|
async function initWasm() {
|
||||||
|
wasmModule = await init();
|
||||||
|
console.log("WASM module loaded successfully");
|
||||||
|
|
||||||
|
// Make function globally available
|
||||||
|
window.evaluate_cel = evaluate_cel;
|
||||||
|
|
||||||
|
setTimeout(evaluate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize WASM when the page loads
|
||||||
|
initWasm().catch(console.error);
|
||||||
|
|
||||||
|
// Make functions globally available for onclick handlers
|
||||||
|
window.evaluate = evaluate;
|
||||||
|
window.loadExample = loadExample;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function evaluate() {
|
||||||
|
if (!window.evaluate_cel) {
|
||||||
|
document.getElementById("result").textContent =
|
||||||
|
"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 and get the actual JavaScript value
|
||||||
|
const result = window.evaluate_cel(expression, context);
|
||||||
|
|
||||||
|
resultDiv.className = "result success";
|
||||||
|
resultDiv.textContent = `Success (type '${typeof result}'): ${JSON.stringify(result, null, 2)}`;
|
||||||
|
|
||||||
|
// Log the actual JavaScript object to console for inspection
|
||||||
|
console.log("CEL Result:", result);
|
||||||
|
console.log("Type:", typeof result);
|
||||||
|
} catch (e) {
|
||||||
|
resultDiv.className = "result error";
|
||||||
|
resultDiv.textContent = `Error: ${e}`;
|
||||||
|
console.log("CEL Error:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadExample(element) {
|
||||||
|
const codeElement = element.querySelector("code[data-expression]");
|
||||||
|
const expression = codeElement.getAttribute("data-expression");
|
||||||
|
const context = codeElement.getAttribute("data-context");
|
||||||
|
|
||||||
|
document.getElementById("expression").value = expression;
|
||||||
|
document.getElementById("context").value = context;
|
||||||
|
|
||||||
|
// Auto-evaluate
|
||||||
|
setTimeout(evaluate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-evaluate on input change
|
||||||
|
document.getElementById("expression").addEventListener("input", () => {
|
||||||
|
setTimeout(evaluate, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("context").addEventListener("input", () => {
|
||||||
|
setTimeout(evaluate, 100);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
325
src/lib.rs
325
src/lib.rs
@@ -1,109 +1,258 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::mem;
|
|
||||||
use std::slice;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
use cel::{Context, Program, Value};
|
use cel::{Context, Program, Value};
|
||||||
use serde_json;
|
use chrono::Duration as ChronoDuration;
|
||||||
|
use js_sys;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
||||||
pub extern "C" fn allocation(n: usize) -> *mut u8 {
|
// allocator.
|
||||||
mem::ManuallyDrop::new(Vec::with_capacity(n)).as_mut_ptr()
|
#[cfg(feature = "wee_alloc")]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
macro_rules! console_log {
|
||||||
pub unsafe extern "C" fn evaluate(s: *const u8) -> *mut u8 {
|
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||||||
unsafe {
|
|
||||||
let length = u32::from_le_bytes(*(s as *const [u8; 4])) as usize;
|
|
||||||
let input = slice::from_raw_parts(s.offset(4), length);
|
|
||||||
let output = evaluate_buffers(input);
|
|
||||||
mem::ManuallyDrop::new(output).as_mut_ptr()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_buffers(input: &[u8]) -> Vec<u8> {
|
/// Parse an ISO 8601 timestamp string into a CEL Timestamp
|
||||||
let contents = str::from_utf8(input).unwrap();
|
fn parse_timestamp(timestamp_str: &str) -> Result<Value, String> {
|
||||||
let result = evaluate_cel_with_context(contents);
|
Ok(Value::Timestamp(
|
||||||
let success = result.is_ok();
|
timestamp_str
|
||||||
let message = result.unwrap_or_else(|e| e);
|
.parse()
|
||||||
let len = message.len();
|
.map_err(|e| format!("Invalid timestamp format: {}", e))?,
|
||||||
let mut buffer = Vec::with_capacity(len + 8);
|
))
|
||||||
buffer.push(if success { 1 } else { 0 });
|
|
||||||
buffer.extend(vec![0; 3]);
|
|
||||||
buffer.extend_from_slice(&(len as u32).to_le_bytes());
|
|
||||||
buffer.extend_from_slice(message.as_bytes());
|
|
||||||
buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_to_cel_value(json_value: &serde_json::Value) -> Value {
|
/// Parse a duration string (e.g., "1h30m", "45s", "2.5h") into a CEL Duration
|
||||||
match json_value {
|
fn parse_duration(duration_str: &str) -> Result<Value, String> {
|
||||||
serde_json::Value::Null => Value::Null,
|
// Parse duration strings like "1h30m45s", "2.5h", "300s", etc.
|
||||||
serde_json::Value::Bool(b) => Value::Bool(*b),
|
let duration_str = duration_str.trim();
|
||||||
serde_json::Value::Number(n) => {
|
|
||||||
if let Some(i) = n.as_i64() {
|
// Simple parser for common duration formats
|
||||||
Value::Int(i)
|
let mut total_seconds = 0.0;
|
||||||
} else if let Some(f) = n.as_f64() {
|
let mut current_number = String::new();
|
||||||
Value::Float(f)
|
|
||||||
} else {
|
for ch in duration_str.chars() {
|
||||||
Value::Null
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serde_json::Value::String(s) => Value::String(s.clone().into()),
|
|
||||||
serde_json::Value::Array(arr) => {
|
|
||||||
let cel_vec: Vec<Value> = arr.iter().map(json_to_cel_value).collect();
|
|
||||||
Value::List(cel_vec.into())
|
|
||||||
}
|
|
||||||
serde_json::Value::Object(obj) => {
|
|
||||||
let mut cel_map = HashMap::new();
|
|
||||||
for (key, value) in obj.iter() {
|
|
||||||
cel_map.insert(key.clone(), json_to_cel_value(value));
|
|
||||||
}
|
|
||||||
Value::Map(cel_map.into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle case where string ends with a number (assume seconds)
|
||||||
|
if !current_number.is_empty() {
|
||||||
|
let value: f64 = current_number
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| format!("Invalid number in duration: {}", current_number))?;
|
||||||
|
total_seconds += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
let duration = ChronoDuration::seconds(total_seconds as i64);
|
||||||
|
Ok(Value::Duration(duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_cel_with_context(content: &str) -> Result<String, String> {
|
/// Convert a JavaScript value to a CEL Value recursively
|
||||||
// Split the input to get expression and context
|
fn js_value_to_cel_value(js_val: &JsValue) -> Result<Value, String> {
|
||||||
let parts: Vec<&str> = content.split("\n---CONTEXT---\n").collect();
|
if js_val.is_null() || js_val.is_undefined() {
|
||||||
let expression = parts[0].trim();
|
Ok(Value::Null)
|
||||||
let context_json = if parts.len() > 1 {
|
} else if let Some(b) = js_val.as_bool() {
|
||||||
parts[1].trim()
|
Ok(Value::Bool(b))
|
||||||
|
} else if let Some(n) = js_val.as_f64() {
|
||||||
|
// Check if it's an integer
|
||||||
|
if n.fract() == 0.0 && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
|
||||||
|
Ok(Value::Int(n as i64))
|
||||||
|
} else {
|
||||||
|
Ok(Value::Float(n))
|
||||||
|
}
|
||||||
|
} 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()))
|
||||||
|
}
|
||||||
|
} else if js_val.is_array() {
|
||||||
|
// Handle arrays
|
||||||
|
let array = js_sys::Array::from(js_val);
|
||||||
|
let mut cel_vec = Vec::new();
|
||||||
|
|
||||||
|
for i in 0..array.length() {
|
||||||
|
let item = array.get(i);
|
||||||
|
let cel_item = js_value_to_cel_value(&item)?;
|
||||||
|
cel_vec.push(cel_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::List(cel_vec.into()))
|
||||||
|
} else if js_val.is_object() {
|
||||||
|
// Handle objects
|
||||||
|
let obj = js_sys::Object::from(js_val.clone());
|
||||||
|
let entries = js_sys::Object::entries(&obj);
|
||||||
|
let mut cel_map = HashMap::new();
|
||||||
|
|
||||||
|
for i in 0..entries.length() {
|
||||||
|
let entry = js_sys::Array::from(&entries.get(i));
|
||||||
|
let key = entry
|
||||||
|
.get(0)
|
||||||
|
.as_string()
|
||||||
|
.ok_or_else(|| "Object key must be a string".to_string())?;
|
||||||
|
let value = entry.get(1);
|
||||||
|
let cel_value = js_value_to_cel_value(&value)?;
|
||||||
|
cel_map.insert(key, cel_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Map(cel_map.into()))
|
||||||
} else {
|
} else {
|
||||||
"{}"
|
Err(format!("Unsupported JavaScript type"))
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the CEL expression
|
/// Convert a CEL Value to a JavaScript value directly
|
||||||
let program = match Program::compile(expression) {
|
fn cel_value_to_js_value(cel_val: &Value) -> Result<JsValue, String> {
|
||||||
Ok(prog) => prog,
|
match cel_val {
|
||||||
Err(e) => return Err(format!("Compilation error: {}", e)),
|
Value::Null => Ok(JsValue::NULL),
|
||||||
};
|
Value::Bool(b) => Ok(JsValue::from_bool(*b)),
|
||||||
|
Value::Int(i) => Ok(JsValue::from_f64(*i as f64)),
|
||||||
// Create context and add JSON values
|
Value::UInt(u) => Ok(JsValue::from_f64(*u as f64)),
|
||||||
let mut context = Context::default();
|
Value::Float(f) => Ok(JsValue::from_f64(*f)),
|
||||||
|
Value::String(s) => Ok(JsValue::from_str(&s.to_string())),
|
||||||
// Parse JSON context if provided and not empty
|
Value::List(list) => {
|
||||||
if !context_json.is_empty() && context_json != "{}" {
|
let js_array = js_sys::Array::new();
|
||||||
match serde_json::from_str::<serde_json::Value>(context_json) {
|
for item in list.iter() {
|
||||||
Ok(json_value) => {
|
let js_item = cel_value_to_js_value(item)?;
|
||||||
if let serde_json::Value::Object(obj) = json_value {
|
js_array.push(&js_item);
|
||||||
for (key, value) in obj.iter() {
|
|
||||||
let cel_value = json_to_cel_value(value);
|
|
||||||
context
|
|
||||||
.add_variable(key, cel_value)
|
|
||||||
.map_err(|e| format!("Context error: {}", e))?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err("JSON context must be an object".to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => return Err(format!("JSON parsing error: {}", e)),
|
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;
|
||||||
|
|
||||||
|
let duration_str = if hours > 0 {
|
||||||
|
if seconds.fract() == 0.0 {
|
||||||
|
format!("{}h{}m{}s", hours, minutes, seconds as u64)
|
||||||
|
} else {
|
||||||
|
format!("{}h{}m{:.3}s", hours, minutes, seconds)
|
||||||
|
}
|
||||||
|
} else if minutes > 0 {
|
||||||
|
if seconds.fract() == 0.0 {
|
||||||
|
format!("{}m{}s", minutes, seconds as u64)
|
||||||
|
} 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 actual JavaScript value
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn evaluate_cel(expression: &str, context: &JsValue) -> Result<JsValue, JsValue> {
|
||||||
|
match evaluate_cel_internal(expression, context) {
|
||||||
|
Ok(cel_value) => cel_value_to_js_value(&cel_value)
|
||||||
|
.map_err(|e| JsValue::from_str(&format!("Conversion error: {}", e))),
|
||||||
|
Err(e) => Err(JsValue::from_str(&e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate_cel_internal(expression: &str, context: &JsValue) -> Result<Value, String> {
|
||||||
|
// Compile the CEL expression
|
||||||
|
let program = Program::compile(expression).map_err(|e| format!("Compilation error: {}", e))?;
|
||||||
|
|
||||||
|
// Create context
|
||||||
|
let mut cel_context = Context::default();
|
||||||
|
|
||||||
|
// Add context variables if provided
|
||||||
|
if !context.is_undefined() && !context.is_null() {
|
||||||
|
if context.is_object() {
|
||||||
|
let obj = js_sys::Object::from(context.clone());
|
||||||
|
let entries = js_sys::Object::entries(&obj);
|
||||||
|
|
||||||
|
for i in 0..entries.length() {
|
||||||
|
let entry = js_sys::Array::from(&entries.get(i));
|
||||||
|
let key = entry
|
||||||
|
.get(0)
|
||||||
|
.as_string()
|
||||||
|
.ok_or_else(|| "Context key must be a string".to_string())?;
|
||||||
|
let value = entry.get(1);
|
||||||
|
|
||||||
|
let cel_value = js_value_to_cel_value(&value)
|
||||||
|
.map_err(|e| format!("Failed to convert context value '{}': {}", key, e))?;
|
||||||
|
|
||||||
|
cel_context
|
||||||
|
.add_variable(&key, cel_value)
|
||||||
|
.map_err(|e| format!("Failed to add context variable '{}': {}", key, e))?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err("Context must be an object".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the program
|
// Execute the program
|
||||||
match program.execute(&context) {
|
program
|
||||||
Ok(value) => Ok(format!("{:?}", value)),
|
.execute(&cel_context)
|
||||||
Err(e) => Err(format!("Execution error: {}", e)),
|
.map_err(|e| format!("Execution error: {}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize the WASM module (optional, for debugging)
|
||||||
|
#[wasm_bindgen(start)]
|
||||||
|
pub fn main() {
|
||||||
|
console_log!("CEL-Rust WASM module initialized!");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user