csonpath 0.16.0__tar.gz → 0.18.0__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.
Files changed (44) hide show
  1. {csonpath-0.16.0 → csonpath-0.18.0}/Makefile +54 -6
  2. {csonpath-0.16.0/csonpath.egg-info → csonpath-0.18.0}/PKG-INFO +160 -12
  3. {csonpath-0.16.0 → csonpath-0.18.0}/README.md +159 -11
  4. {csonpath-0.16.0 → csonpath-0.18.0/csonpath.egg-info}/PKG-INFO +160 -12
  5. {csonpath-0.16.0 → csonpath-0.18.0}/csonpath.egg-info/SOURCES.txt +4 -0
  6. {csonpath-0.16.0 → csonpath-0.18.0}/csonpath.h +335 -167
  7. {csonpath-0.16.0 → csonpath-0.18.0}/csonpath_do.h +28 -12
  8. {csonpath-0.16.0 → csonpath-0.18.0}/csonpath_json-c.h +22 -0
  9. csonpath-0.18.0/csonpath_my_fuzzing.h +440 -0
  10. {csonpath-0.16.0 → csonpath-0.18.0}/csonpath_python.c +210 -24
  11. csonpath-0.18.0/csonpath_undef.h +44 -0
  12. csonpath-0.18.0/csonpath_yyjson.h +19 -0
  13. csonpath-0.16.0/csonpath_yyjson.h → csonpath-0.18.0/csonpath_yyjson_const.h +22 -4
  14. csonpath-0.18.0/csonpath_yyjson_mut.h +223 -0
  15. {csonpath-0.16.0 → csonpath-0.18.0}/pyproject.toml +2 -2
  16. {csonpath-0.16.0 → csonpath-0.18.0}/LICENSE +0 -0
  17. {csonpath-0.16.0 → csonpath-0.18.0}/MANIFEST.in +0 -0
  18. {csonpath-0.16.0 → csonpath-0.18.0}/configure +0 -0
  19. {csonpath-0.16.0 → csonpath-0.18.0}/csonpath.egg-info/dependency_links.txt +0 -0
  20. {csonpath-0.16.0 → csonpath-0.18.0}/csonpath.egg-info/requires.txt +0 -0
  21. {csonpath-0.16.0 → csonpath-0.18.0}/csonpath.egg-info/top_level.txt +0 -0
  22. {csonpath-0.16.0 → csonpath-0.18.0}/setup.cfg +0 -0
  23. {csonpath-0.16.0 → csonpath-0.18.0}/setup.py +0 -0
  24. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/.git +0 -0
  25. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/.github/workflows/c-cpp.yml +0 -0
  26. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/.gitignore +0 -0
  27. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/LICENSE +0 -0
  28. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/Makefile +0 -0
  29. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/README.md +0 -0
  30. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/clib.json +0 -0
  31. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/formal_verification.md +0 -0
  32. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/re.c +0 -0
  33. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/re.h +0 -0
  34. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/scripts/exrex.py +0 -0
  35. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/scripts/regex_test.py +0 -0
  36. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/scripts/regex_test_neg.py +0 -0
  37. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/tests/test1.c +0 -0
  38. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/tests/test2.c +0 -0
  39. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/tests/test3.c +0 -0
  40. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/tests/test_compile.c +0 -0
  41. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/tests/test_print.c +0 -0
  42. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/tests/test_rand.c +0 -0
  43. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/tests/test_rand_neg.c +0 -0
  44. {csonpath-0.16.0 → csonpath-0.18.0}/tiny-regex-c/tests/test_thread.c +0 -0
@@ -5,7 +5,7 @@ YYJSON_LDFLAGS=$(shell pkg-config --libs yyjson)
5
5
 
6
6
  include config.mk
7
7
 
8
- all: test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index test-json-union test-json-audit-bugs test-json-crash-vectors
8
+ all: test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index test-json-union test-json-audit-bugs test-json-crash-vectors test-json-my-fuzz test-json-regex-overflow
9
9
 
10
10
  YYJSON_TESTS=test-yyjson
11
11
 
@@ -15,7 +15,7 @@ bench:
15
15
  bench-clean:
16
16
  make -C bench clean
17
17
 
18
- .PHONY: all clean tests pip-dev pip-dev bench bench-clean
18
+ .PHONY: all clean tests pip-dev pip-dev bench bench-clean tests-cli tests-rust cov
19
19
 
20
20
  CFLAGS+= -Wall -Wextra -Wno-unused-function -Wno-unused-parameter -O0 -g
21
21
 
@@ -49,10 +49,22 @@ test-json-audit-bugs: tests/json-c/audit-bugs.c csonpath_json-c.h csonpath.h cso
49
49
  test-json-crash-vectors: tests/json-c/crash-vectors.c csonpath_json-c.h csonpath.h csonpath_do.h
50
50
  $(CC) tests/json-c/crash-vectors.c $(EXTRA_FILES) $(JSON_C_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-json-crash-vectors $(JSON_C_LDFLAGS) $(LDFLAGS)
51
51
 
52
- test-yyjson: tests/yyjson/test-yyjson.c csonpath_yyjson.h csonpath.h csonpath_do.h
52
+ test-json-my-fuzz: tests/json-c/my_fuzz.c csonpath_json-c.h csonpath_my_fuzzing.h csonpath.h csonpath_do.h
53
+ $(CC) tests/json-c/my_fuzz.c $(EXTRA_FILES) $(JSON_C_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-json-my-fuzz $(JSON_C_LDFLAGS) $(LDFLAGS)
54
+
55
+ test-json-regex-overflow: tests/json-c/regex-overflow.c csonpath_json-c.h csonpath.h csonpath_do.h
56
+ $(CC) tests/json-c/regex-overflow.c $(EXTRA_FILES) $(JSON_C_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-json-regex-overflow $(JSON_C_LDFLAGS) $(LDFLAGS)
57
+
58
+ test-yyjson: tests/yyjson/test-yyjson.c csonpath_yyjson_const.h csonpath.h csonpath_do.h
53
59
  $(CC) tests/yyjson/test-yyjson.c $(EXTRA_FILES) $(YYJSON_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-yyjson $(YYJSON_LDFLAGS) $(LDFLAGS)
54
60
 
55
- tests-c: test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index test-json-union test-json-audit-bugs test-json-crash-vectors test-yyjson
61
+ test-yyjson-mixed: tests/yyjson/test-yyjson-mixed.c csonpath_yyjson.h csonpath_yyjson_const.h csonpath_yyjson_mut.h csonpath.h csonpath_do.h
62
+ $(CC) tests/yyjson/test-yyjson-mixed.c $(EXTRA_FILES) $(YYJSON_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-yyjson-mixed $(YYJSON_LDFLAGS) $(LDFLAGS)
63
+
64
+ test-multi-backend-prefix: tests/multi-backend-prefix.c csonpath_json-c.h csonpath_yyjson.h csonpath_yyjson_const.h csonpath_yyjson_mut.h csonpath.h csonpath_do.h
65
+ $(CC) tests/multi-backend-prefix.c $(EXTRA_FILES) $(JSON_C_CFLAGS) $(YYJSON_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-multi-backend-prefix $(JSON_C_LDFLAGS) $(YYJSON_LDFLAGS) $(LDFLAGS)
66
+
67
+ tests-c: test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index test-json-union test-json-audit-bugs test-json-crash-vectors test-yyjson test-yyjson-mixed test-json-my-fuzz test-multi-backend-prefix test-json-regex-overflow
56
68
  ./test-json-c-get-a
57
69
  ./test-json-update
58
70
  ./test-json-filter
@@ -64,6 +76,16 @@ test-yyjson: tests/yyjson/test-yyjson.c csonpath_yyjson.h csonpath.h csonpath_do
64
76
  ./test-json-audit-bugs
65
77
  ./test-json-crash-vectors
66
78
  ./test-yyjson
79
+ ./test-yyjson-mixed
80
+ ./test-json-my-fuzz
81
+ ./test-multi-backend-prefix
82
+ ./test-json-regex-overflow
83
+
84
+ csonpath: cli/csonpath_cli.c csonpath_json-c.h csonpath.h csonpath_do.h
85
+ $(CC) cli/csonpath_cli.c $(EXTRA_FILES) $(JSON_C_CFLAGS) $(CFLAGS) -I./ -o csonpath $(JSON_C_LDFLAGS) $(LDFLAGS)
86
+
87
+ tests-cli: csonpath
88
+ bash tests/cli/run.sh
67
89
 
68
90
  pip-dev:
69
91
  pip install -e .[dev] --force-reinstall
@@ -71,8 +93,34 @@ pip-dev:
71
93
  tests-py: pip-dev
72
94
  python -m pytest
73
95
 
74
- tests: tests-py tests-c
96
+ tests-rust:
97
+ cargo test --manifest-path rust/Cargo.toml
98
+
99
+ cov:
100
+ $(MAKE) clean
101
+ rm -rf coverage-c coverage-c.html coverage-py coverage-rust build
102
+ find . -name '*.gcov' -delete
103
+ $(MAKE) CFLAGS='--coverage -O0 -g' LDFLAGS='--coverage' tests-c
104
+ CFLAGS='--coverage -O0' LDFLAGS='--coverage' python setup.py build_ext --inplace --force
105
+ python -m coverage run -m pytest
106
+ python -m coverage html -d coverage-py
107
+ python -m gcovr -r . \
108
+ --gcov-object-directory "$$(ls -d build/temp.* 2>/dev/null | head -1)" \
109
+ --filter 'csonpath\.h' --filter 'csonpath_do\.h' --filter 'csonpath_python\.c' \
110
+ --html-details coverage-c.html
111
+ PATH="$$HOME/.cargo/bin:$$PATH" \
112
+ LLVM_COV="$$(find $$HOME/.rustup/toolchains -name llvm-cov -print -quit)" \
113
+ LLVM_PROFDATA="$$(find $$HOME/.rustup/toolchains -name llvm-profdata -print -quit)" \
114
+ cargo llvm-cov --manifest-path rust/Cargo.toml --html --output-dir coverage-rust
115
+ @echo "Coverage reports:"
116
+ @echo " coverage-c.html"
117
+ @echo " coverage-py/index.html"
118
+ @echo " coverage-rust/html/index.html"
119
+
120
+ tests: tests-py tests-c tests-cli tests-rust
75
121
 
76
122
  clean:
77
- rm -rvf test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index test-json-union test-json-audit-bugs test-json-crash-vectors test-yyjson
123
+ rm -rvf test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index test-json-union test-json-audit-bugs test-json-crash-vectors test-yyjson test-yyjson-mixed test-json-my-fuzz test-multi-backend-prefix test-json-regex-overflow csonpath
124
+ find . -name '*.gcda' -delete
125
+ find . -name '*.gcno' -delete
78
126
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csonpath
3
- Version: 0.16.0
3
+ Version: 0.18.0
4
4
  Description-Content-Type: text/markdown
5
5
  License-File: LICENSE
6
6
  Provides-Extra: dev
@@ -13,9 +13,9 @@ Dynamic: license-file
13
13
 
14
14
  [![Project Sandbox](https://docs.outscale.com/fr/userguide/_images/Project-Sandbox-yellow.svg)](https://docs.outscale.com/en/userguide/Open-Source-Projects.html)
15
15
 
16
- **csonpath** is a partial [JSONPath](https://goessner.net/articles/JsonPath/) implementation in C, with Python bindings. It allows you to query, update, and remove data from JSON objects using path expressions.
16
+ **csonpath** is a partial [JSONPath](https://goessner.net/articles/JsonPath/) implementation in C. It is **backend-agnostic**: the same core engine can query, update, and remove data from any value representation that supports array, object, and scalar semantics—not just JSON.
17
17
 
18
- Unlike many JSONPath libraries, csonpath is **backend-agnostic**: it can work with any C library or environment that manipulates array, object, and scalar types—not just JSON. Out of the box, backends for [json-c](https://github.com/json-c/json-c) and Python are provided.
18
+ Out of the box it ships with C backends for [json-c](https://github.com/json-c/json-c) and [yyjson](https://github.com/ibireme/yyjson), plus language bindings for **Python** and **Rust**.
19
19
 
20
20
  ---
21
21
 
@@ -62,6 +62,8 @@ Unlike many JSONPath libraries, csonpath is **backend-agnostic**: it can work wi
62
62
  - [Usage](#-usage)
63
63
  - [C API Reference](#-c-api-reference)
64
64
  - [Python API Reference](#-python-api-reference)
65
+ - [CLI](#%EF%B8%8F-cli)
66
+ - [Using Multiple Backends](#-using-multiple-backends)
65
67
  - [Custom Backends](#-custom-backends)
66
68
  - [Running Tests](#-running-tests)
67
69
  - [Directory Structure](#-directory-structure)
@@ -72,12 +74,12 @@ Unlike many JSONPath libraries, csonpath is **backend-agnostic**: it can work wi
72
74
 
73
75
  ## 📦 Installation
74
76
 
75
- ### Prerequisites
76
- - C compiler (`gcc` or `clang`)
77
- - [json-c](https://github.com/json-c/json-c) library (for C usage)
78
- - Python 3.x (for Python bindings)
77
+ csonpath is a C library at its core. Pick the instructions for the language you want to use it from.
79
78
 
80
79
  ### C (json-c)
80
+
81
+ **Prerequisites:** C compiler (`gcc`, `clang`, or `tcc`), [json-c](https://github.com/json-c/json-c).
82
+
81
83
  Just include the header in your project. There is no separate install step required:
82
84
  ```c
83
85
  #include "csonpath_json-c.h"
@@ -87,7 +89,23 @@ Make sure to link against `json-c` when compiling:
87
89
  gcc myapp.c -o myapp $(pkg-config --cflags --libs json-c)
88
90
  ```
89
91
 
92
+ ### C (yyjson)
93
+
94
+ **Prerequisites:** C compiler (`gcc`, `clang`, or `tcc`), [yyjson](https://github.com/ibireme/yyjson).
95
+
96
+ ```c
97
+ #include "csonpath_yyjson.h"
98
+ ```
99
+
100
+ Link against `yyjson` when compiling:
101
+ ```sh
102
+ gcc myapp.c -o myapp $(pkg-config --cflags --libs yyjson)
103
+ ```
104
+
90
105
  ### Python
106
+
107
+ **Prerequisites:** Python 3.x.
108
+
91
109
  Install from PyPI:
92
110
  ```sh
93
111
  pip install csonpath
@@ -100,6 +118,23 @@ pip install .
100
118
  make pip-dev
101
119
  ```
102
120
 
121
+ ### Rust
122
+
123
+ **Prerequisites:** Rust toolchain.
124
+
125
+ The Rust crate is located in `rust/`. Build and test it with:
126
+ ```sh
127
+ cd rust
128
+ cargo build
129
+ cargo test
130
+ ```
131
+
132
+ To depend on it from another Rust project:
133
+ ```toml
134
+ [dependencies]
135
+ csonpath = { path = "rust" }
136
+ ```
137
+
103
138
  ---
104
139
 
105
140
  ## 🛠️ Usage
@@ -180,6 +215,8 @@ p.update_or_create_callback(dst, my_sync_fn, userdata)
180
215
 
181
216
  ## 📘 C API Reference
182
217
 
218
+ The reference below uses the **json-c** backend as an example (`struct json_object *`). All backends expose the same functions with their own value type.
219
+
183
220
  ```c
184
221
  struct csonpath *csonpath_new(const char *path);
185
222
  ```
@@ -252,15 +289,117 @@ Free the csonpath object.
252
289
 
253
290
  ---
254
291
 
292
+ ## 🖥️ CLI
293
+
294
+ A standalone C CLI is available in `cli/csonpath_cli.c`. It links directly against `json-c` and the csonpath C core, so it works without Python.
295
+
296
+ ```sh
297
+ make csonpath # build ./csonpath
298
+ make tests-cli # run shell tests
299
+ ```
300
+
301
+ It reads JSON from stdin, a file (`-f`), or a string (`-s`) and exposes the library operations through action flags.
302
+
303
+ ```sh
304
+ # Get the first match (default)
305
+ echo '{"a": "value", "array": [1, 2, 3]}' | ./csonpath '$.a'
306
+ # => "value"
307
+
308
+ # Find all matches
309
+ echo '{"items": [{"price": 5}, {"price": 15}]}' | ./csonpath -a '$.items[?price > 10]'
310
+ # => [{"price":15}]
311
+
312
+ # One match per line
313
+ echo '{"array": [1, 2, 3]}' | ./csonpath -a -o lines '$.array[*]'
314
+ # => 1
315
+ # => 2
316
+ # => 3
317
+
318
+ # Set or create a value
319
+ echo '{"a": 1}' | ./csonpath --set '42' '$.x.y.z'
320
+ # => {"a":1,"x":{"y":{"z":42}}}
321
+
322
+ # Or with a positional value
323
+ echo '{"a": 1}' | ./csonpath '$.x.y.z' '42'
324
+
325
+ # Remove matches
326
+ echo '{"a": 1, "b": 2}' | ./csonpath -d '$.b'
327
+ # => {"a":1}
328
+
329
+ # Edit a file in place
330
+ ./csonpath -f data.json -p -i --set '"2.0"' '$.version'
331
+ ```
332
+
333
+ ### Options
334
+
335
+ | Option | Description |
336
+ |---|---|
337
+ | `-a`, `--all` | Return all matches instead of the first one. |
338
+ | `-d`, `--delete` | Remove matches and print the modified JSON. |
339
+ | `--set VALUE` | Set `PATH` to `VALUE` (JSON). |
340
+ | `VALUE` (positional) | Alternative to `--set`. |
341
+ | `-r`, `--raw` | Treat the value as a raw string. |
342
+ | `-i`, `--in-place` | Edit `FILE` in place (requires `--file`). |
343
+ | `--strict` | Exit with an error if `--delete` removes nothing. |
344
+ | `-f FILE`, `--file FILE` | Read JSON from `FILE` instead of stdin. |
345
+ | `-s JSON`, `--string JSON` | Read JSON from a string. |
346
+ | `-j`, `--jq-like` | Allow jq-style paths without a leading `$`. |
347
+ | `-p`, `--pretty` | Pretty-print JSON output. |
348
+ | `-o {json,pretty,raw,lines}` | Output format (default: `json`). |
349
+ | `-e`, `--empty-array` | Return `[]` instead of nothing when `-a` matches nothing. |
350
+
351
+ ### Exit codes
352
+
353
+ | Code | Meaning |
354
+ |---|---|
355
+ | `0` | Success. |
356
+ | `1` | No match found, or `--strict` delete found nothing. |
357
+ | `EINVAL` | Usage error, JSON parse error, JSONPath compilation error, or invalid JSON value. |
358
+ | `errno` | I/O error (e.g. `ENOENT`, `EACCES`). |
359
+
360
+ ---
361
+
362
+ ## 🧩 Using Multiple Backends
363
+
364
+ You can include more than one csonpath backend in the same translation unit, provided you prefix all but one of them to avoid symbol collisions. Define `CSONPATH_USE_PREFIX` before a backend header to prefix its API with the backend name.
365
+
366
+ For example, to use both json-c and the immutable yyjson backend in the same file:
367
+
368
+ ```c
369
+ #define CSONPATH_USE_PREFIX
370
+ #include "csonpath_json-c.h"
371
+
372
+ #undef CSONPATH_USE_PREFIX
373
+ #include "csonpath_yyjson_const.h"
374
+ ```
375
+
376
+ To use both yyjson backends (immutable and mutable) together, include the aggregator header:
377
+
378
+ ```c
379
+ #include "csonpath_yyjson.h"
380
+ ```
381
+
382
+ When using the mutable yyjson backend, assign the document pointer to `backend_ctx` before mutable operations:
383
+
384
+ ```c
385
+ struct csonpath *mp = yyjson_mut_csonpath_new("$.a");
386
+ mp->backend_ctx = mdoc;
387
+ yyjson_mut_csonpath_update_or_create(mp, mroot, new_val);
388
+ ```
389
+
390
+ ---
391
+
255
392
  ## 🔌 Custom Backends
256
393
 
257
- csonpath is designed to be backend-agnostic. It can work with any data structure that supports array, object, and scalar semantics.
394
+ csonpath is designed to be backend-agnostic. The C core manipulates opaque value pointers through a small set of macros, so it can work with any data structure that supports array, object, and scalar semantics.
258
395
 
259
- To create a custom backend, define the required macros and types in a header file (similar to `csonpath_json-c.h` or `csonpath_python.c`), then include your backend header before `csonpath.h`. This allows you to adapt csonpath for manipulating data in any format that supports array/object semantics, giving you full flexibility beyond just JSON.
396
+ A backend is an adapter that implements those macros for a concrete value representation. To create a custom backend, define the required macros and types in a header file (similar to `csonpath_json-c.h` or `csonpath_yyjson.h`), then include your backend header before `csonpath.h`.
260
397
 
261
- For more details, see the existing backend implementations:
262
- - `csonpath_json-c.h` — json-c backend
263
- - `csonpath_python.c` — Python backend
398
+ Existing backend adapters:
399
+ - `csonpath_json-c.h` — json-c values
400
+ - `csonpath_yyjson.h` — yyjson values
401
+ - `csonpath_python.c` — Python C API values (used by the Python bindings)
402
+ - `rust/csonpath_rust_backend.h` — Rust `serde_json::Value` (used by the Rust crate)
264
403
 
265
404
  ---
266
405
 
@@ -271,6 +410,12 @@ For more details, see the existing backend implementations:
271
410
  make tests-c
272
411
  ```
273
412
 
413
+ ### Rust Tests
414
+ ```sh
415
+ cd rust
416
+ cargo test
417
+ ```
418
+
274
419
  ### Python Tests
275
420
  ```sh
276
421
  make tests-py
@@ -289,7 +434,10 @@ make tests
289
434
  |---|---|
290
435
  | `csonpath.h`, `csonpath_do.h` | Core implementation (header-only style) |
291
436
  | `csonpath_json-c.h` | json-c backend |
437
+ | `csonpath_yyjson.h` | yyjson backend |
292
438
  | `csonpath_python.c` | Python C extension backend |
439
+ | `rust/` | Rust crate and its C backend adapter |
440
+ | `csonpath_my_fuzzing.h` | Fuzzer helpers (C) |
293
441
  | `tests/` | C and Python test suites |
294
442
  | `bench/` | Performance benchmarks |
295
443
 
@@ -4,9 +4,9 @@
4
4
 
5
5
  [![Project Sandbox](https://docs.outscale.com/fr/userguide/_images/Project-Sandbox-yellow.svg)](https://docs.outscale.com/en/userguide/Open-Source-Projects.html)
6
6
 
7
- **csonpath** is a partial [JSONPath](https://goessner.net/articles/JsonPath/) implementation in C, with Python bindings. It allows you to query, update, and remove data from JSON objects using path expressions.
7
+ **csonpath** is a partial [JSONPath](https://goessner.net/articles/JsonPath/) implementation in C. It is **backend-agnostic**: the same core engine can query, update, and remove data from any value representation that supports array, object, and scalar semantics—not just JSON.
8
8
 
9
- Unlike many JSONPath libraries, csonpath is **backend-agnostic**: it can work with any C library or environment that manipulates array, object, and scalar types—not just JSON. Out of the box, backends for [json-c](https://github.com/json-c/json-c) and Python are provided.
9
+ Out of the box it ships with C backends for [json-c](https://github.com/json-c/json-c) and [yyjson](https://github.com/ibireme/yyjson), plus language bindings for **Python** and **Rust**.
10
10
 
11
11
  ---
12
12
 
@@ -53,6 +53,8 @@ Unlike many JSONPath libraries, csonpath is **backend-agnostic**: it can work wi
53
53
  - [Usage](#-usage)
54
54
  - [C API Reference](#-c-api-reference)
55
55
  - [Python API Reference](#-python-api-reference)
56
+ - [CLI](#%EF%B8%8F-cli)
57
+ - [Using Multiple Backends](#-using-multiple-backends)
56
58
  - [Custom Backends](#-custom-backends)
57
59
  - [Running Tests](#-running-tests)
58
60
  - [Directory Structure](#-directory-structure)
@@ -63,12 +65,12 @@ Unlike many JSONPath libraries, csonpath is **backend-agnostic**: it can work wi
63
65
 
64
66
  ## 📦 Installation
65
67
 
66
- ### Prerequisites
67
- - C compiler (`gcc` or `clang`)
68
- - [json-c](https://github.com/json-c/json-c) library (for C usage)
69
- - Python 3.x (for Python bindings)
68
+ csonpath is a C library at its core. Pick the instructions for the language you want to use it from.
70
69
 
71
70
  ### C (json-c)
71
+
72
+ **Prerequisites:** C compiler (`gcc`, `clang`, or `tcc`), [json-c](https://github.com/json-c/json-c).
73
+
72
74
  Just include the header in your project. There is no separate install step required:
73
75
  ```c
74
76
  #include "csonpath_json-c.h"
@@ -78,7 +80,23 @@ Make sure to link against `json-c` when compiling:
78
80
  gcc myapp.c -o myapp $(pkg-config --cflags --libs json-c)
79
81
  ```
80
82
 
83
+ ### C (yyjson)
84
+
85
+ **Prerequisites:** C compiler (`gcc`, `clang`, or `tcc`), [yyjson](https://github.com/ibireme/yyjson).
86
+
87
+ ```c
88
+ #include "csonpath_yyjson.h"
89
+ ```
90
+
91
+ Link against `yyjson` when compiling:
92
+ ```sh
93
+ gcc myapp.c -o myapp $(pkg-config --cflags --libs yyjson)
94
+ ```
95
+
81
96
  ### Python
97
+
98
+ **Prerequisites:** Python 3.x.
99
+
82
100
  Install from PyPI:
83
101
  ```sh
84
102
  pip install csonpath
@@ -91,6 +109,23 @@ pip install .
91
109
  make pip-dev
92
110
  ```
93
111
 
112
+ ### Rust
113
+
114
+ **Prerequisites:** Rust toolchain.
115
+
116
+ The Rust crate is located in `rust/`. Build and test it with:
117
+ ```sh
118
+ cd rust
119
+ cargo build
120
+ cargo test
121
+ ```
122
+
123
+ To depend on it from another Rust project:
124
+ ```toml
125
+ [dependencies]
126
+ csonpath = { path = "rust" }
127
+ ```
128
+
94
129
  ---
95
130
 
96
131
  ## 🛠️ Usage
@@ -171,6 +206,8 @@ p.update_or_create_callback(dst, my_sync_fn, userdata)
171
206
 
172
207
  ## 📘 C API Reference
173
208
 
209
+ The reference below uses the **json-c** backend as an example (`struct json_object *`). All backends expose the same functions with their own value type.
210
+
174
211
  ```c
175
212
  struct csonpath *csonpath_new(const char *path);
176
213
  ```
@@ -243,15 +280,117 @@ Free the csonpath object.
243
280
 
244
281
  ---
245
282
 
283
+ ## 🖥️ CLI
284
+
285
+ A standalone C CLI is available in `cli/csonpath_cli.c`. It links directly against `json-c` and the csonpath C core, so it works without Python.
286
+
287
+ ```sh
288
+ make csonpath # build ./csonpath
289
+ make tests-cli # run shell tests
290
+ ```
291
+
292
+ It reads JSON from stdin, a file (`-f`), or a string (`-s`) and exposes the library operations through action flags.
293
+
294
+ ```sh
295
+ # Get the first match (default)
296
+ echo '{"a": "value", "array": [1, 2, 3]}' | ./csonpath '$.a'
297
+ # => "value"
298
+
299
+ # Find all matches
300
+ echo '{"items": [{"price": 5}, {"price": 15}]}' | ./csonpath -a '$.items[?price > 10]'
301
+ # => [{"price":15}]
302
+
303
+ # One match per line
304
+ echo '{"array": [1, 2, 3]}' | ./csonpath -a -o lines '$.array[*]'
305
+ # => 1
306
+ # => 2
307
+ # => 3
308
+
309
+ # Set or create a value
310
+ echo '{"a": 1}' | ./csonpath --set '42' '$.x.y.z'
311
+ # => {"a":1,"x":{"y":{"z":42}}}
312
+
313
+ # Or with a positional value
314
+ echo '{"a": 1}' | ./csonpath '$.x.y.z' '42'
315
+
316
+ # Remove matches
317
+ echo '{"a": 1, "b": 2}' | ./csonpath -d '$.b'
318
+ # => {"a":1}
319
+
320
+ # Edit a file in place
321
+ ./csonpath -f data.json -p -i --set '"2.0"' '$.version'
322
+ ```
323
+
324
+ ### Options
325
+
326
+ | Option | Description |
327
+ |---|---|
328
+ | `-a`, `--all` | Return all matches instead of the first one. |
329
+ | `-d`, `--delete` | Remove matches and print the modified JSON. |
330
+ | `--set VALUE` | Set `PATH` to `VALUE` (JSON). |
331
+ | `VALUE` (positional) | Alternative to `--set`. |
332
+ | `-r`, `--raw` | Treat the value as a raw string. |
333
+ | `-i`, `--in-place` | Edit `FILE` in place (requires `--file`). |
334
+ | `--strict` | Exit with an error if `--delete` removes nothing. |
335
+ | `-f FILE`, `--file FILE` | Read JSON from `FILE` instead of stdin. |
336
+ | `-s JSON`, `--string JSON` | Read JSON from a string. |
337
+ | `-j`, `--jq-like` | Allow jq-style paths without a leading `$`. |
338
+ | `-p`, `--pretty` | Pretty-print JSON output. |
339
+ | `-o {json,pretty,raw,lines}` | Output format (default: `json`). |
340
+ | `-e`, `--empty-array` | Return `[]` instead of nothing when `-a` matches nothing. |
341
+
342
+ ### Exit codes
343
+
344
+ | Code | Meaning |
345
+ |---|---|
346
+ | `0` | Success. |
347
+ | `1` | No match found, or `--strict` delete found nothing. |
348
+ | `EINVAL` | Usage error, JSON parse error, JSONPath compilation error, or invalid JSON value. |
349
+ | `errno` | I/O error (e.g. `ENOENT`, `EACCES`). |
350
+
351
+ ---
352
+
353
+ ## 🧩 Using Multiple Backends
354
+
355
+ You can include more than one csonpath backend in the same translation unit, provided you prefix all but one of them to avoid symbol collisions. Define `CSONPATH_USE_PREFIX` before a backend header to prefix its API with the backend name.
356
+
357
+ For example, to use both json-c and the immutable yyjson backend in the same file:
358
+
359
+ ```c
360
+ #define CSONPATH_USE_PREFIX
361
+ #include "csonpath_json-c.h"
362
+
363
+ #undef CSONPATH_USE_PREFIX
364
+ #include "csonpath_yyjson_const.h"
365
+ ```
366
+
367
+ To use both yyjson backends (immutable and mutable) together, include the aggregator header:
368
+
369
+ ```c
370
+ #include "csonpath_yyjson.h"
371
+ ```
372
+
373
+ When using the mutable yyjson backend, assign the document pointer to `backend_ctx` before mutable operations:
374
+
375
+ ```c
376
+ struct csonpath *mp = yyjson_mut_csonpath_new("$.a");
377
+ mp->backend_ctx = mdoc;
378
+ yyjson_mut_csonpath_update_or_create(mp, mroot, new_val);
379
+ ```
380
+
381
+ ---
382
+
246
383
  ## 🔌 Custom Backends
247
384
 
248
- csonpath is designed to be backend-agnostic. It can work with any data structure that supports array, object, and scalar semantics.
385
+ csonpath is designed to be backend-agnostic. The C core manipulates opaque value pointers through a small set of macros, so it can work with any data structure that supports array, object, and scalar semantics.
249
386
 
250
- To create a custom backend, define the required macros and types in a header file (similar to `csonpath_json-c.h` or `csonpath_python.c`), then include your backend header before `csonpath.h`. This allows you to adapt csonpath for manipulating data in any format that supports array/object semantics, giving you full flexibility beyond just JSON.
387
+ A backend is an adapter that implements those macros for a concrete value representation. To create a custom backend, define the required macros and types in a header file (similar to `csonpath_json-c.h` or `csonpath_yyjson.h`), then include your backend header before `csonpath.h`.
251
388
 
252
- For more details, see the existing backend implementations:
253
- - `csonpath_json-c.h` — json-c backend
254
- - `csonpath_python.c` — Python backend
389
+ Existing backend adapters:
390
+ - `csonpath_json-c.h` — json-c values
391
+ - `csonpath_yyjson.h` — yyjson values
392
+ - `csonpath_python.c` — Python C API values (used by the Python bindings)
393
+ - `rust/csonpath_rust_backend.h` — Rust `serde_json::Value` (used by the Rust crate)
255
394
 
256
395
  ---
257
396
 
@@ -262,6 +401,12 @@ For more details, see the existing backend implementations:
262
401
  make tests-c
263
402
  ```
264
403
 
404
+ ### Rust Tests
405
+ ```sh
406
+ cd rust
407
+ cargo test
408
+ ```
409
+
265
410
  ### Python Tests
266
411
  ```sh
267
412
  make tests-py
@@ -280,7 +425,10 @@ make tests
280
425
  |---|---|
281
426
  | `csonpath.h`, `csonpath_do.h` | Core implementation (header-only style) |
282
427
  | `csonpath_json-c.h` | json-c backend |
428
+ | `csonpath_yyjson.h` | yyjson backend |
283
429
  | `csonpath_python.c` | Python C extension backend |
430
+ | `rust/` | Rust crate and its C backend adapter |
431
+ | `csonpath_my_fuzzing.h` | Fuzzer helpers (C) |
284
432
  | `tests/` | C and Python test suites |
285
433
  | `bench/` | Performance benchmarks |
286
434