jsonatapy 2.2.5__tar.gz → 2.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/CHANGELOG.md +64 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/Cargo.lock +44 -33
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/Cargo.toml +1 -1
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/PKG-INFO +13 -6
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/README.md +12 -5
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/bindings/c/README.md +10 -1
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/bindings/c/examples/smoke.c +58 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/bindings/c/jsonata.h +35 -0
- jsonatapy-2.2.7/examples/host_functions.rs +113 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/pyproject.toml +1 -1
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/python/jsonatapy/__init__.py +68 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/capi.rs +352 -4
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/evaluator.rs +171 -8
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/lib.rs +153 -2
- jsonatapy-2.2.7/tests/host_functions_test.rs +188 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.gitignore +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.gitmodules +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.serena/.gitignore +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.serena/memories/architecture.md +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.serena/memories/conventions.md +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.serena/memories/core.md +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.serena/memories/memory_maintenance.md +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.serena/memories/suggested_commands.md +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.serena/memories/task_completion.md +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.serena/memories/tech_stack.md +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/.serena/project.yml +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/LICENSE +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/benches/evaluator_bench.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/examples/evaluator_demo.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/examples/parser_demo.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/examples/simd_json_bench.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/python/jsonatapy/__main__.py +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/python/jsonatapy/_cli/__init__.py +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/python/jsonatapy/_cli/bindings.py +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/python/jsonatapy/_cli/error_format.py +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/python/jsonatapy/_cli/mcp_server.py +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/python/jsonatapy/_cli/resolve.py +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/python/jsonatapy/_cli/run.py +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/python/jsonatapy/py.typed +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/ast.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/ast_transform.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/bin/jsonata/bindings.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/bin/jsonata/error_format.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/bin/jsonata/main.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/bin/jsonata/resolve.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/compiler.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/datetime.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/functions.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/lazy.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/parser/README.md +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/parser.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/signature.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/value.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/src/vm.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/study/cli_fixtures.json +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/study/cli_fixtures_testdata.json +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/study/cli_spec.md +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/tests/cli_fixtures_test.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/tests/cli_test.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/tests/datetime_picture_suite.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/tests/integration_test.rs +0 -0
- {jsonatapy-2.2.5 → jsonatapy-2.2.7}/tests/parent_and_focus_binding_suite.rs +0 -0
|
@@ -19,6 +19,70 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19
19
|
|
|
20
20
|
### Security
|
|
21
21
|
|
|
22
|
+
## [2.2.7] - 2026-07-21
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Dependabot no longer auto-bumps the `tests/jsonata-js` reference submodule (removed the
|
|
28
|
+
`gitsubmodule` ecosystem entry). Reference-suite updates are handled by the
|
|
29
|
+
`sync-jsonata.yml` workflow, which runs the conformance suite against each new jsonata-js
|
|
30
|
+
release and opens a tracking issue (or a clean PR) — avoiding context-free failing bump
|
|
31
|
+
PRs like #82.
|
|
32
|
+
|
|
33
|
+
### Deprecated
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
- Ensures compliance with the **jsonata-js 2.2.2** reference test suite (reference submodule
|
|
39
|
+
bumped to `6c7e95f`); the full reference suite — 1686 cases — passes. Three behavior
|
|
40
|
+
changes were required to match jsonata-js 2.2.2:
|
|
41
|
+
- `$contains(str, token)` now returns `undefined` when either argument is undefined,
|
|
42
|
+
instead of raising a type error (jsonata-js #809).
|
|
43
|
+
- `$each(obj, fn)` now returns `undefined` when its first argument is undefined, instead
|
|
44
|
+
of raising `each() first argument must be an object`.
|
|
45
|
+
- An object constructor (group-by) applied to an empty or undefined sequence now yields
|
|
46
|
+
an empty object `{}` instead of `undefined` (jsonata-js #817, "correctly handle empty
|
|
47
|
+
joins"); `null` input still returns `null` and non-empty grouping is unchanged.
|
|
48
|
+
|
|
49
|
+
### Security
|
|
50
|
+
|
|
51
|
+
## [2.2.6] - 2026-07-20
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
- Host-callable custom functions (Rust core): `Evaluator::register_fn` and
|
|
55
|
+
`Evaluator::register_fn_override` let a host register native functions callable
|
|
56
|
+
from an expression as `$name(...)` — the equivalent of jsonata-js's
|
|
57
|
+
`registerFunction`. Functions are plain closures
|
|
58
|
+
(`Fn(&[JValue]) -> Result<JValue, EvaluatorError>`) and resolve after the
|
|
59
|
+
expression's own bindings/lambdas and before built-ins. `register_fn` rejects
|
|
60
|
+
collisions with built-ins; `register_fn_override` allows deliberately replacing
|
|
61
|
+
the impure built-ins (`$now`, `$millis`, `$random`, `$eval`) for determinism
|
|
62
|
+
injection or sandboxing. Evaluation stays synchronous. See
|
|
63
|
+
`examples/host_functions.rs` and the Rust crate docs.
|
|
64
|
+
- Host-callable custom functions (Python binding): `JsonataExpression.register(name,
|
|
65
|
+
func)` and `.register_override(name, func)` expose the above to Python. The callable
|
|
66
|
+
receives already-evaluated positional arguments and must return a JSON-compatible
|
|
67
|
+
value synchronously; an `async def` (coroutine) is rejected at call time with
|
|
68
|
+
guidance to await I/O outside jsonata and pass results via `bindings`. Collision and
|
|
69
|
+
compilable-builtin-override rules are validated at `register()` time.
|
|
70
|
+
- Host-callable custom functions (C ABI): `jsonata_register_fn(expr, name, fn, user_data)`
|
|
71
|
+
and `jsonata_register_fn_override(...)` expose the feature to C and any language with C
|
|
72
|
+
interop. The callback receives its arguments as a JSON array string and returns a JSON
|
|
73
|
+
result string (jsonata copies it; the host retains ownership), or NULL to signal an error.
|
|
74
|
+
See `bindings/c/jsonata.h`, `bindings/c/README.md`, and `bindings/c/examples/smoke.c`.
|
|
75
|
+
|
|
76
|
+
### Changed
|
|
77
|
+
|
|
78
|
+
### Deprecated
|
|
79
|
+
|
|
80
|
+
### Removed
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
|
|
84
|
+
### Security
|
|
85
|
+
|
|
22
86
|
## [2.2.5] - 2026-07-14
|
|
23
87
|
|
|
24
88
|
### Added
|
|
@@ -240,9 +240,9 @@ dependencies = [
|
|
|
240
240
|
|
|
241
241
|
[[package]]
|
|
242
242
|
name = "clap"
|
|
243
|
-
version = "4.6.
|
|
243
|
+
version = "4.6.3"
|
|
244
244
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
245
|
-
checksum = "
|
|
245
|
+
checksum = "0fb99565819980999fb7b4a1796046a5c949e6d4ff132cf5fadf5a641e20d776"
|
|
246
246
|
dependencies = [
|
|
247
247
|
"clap_builder",
|
|
248
248
|
"clap_derive",
|
|
@@ -250,9 +250,9 @@ dependencies = [
|
|
|
250
250
|
|
|
251
251
|
[[package]]
|
|
252
252
|
name = "clap_builder"
|
|
253
|
-
version = "4.6.
|
|
253
|
+
version = "4.6.2"
|
|
254
254
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
255
|
-
checksum = "
|
|
255
|
+
checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b"
|
|
256
256
|
dependencies = [
|
|
257
257
|
"anstream",
|
|
258
258
|
"anstyle",
|
|
@@ -262,14 +262,14 @@ dependencies = [
|
|
|
262
262
|
|
|
263
263
|
[[package]]
|
|
264
264
|
name = "clap_derive"
|
|
265
|
-
version = "4.6.
|
|
265
|
+
version = "4.6.3"
|
|
266
266
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
267
|
-
checksum = "
|
|
267
|
+
checksum = "32f2392eae7f16557a3d727ef3a12e57b2b2ca6f98566a5f4fb41ffe305df077"
|
|
268
268
|
dependencies = [
|
|
269
269
|
"heck",
|
|
270
270
|
"proc-macro2",
|
|
271
271
|
"quote",
|
|
272
|
-
"syn",
|
|
272
|
+
"syn 2.0.118",
|
|
273
273
|
]
|
|
274
274
|
|
|
275
275
|
[[package]]
|
|
@@ -586,7 +586,7 @@ dependencies = [
|
|
|
586
586
|
|
|
587
587
|
[[package]]
|
|
588
588
|
name = "jsonata-core"
|
|
589
|
-
version = "2.2.
|
|
589
|
+
version = "2.2.7"
|
|
590
590
|
dependencies = [
|
|
591
591
|
"assert_cmd",
|
|
592
592
|
"base64",
|
|
@@ -849,7 +849,7 @@ dependencies = [
|
|
|
849
849
|
"proc-macro2",
|
|
850
850
|
"pyo3-macros-backend",
|
|
851
851
|
"quote",
|
|
852
|
-
"syn",
|
|
852
|
+
"syn 2.0.118",
|
|
853
853
|
]
|
|
854
854
|
|
|
855
855
|
[[package]]
|
|
@@ -861,7 +861,7 @@ dependencies = [
|
|
|
861
861
|
"heck",
|
|
862
862
|
"proc-macro2",
|
|
863
863
|
"quote",
|
|
864
|
-
"syn",
|
|
864
|
+
"syn 2.0.118",
|
|
865
865
|
]
|
|
866
866
|
|
|
867
867
|
[[package]]
|
|
@@ -983,14 +983,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
|
|
|
983
983
|
dependencies = [
|
|
984
984
|
"proc-macro2",
|
|
985
985
|
"quote",
|
|
986
|
-
"syn",
|
|
986
|
+
"syn 2.0.118",
|
|
987
987
|
]
|
|
988
988
|
|
|
989
989
|
[[package]]
|
|
990
990
|
name = "regex"
|
|
991
|
-
version = "1.13.
|
|
991
|
+
version = "1.13.1"
|
|
992
992
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
993
|
-
checksum = "
|
|
993
|
+
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
|
994
994
|
dependencies = [
|
|
995
995
|
"aho-corasick",
|
|
996
996
|
"memchr",
|
|
@@ -1000,9 +1000,9 @@ dependencies = [
|
|
|
1000
1000
|
|
|
1001
1001
|
[[package]]
|
|
1002
1002
|
name = "regex-automata"
|
|
1003
|
-
version = "0.4.
|
|
1003
|
+
version = "0.4.16"
|
|
1004
1004
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1005
|
-
checksum = "
|
|
1005
|
+
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
|
|
1006
1006
|
dependencies = [
|
|
1007
1007
|
"aho-corasick",
|
|
1008
1008
|
"memchr",
|
|
@@ -1063,9 +1063,9 @@ dependencies = [
|
|
|
1063
1063
|
|
|
1064
1064
|
[[package]]
|
|
1065
1065
|
name = "serde"
|
|
1066
|
-
version = "1.0.
|
|
1066
|
+
version = "1.0.229"
|
|
1067
1067
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1068
|
-
checksum = "
|
|
1068
|
+
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
|
1069
1069
|
dependencies = [
|
|
1070
1070
|
"serde_core",
|
|
1071
1071
|
"serde_derive",
|
|
@@ -1073,29 +1073,29 @@ dependencies = [
|
|
|
1073
1073
|
|
|
1074
1074
|
[[package]]
|
|
1075
1075
|
name = "serde_core"
|
|
1076
|
-
version = "1.0.
|
|
1076
|
+
version = "1.0.229"
|
|
1077
1077
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1078
|
-
checksum = "
|
|
1078
|
+
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
|
1079
1079
|
dependencies = [
|
|
1080
1080
|
"serde_derive",
|
|
1081
1081
|
]
|
|
1082
1082
|
|
|
1083
1083
|
[[package]]
|
|
1084
1084
|
name = "serde_derive"
|
|
1085
|
-
version = "1.0.
|
|
1085
|
+
version = "1.0.229"
|
|
1086
1086
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1087
|
-
checksum = "
|
|
1087
|
+
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
1088
1088
|
dependencies = [
|
|
1089
1089
|
"proc-macro2",
|
|
1090
1090
|
"quote",
|
|
1091
|
-
"syn",
|
|
1091
|
+
"syn 3.0.2",
|
|
1092
1092
|
]
|
|
1093
1093
|
|
|
1094
1094
|
[[package]]
|
|
1095
1095
|
name = "serde_json"
|
|
1096
|
-
version = "1.0.
|
|
1096
|
+
version = "1.0.151"
|
|
1097
1097
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1098
|
-
checksum = "
|
|
1098
|
+
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
|
1099
1099
|
dependencies = [
|
|
1100
1100
|
"indexmap",
|
|
1101
1101
|
"itoa",
|
|
@@ -1167,6 +1167,17 @@ dependencies = [
|
|
|
1167
1167
|
"unicode-ident",
|
|
1168
1168
|
]
|
|
1169
1169
|
|
|
1170
|
+
[[package]]
|
|
1171
|
+
name = "syn"
|
|
1172
|
+
version = "3.0.2"
|
|
1173
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1174
|
+
checksum = "a207d6d6a2b7fc470b80443726053f18a2481b7e1eee970597051596567987a3"
|
|
1175
|
+
dependencies = [
|
|
1176
|
+
"proc-macro2",
|
|
1177
|
+
"quote",
|
|
1178
|
+
"unicode-ident",
|
|
1179
|
+
]
|
|
1180
|
+
|
|
1170
1181
|
[[package]]
|
|
1171
1182
|
name = "target-lexicon"
|
|
1172
1183
|
version = "0.13.5"
|
|
@@ -1194,22 +1205,22 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
|
|
1194
1205
|
|
|
1195
1206
|
[[package]]
|
|
1196
1207
|
name = "thiserror"
|
|
1197
|
-
version = "2.0.
|
|
1208
|
+
version = "2.0.19"
|
|
1198
1209
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1199
|
-
checksum = "
|
|
1210
|
+
checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
|
|
1200
1211
|
dependencies = [
|
|
1201
1212
|
"thiserror-impl",
|
|
1202
1213
|
]
|
|
1203
1214
|
|
|
1204
1215
|
[[package]]
|
|
1205
1216
|
name = "thiserror-impl"
|
|
1206
|
-
version = "2.0.
|
|
1217
|
+
version = "2.0.19"
|
|
1207
1218
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1208
|
-
checksum = "
|
|
1219
|
+
checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
|
|
1209
1220
|
dependencies = [
|
|
1210
1221
|
"proc-macro2",
|
|
1211
1222
|
"quote",
|
|
1212
|
-
"syn",
|
|
1223
|
+
"syn 3.0.2",
|
|
1213
1224
|
]
|
|
1214
1225
|
|
|
1215
1226
|
[[package]]
|
|
@@ -1312,7 +1323,7 @@ dependencies = [
|
|
|
1312
1323
|
"bumpalo",
|
|
1313
1324
|
"proc-macro2",
|
|
1314
1325
|
"quote",
|
|
1315
|
-
"syn",
|
|
1326
|
+
"syn 2.0.118",
|
|
1316
1327
|
"wasm-bindgen-shared",
|
|
1317
1328
|
]
|
|
1318
1329
|
|
|
@@ -1387,7 +1398,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
|
|
1387
1398
|
dependencies = [
|
|
1388
1399
|
"proc-macro2",
|
|
1389
1400
|
"quote",
|
|
1390
|
-
"syn",
|
|
1401
|
+
"syn 2.0.118",
|
|
1391
1402
|
]
|
|
1392
1403
|
|
|
1393
1404
|
[[package]]
|
|
@@ -1398,7 +1409,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
|
|
1398
1409
|
dependencies = [
|
|
1399
1410
|
"proc-macro2",
|
|
1400
1411
|
"quote",
|
|
1401
|
-
"syn",
|
|
1412
|
+
"syn 2.0.118",
|
|
1402
1413
|
]
|
|
1403
1414
|
|
|
1404
1415
|
[[package]]
|
|
@@ -1457,7 +1468,7 @@ checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
|
|
|
1457
1468
|
dependencies = [
|
|
1458
1469
|
"proc-macro2",
|
|
1459
1470
|
"quote",
|
|
1460
|
-
"syn",
|
|
1471
|
+
"syn 2.0.118",
|
|
1461
1472
|
]
|
|
1462
1473
|
|
|
1463
1474
|
[[package]]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonatapy
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.7
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -47,19 +47,26 @@ Project-URL: Documentation, https://github.com/txjmb/jsonata-core
|
|
|
47
47
|
Project-URL: Homepage, https://github.com/txjmb/jsonata-core
|
|
48
48
|
Project-URL: Repository, https://github.com/txjmb/jsonata-core
|
|
49
49
|
|
|
50
|
-
# jsonata-core + jsonatapy
|
|
50
|
+
# jsonata-core (rust) + jsonatapy + jsonata C-ABI library + jsonata cli
|
|
51
|
+
#### Pypi stats
|
|
52
|
+
[](https://piptrends.com/package/jsonatapy) [](https://piptrends.com/package/jsonatapy) [](https://piptrends.com/package/jsonatapy)
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
#### Crates.io stats
|
|
55
|
+

|
|
56
|
+
|
|
57
|
+
High-performance [JSONata](https://jsonata.org/) implementation in Rust, with Python binding and C ABI/library. If you use this library, please add a github star!
|
|
53
58
|
|
|
54
59
|
Much of this project was built using Claude Code with significant human oversight. There was no performant
|
|
55
60
|
JSONata implementation in Python, so the goal was to port JSONata to Rust (with a PyO3 wrapper
|
|
56
61
|
for Python) and see how fast it could go. The answer: faster than V8 for most expression
|
|
57
|
-
workloads, and faster than the next pure-Rust implementation. The rust versions are published on crates.io, and the python wheels on pypi.
|
|
62
|
+
workloads, and faster than the next pure-Rust implementation. The rust versions are published on crates.io, and the python wheels on pypi. There is also a command-line binary and Python command-line available (works great with uvx) for use in scripting. The Python library is also usable in a command-line fashion, and a C-compatible library is available for those who want to easily use jsonata in C/C++.
|
|
58
63
|
|
|
59
|
-
Many, many thanks to the incredible work of all the maintainers of the [JSONata](https://github.com/jsonata-js/jsonata) reference library. JSONata is a very powerful, well-designed, and useful language that has made an impact on many projects. This project leverages their outstanding work to extend that capability to Python and Rust and would not be possible without that project. The implementation in Rust was strongly influenced by their implementation. The 1600+ tests they created provided the scaffolding and validation for all of this project. This project will continue to follow and be a derivative of the reference project as the JSONata reference library evolves.
|
|
64
|
+
Many, many thanks to the incredible work of all the maintainers of the [JSONata](https://github.com/jsonata-js/jsonata) reference library. JSONata is a very powerful, well-designed, and useful language that has made an impact on many projects. This project leverages their outstanding work to extend that capability to Python and Rust and would not be possible without that project. The implementation in Rust was strongly influenced by their implementation. The 1600+ (1682/1682 passing for last build of this project) tests they created provided the scaffolding and validation for all of this project. This project will continue to follow and be a derivative of the reference project as the JSONata reference library evolves.
|
|
60
65
|
|
|
61
66
|
Release versions will follow the reference jsonata-js project major and minor release numbers, but not necessarily patches. This will make it easier for adopters of this library to understand each release's JSONata API compatibility. As an example, 2.1.7 should be compliant with 2.1.x jsonata-js tests, but may have fixes specific to this library. If a patch release for jsonata-js is relevant for this project, it will be included in a patch release that may or may not follow the patch numbers of the upstream project.
|
|
62
67
|
|
|
68
|
+
This project currently chooses not to implement async, because it has limited value for most of the most common use-cases. We
|
|
69
|
+
|
|
63
70
|
[](https://crates.io/crates/jsonata-core)
|
|
64
71
|
[](https://pypi.org/project/jsonatapy/)
|
|
65
72
|
[](https://pypi.org/project/jsonatapy/)
|
|
@@ -141,7 +148,7 @@ Supports Python 3.10, 3.11, 3.12, 3.13, 3.14 on Linux, macOS (Intel & ARM), and
|
|
|
141
148
|
|
|
142
149
|
## Command-line quick start
|
|
143
150
|
|
|
144
|
-
Both packages also ship a CLI, `jq`-shaped, with an identical contract:
|
|
151
|
+
Both packages also ship a binary CLI and a Python-based CLI, `jq`-shaped, with an identical contract:
|
|
145
152
|
|
|
146
153
|
```bash
|
|
147
154
|
pip install jsonatapy
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
# jsonata-core + jsonatapy
|
|
1
|
+
# jsonata-core (rust) + jsonatapy + jsonata C-ABI library + jsonata cli
|
|
2
|
+
#### Pypi stats
|
|
3
|
+
[](https://piptrends.com/package/jsonatapy) [](https://piptrends.com/package/jsonatapy) [](https://piptrends.com/package/jsonatapy)
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
#### Crates.io stats
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
High-performance [JSONata](https://jsonata.org/) implementation in Rust, with Python binding and C ABI/library. If you use this library, please add a github star!
|
|
4
9
|
|
|
5
10
|
Much of this project was built using Claude Code with significant human oversight. There was no performant
|
|
6
11
|
JSONata implementation in Python, so the goal was to port JSONata to Rust (with a PyO3 wrapper
|
|
7
12
|
for Python) and see how fast it could go. The answer: faster than V8 for most expression
|
|
8
|
-
workloads, and faster than the next pure-Rust implementation. The rust versions are published on crates.io, and the python wheels on pypi.
|
|
13
|
+
workloads, and faster than the next pure-Rust implementation. The rust versions are published on crates.io, and the python wheels on pypi. There is also a command-line binary and Python command-line available (works great with uvx) for use in scripting. The Python library is also usable in a command-line fashion, and a C-compatible library is available for those who want to easily use jsonata in C/C++.
|
|
9
14
|
|
|
10
|
-
Many, many thanks to the incredible work of all the maintainers of the [JSONata](https://github.com/jsonata-js/jsonata) reference library. JSONata is a very powerful, well-designed, and useful language that has made an impact on many projects. This project leverages their outstanding work to extend that capability to Python and Rust and would not be possible without that project. The implementation in Rust was strongly influenced by their implementation. The 1600+ tests they created provided the scaffolding and validation for all of this project. This project will continue to follow and be a derivative of the reference project as the JSONata reference library evolves.
|
|
15
|
+
Many, many thanks to the incredible work of all the maintainers of the [JSONata](https://github.com/jsonata-js/jsonata) reference library. JSONata is a very powerful, well-designed, and useful language that has made an impact on many projects. This project leverages their outstanding work to extend that capability to Python and Rust and would not be possible without that project. The implementation in Rust was strongly influenced by their implementation. The 1600+ (1682/1682 passing for last build of this project) tests they created provided the scaffolding and validation for all of this project. This project will continue to follow and be a derivative of the reference project as the JSONata reference library evolves.
|
|
11
16
|
|
|
12
17
|
Release versions will follow the reference jsonata-js project major and minor release numbers, but not necessarily patches. This will make it easier for adopters of this library to understand each release's JSONata API compatibility. As an example, 2.1.7 should be compliant with 2.1.x jsonata-js tests, but may have fixes specific to this library. If a patch release for jsonata-js is relevant for this project, it will be included in a patch release that may or may not follow the patch numbers of the upstream project.
|
|
13
18
|
|
|
19
|
+
This project currently chooses not to implement async, because it has limited value for most of the most common use-cases. We
|
|
20
|
+
|
|
14
21
|
[](https://crates.io/crates/jsonata-core)
|
|
15
22
|
[](https://pypi.org/project/jsonatapy/)
|
|
16
23
|
[](https://pypi.org/project/jsonatapy/)
|
|
@@ -92,7 +99,7 @@ Supports Python 3.10, 3.11, 3.12, 3.13, 3.14 on Linux, macOS (Intel & ARM), and
|
|
|
92
99
|
|
|
93
100
|
## Command-line quick start
|
|
94
101
|
|
|
95
|
-
Both packages also ship a CLI, `jq`-shaped, with an identical contract:
|
|
102
|
+
Both packages also ship a binary CLI and a Python-based CLI, `jq`-shaped, with an identical contract:
|
|
96
103
|
|
|
97
104
|
```bash
|
|
98
105
|
pip install jsonatapy
|
|
@@ -132,7 +132,16 @@ Rust library rebuilds with your project.)
|
|
|
132
132
|
6. **Variables:** `jsonata_bind_var(expr, "x", "[1,2,3]")` binds `$x` for
|
|
133
133
|
all subsequent evaluations on that handle (values are JSON text;
|
|
134
134
|
re-binding replaces).
|
|
135
|
-
7. **
|
|
135
|
+
7. **Host functions:** `jsonata_register_fn(expr, "name", fn, user_data)`
|
|
136
|
+
exposes a C callback to the expression as `$name(...)`. The callback
|
|
137
|
+
receives its arguments as a JSON array string and returns a JSON result
|
|
138
|
+
string that jsonata copies (you keep ownership — do not expect jsonata to
|
|
139
|
+
free it); returning NULL signals an error. A name colliding with a
|
|
140
|
+
built-in is rejected; `jsonata_register_fn_override` replaces a built-in
|
|
141
|
+
deliberately (e.g. a frozen `$now`, or a disabled `$eval`). `user_data`
|
|
142
|
+
must outlive the handle. Registering a host function routes evaluation
|
|
143
|
+
through the tree-walker, like variable bindings.
|
|
144
|
+
8. **Panics:** internal engine panics are caught at the boundary and
|
|
136
145
|
surface as errors prefixed `internal error:` — they will not abort
|
|
137
146
|
your process.
|
|
138
147
|
|
|
@@ -27,6 +27,31 @@ static int failures = 0;
|
|
|
27
27
|
|
|
28
28
|
static char *take_error(void) { return jsonata_last_error_message(); }
|
|
29
29
|
|
|
30
|
+
/*
|
|
31
|
+
* Host function: given the JSON array ["<name>"], returns "hello <name>".
|
|
32
|
+
* Uses a static buffer — valid until the next call, which is all jsonata needs
|
|
33
|
+
* (it copies the result before returning to the expression).
|
|
34
|
+
*/
|
|
35
|
+
static char host_buf[256];
|
|
36
|
+
static const char *greet_cb(void *user_data, const char *args_json) {
|
|
37
|
+
(void)user_data;
|
|
38
|
+
const char *open = strchr(args_json, '"');
|
|
39
|
+
if (!open) return NULL;
|
|
40
|
+
const char *start = open + 1;
|
|
41
|
+
const char *end = strchr(start, '"');
|
|
42
|
+
if (!end) return NULL;
|
|
43
|
+
int len = (int)(end - start);
|
|
44
|
+
snprintf(host_buf, sizeof(host_buf), "\"hello %.*s\"", len, start);
|
|
45
|
+
return host_buf;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Host override for $now(): a frozen timestamp (static string, always valid). */
|
|
49
|
+
static const char *frozen_now_cb(void *user_data, const char *args_json) {
|
|
50
|
+
(void)user_data;
|
|
51
|
+
(void)args_json;
|
|
52
|
+
return "\"2020-01-01T00:00:00.000Z\"";
|
|
53
|
+
}
|
|
54
|
+
|
|
30
55
|
int main(void) {
|
|
31
56
|
/* version */
|
|
32
57
|
const char *v = jsonata_version();
|
|
@@ -119,6 +144,39 @@ int main(void) {
|
|
|
119
144
|
jsonata_free_expr(e);
|
|
120
145
|
}
|
|
121
146
|
|
|
147
|
+
/* host function registration + call */
|
|
148
|
+
{
|
|
149
|
+
JsonataExpr *e = jsonata_compile("$greet(name)");
|
|
150
|
+
int rc = jsonata_register_fn(e, "greet", greet_cb, NULL);
|
|
151
|
+
CHECK(rc == 0, "register_fn succeeds");
|
|
152
|
+
char *r = jsonata_evaluate(e, "{\"name\":\"Ada\"}");
|
|
153
|
+
CHECK(r != NULL && strcmp(r, "\"hello Ada\"") == 0, "host function call");
|
|
154
|
+
jsonata_free_string(r);
|
|
155
|
+
jsonata_free_expr(e);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* host function override of an impure built-in ($now) */
|
|
159
|
+
{
|
|
160
|
+
JsonataExpr *e = jsonata_compile("$now()");
|
|
161
|
+
int rc = jsonata_register_fn_override(e, "now", frozen_now_cb, NULL);
|
|
162
|
+
CHECK(rc == 0, "register_fn_override succeeds");
|
|
163
|
+
char *r = jsonata_evaluate(e, "null");
|
|
164
|
+
CHECK(r != NULL && strcmp(r, "\"2020-01-01T00:00:00.000Z\"") == 0,
|
|
165
|
+
"override $now");
|
|
166
|
+
jsonata_free_string(r);
|
|
167
|
+
jsonata_free_expr(e);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* collision with a built-in is rejected */
|
|
171
|
+
{
|
|
172
|
+
JsonataExpr *e = jsonata_compile("$sum(x)");
|
|
173
|
+
int rc = jsonata_register_fn(e, "sum", greet_cb, NULL);
|
|
174
|
+
char *err = take_error();
|
|
175
|
+
CHECK(rc == -1 && err != NULL, "register_fn collision rejected");
|
|
176
|
+
jsonata_free_string(err);
|
|
177
|
+
jsonata_free_expr(e);
|
|
178
|
+
}
|
|
179
|
+
|
|
122
180
|
/* NULL tolerance of free functions */
|
|
123
181
|
jsonata_free_expr(NULL);
|
|
124
182
|
jsonata_free_string(NULL);
|
|
@@ -75,6 +75,41 @@ char *jsonata_evaluate(JsonataExpr *expr, const char *json_utf8);
|
|
|
75
75
|
int jsonata_bind_var(JsonataExpr *expr, const char *name,
|
|
76
76
|
const char *json_value_utf8);
|
|
77
77
|
|
|
78
|
+
/*
|
|
79
|
+
* A host function callback, callable from the expression as $name(...).
|
|
80
|
+
* user_data: the pointer supplied at registration (opaque to jsonata).
|
|
81
|
+
* args_json: a NUL-terminated UTF-8 JSON ARRAY of the (already evaluated)
|
|
82
|
+
* arguments.
|
|
83
|
+
* Returns a NUL-terminated UTF-8 JSON string with the result, which must stay
|
|
84
|
+
* valid until the call returns — jsonata COPIES it and does NOT free it — or
|
|
85
|
+
* NULL to signal an error.
|
|
86
|
+
*/
|
|
87
|
+
typedef const char *(*jsonata_host_fn)(void *user_data, const char *args_json);
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* Register a host function callable from the expression as $name(...) — the
|
|
91
|
+
* C equivalent of jsonata-js's registerFunction. Applies to every subsequent
|
|
92
|
+
* jsonata_evaluate() on this handle. A leading '$' in name is accepted and
|
|
93
|
+
* stripped; re-registering a name replaces it. Host functions resolve after
|
|
94
|
+
* the expression's own bindings/lambdas and before built-ins.
|
|
95
|
+
*
|
|
96
|
+
* user_data is passed back to the callback unchanged and must remain valid for
|
|
97
|
+
* the lifetime of the handle. Returns 0 on success, -1 on error (slot set) —
|
|
98
|
+
* including when name collides with a built-in (use
|
|
99
|
+
* jsonata_register_fn_override to replace a built-in deliberately).
|
|
100
|
+
*/
|
|
101
|
+
int jsonata_register_fn(JsonataExpr *expr, const char *name,
|
|
102
|
+
jsonata_host_fn fn, void *user_data);
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
* Like jsonata_register_fn, but deliberately replaces a built-in of the same
|
|
106
|
+
* name — for determinism injection (a frozen $now, seeded $random) or
|
|
107
|
+
* sandboxing (disabling $eval). Overriding a built-in that participates in the
|
|
108
|
+
* compiled fast path returns -1 with an error.
|
|
109
|
+
*/
|
|
110
|
+
int jsonata_register_fn_override(JsonataExpr *expr, const char *name,
|
|
111
|
+
jsonata_host_fn fn, void *user_data);
|
|
112
|
+
|
|
78
113
|
/* Free a handle returned by jsonata_compile(). NULL is a no-op. */
|
|
79
114
|
void jsonata_free_expr(JsonataExpr *expr);
|
|
80
115
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Host-callable custom functions
|
|
2
|
+
//
|
|
3
|
+
// Shows how a host application registers native Rust functions that a JSONata
|
|
4
|
+
// expression can call as `$name(...)`:
|
|
5
|
+
// - enrichment/lookup functions (the canonical use case)
|
|
6
|
+
// - determinism injection: overriding an impure built-in ($now) with a frozen
|
|
7
|
+
// implementation for reproducible output
|
|
8
|
+
// - sandboxing: overriding a powerful built-in ($eval) to disable it
|
|
9
|
+
//
|
|
10
|
+
// Run with: cargo run --example host_functions
|
|
11
|
+
|
|
12
|
+
use jsonata_core::value::JValue;
|
|
13
|
+
use jsonata_core::{evaluator::Evaluator, parser::parse};
|
|
14
|
+
use serde_json::json;
|
|
15
|
+
|
|
16
|
+
fn main() {
|
|
17
|
+
demo_enrichment_lookup();
|
|
18
|
+
demo_multi_arg();
|
|
19
|
+
demo_override_now();
|
|
20
|
+
demo_sandbox_eval();
|
|
21
|
+
demo_collision_is_rejected();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fn eval(ev: &mut Evaluator, expr: &str, data: serde_json::Value) -> JValue {
|
|
25
|
+
let ast = parse(expr).expect("expression parses");
|
|
26
|
+
ev.evaluate(&ast, &JValue::from(data))
|
|
27
|
+
.expect("evaluation succeeds")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// The canonical use case: a lookup backed by host-owned data. The expression
|
|
31
|
+
/// stays a clean artifact; the host owns the (here trivial) data source.
|
|
32
|
+
fn demo_enrichment_lookup() {
|
|
33
|
+
let mut ev = Evaluator::new();
|
|
34
|
+
ev.register_fn("productName", |args: &[JValue]| {
|
|
35
|
+
let sku = args.first().and_then(|v| v.as_str()).unwrap_or("");
|
|
36
|
+
let name = match sku {
|
|
37
|
+
"A-1" => "Widget",
|
|
38
|
+
"B-2" => "Gadget",
|
|
39
|
+
_ => "Unknown",
|
|
40
|
+
};
|
|
41
|
+
Ok(JValue::from(name))
|
|
42
|
+
})
|
|
43
|
+
.unwrap();
|
|
44
|
+
|
|
45
|
+
let out = eval(
|
|
46
|
+
&mut ev,
|
|
47
|
+
"items.{ 'sku': sku, 'name': $productName(sku) }",
|
|
48
|
+
json!({ "items": [ { "sku": "A-1" }, { "sku": "B-2" } ] }),
|
|
49
|
+
);
|
|
50
|
+
println!("enrichment lookup: {}", out.to_json_string().unwrap());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Host functions receive all arguments already evaluated.
|
|
54
|
+
fn demo_multi_arg() {
|
|
55
|
+
let mut ev = Evaluator::new();
|
|
56
|
+
ev.register_fn("convert", |args: &[JValue]| {
|
|
57
|
+
let amount = args.first().and_then(|v| v.as_f64()).unwrap_or(0.0);
|
|
58
|
+
let rate = match args.get(1).and_then(|v| v.as_str()) {
|
|
59
|
+
Some("EUR") => 1.1,
|
|
60
|
+
_ => 1.0,
|
|
61
|
+
};
|
|
62
|
+
Ok(JValue::from_f64(amount * rate))
|
|
63
|
+
})
|
|
64
|
+
.unwrap();
|
|
65
|
+
|
|
66
|
+
let out = eval(
|
|
67
|
+
&mut ev,
|
|
68
|
+
"$convert(amount, currency)",
|
|
69
|
+
json!({ "amount": 10, "currency": "EUR" }),
|
|
70
|
+
);
|
|
71
|
+
println!("multi-arg convert: {}", out.to_json_string().unwrap());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Determinism injection: freeze `$now()` so output is reproducible in tests.
|
|
75
|
+
/// `$now` is a non-compilable (impure) built-in, so overriding it is allowed.
|
|
76
|
+
fn demo_override_now() {
|
|
77
|
+
let mut ev = Evaluator::new();
|
|
78
|
+
ev.register_fn_override("now", |_args: &[JValue]| {
|
|
79
|
+
Ok(JValue::from("2020-01-01T00:00:00.000Z"))
|
|
80
|
+
})
|
|
81
|
+
.unwrap();
|
|
82
|
+
|
|
83
|
+
let out = eval(&mut ev, "{ 'generatedAt': $now() }", json!(null));
|
|
84
|
+
println!("frozen $now: {}", out.to_json_string().unwrap());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Sandboxing: disable the dynamic-evaluation built-in `$eval` when running
|
|
88
|
+
/// semi-trusted expressions.
|
|
89
|
+
fn demo_sandbox_eval() {
|
|
90
|
+
let mut ev = Evaluator::new();
|
|
91
|
+
ev.register_fn_override("eval", |_args: &[JValue]| {
|
|
92
|
+
Err(jsonata_core::evaluator::EvaluatorError::EvaluationError(
|
|
93
|
+
"$eval is disabled in this environment".to_string(),
|
|
94
|
+
))
|
|
95
|
+
})
|
|
96
|
+
.unwrap();
|
|
97
|
+
|
|
98
|
+
let ast = parse("$eval('1 + 1')").unwrap();
|
|
99
|
+
match ev.evaluate(&ast, &JValue::Null) {
|
|
100
|
+
Ok(v) => println!("sandbox $eval: unexpectedly returned {v:?}"),
|
|
101
|
+
Err(e) => println!("sandbox $eval: blocked as expected -> {e}"),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Registering a name that collides with a built-in is refused; use
|
|
106
|
+
/// `register_fn_override` to replace a built-in deliberately.
|
|
107
|
+
fn demo_collision_is_rejected() {
|
|
108
|
+
let mut ev = Evaluator::new();
|
|
109
|
+
match ev.register_fn("sum", |_args: &[JValue]| Ok(JValue::Null)) {
|
|
110
|
+
Ok(()) => println!("collision: unexpectedly accepted"),
|
|
111
|
+
Err(e) => println!("collision: rejected as expected -> {e}"),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jsonatapy"
|
|
7
|
-
version = "2.2.
|
|
7
|
+
version = "2.2.7"
|
|
8
8
|
description = "High-performance Python/Rust implementation of JSONata query and transformation language"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "txjmb", email = "txjmb@users.noreply.github.com"}
|