navrules 0.1.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.
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.4
2
+ Name: navrules
3
+ Version: 0.1.0
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: Operating System :: POSIX :: Linux
7
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Typing :: Typed
14
+ Requires-Dist: cel-python>=0.1.5 ; extra == 'cel'
15
+ Requires-Dist: pandas>=2.0 ; extra == 'pandas'
16
+ Provides-Extra: cel
17
+ Provides-Extra: pandas
18
+ Summary: Generic, low-latency rule evaluation engine (priority, first-match, all-match policies) with optional Rust backend
19
+ Keywords: rules,rule-engine,policy,first-match,evaluation,asyncio
20
+ Author-email: Jesus Lara <jesuslara@phenobarbital.info>
21
+ License-Expression: MIT
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
24
+ Project-URL: Homepage, https://github.com/phenobarbital/ai-parrot
25
+ Project-URL: Repository, https://github.com/phenobarbital/ai-parrot
26
+
27
+ # navrules
28
+
29
+ Generic, low-latency **rule evaluation engine** with priority-ordered
30
+ first-match, declarative conditions, and an optional **Rust backend** for the
31
+ CPU-bound hot path.
32
+
33
+ Extracted from a production rewards system; designed to be shared by any
34
+ consumer that evaluates "subject + environment vs. N rules": rewards/badges,
35
+ payrate selection at clock-out, eligibility checks, etc.
36
+
37
+ - **Zero mandatory dependencies** — stdlib only; `pandas` and `cel` are extras.
38
+ - **Two-phase rule contract** — `fits(ctx, env)` (sync, cheap pre-filter) and
39
+ `async evaluate(ctx, env)` (full evaluation, may do I/O).
40
+ - **Policies** — `ALL_MATCH`, `ANY_MATCH`, `FIRST_MATCH` (priority-ordered,
41
+ short-circuiting, typed result payload).
42
+ - **Rust hot path** — declarative rule sets compile once into a native
43
+ matcher (PyO3 + rayon, GIL released); pure-Python fallback is always
44
+ available (`navrules.HAS_RUST` tells you which one is active).
45
+
46
+ ## Quick start: payrate selection at clock-out
47
+
48
+ ```python
49
+ from navrules import ConditionRule, EvalContext, Environment, Policy, RuleSet
50
+
51
+ ruleset: RuleSet[dict] = RuleSet(
52
+ policy=Policy.FIRST_MATCH,
53
+ default={"payrate": 25.0, "code": "BASE"},
54
+ )
55
+ ruleset.add_rule({
56
+ "rule_type": "ConditionRule",
57
+ "name": "ot_weekend",
58
+ "priority": 100,
59
+ "conditions": {"env.is_weekend": True, "ctx.worked_hours": {"gt": 8}},
60
+ "result": {"payrate": 33.75, "code": "OT-WKND"},
61
+ })
62
+ ruleset.add_rule({
63
+ "rule_type": "ConditionRule",
64
+ "name": "night_diff",
65
+ "priority": 90,
66
+ "conditions": {
67
+ "env.day_period": {"in": ["evening", "night"]},
68
+ "ctx.job_code": {"in": ["TECH1", "TECH2"]},
69
+ },
70
+ "result": {"payrate": 29.50, "code": "NIGHT"},
71
+ })
72
+ ruleset.compile() # sorts by priority, compiles to Rust when available
73
+
74
+ # hot path — one call per clock-out, no async, no I/O:
75
+ env = Environment.at(clockout_ts, tz=site_tz)
76
+ ctx = EvalContext.from_user(employee, worked_hours=9.5, job_code="TECH1")
77
+ result = ruleset.evaluate_sync(ctx, env)
78
+ payrate = result.value["payrate"]
79
+
80
+ # end-of-day close, thousands of activities (rayon batch):
81
+ results = ruleset.evaluate_batch(contexts, env)
82
+ ```
83
+
84
+ ## Condition DSL
85
+
86
+ JSON-friendly dict, one entry per field (entries AND together):
87
+
88
+ ```json
89
+ {
90
+ "env.is_weekend": true,
91
+ "ctx.worked_hours": {"gte": 4, "lt": 12},
92
+ "ctx.job_code": {"in": ["TECH1", "TECH2"]},
93
+ "quarter": "Q4"
94
+ }
95
+ ```
96
+
97
+ - Scalar value ⇒ `eq` shorthand. A `{op: operand}` map applies operators;
98
+ multiple operators under one field AND together.
99
+ - `ctx.` prefix reads the evaluation context, `env.` the environment; bare
100
+ keys resolve ctx first, then env.
101
+ - Operators: `eq ne gt gte lt lte in not_in contains startswith endswith
102
+ regex between is_null`.
103
+ - Missing values match only `is_null`; `bool` never coerces to a number;
104
+ `int`/`float` coerce to each other. The Rust backend mirrors these
105
+ semantics bit-for-bit (enforced by the parity suite).
106
+ - Custom operators can be registered on an `OperatorRegistry`; rule sets
107
+ using them transparently fall back to the Python matcher.
108
+
109
+ ## Rule families
110
+
111
+ | Family | Use case | Backend |
112
+ |---|---|---|
113
+ | `ConditionRule` | Declarative conditions, CPU-bound (payrates, ambient rules) | Rust or Python |
114
+ | `AbstractRule` subclass | Code rules; `evaluate()` may hit a DB or service | Python (async) |
115
+ | `ComputedRule` | Pull pattern: the rule queries its own candidates | Python (async) |
116
+
117
+ Mixed sets work: `await ruleset.evaluate(ctx, env)` walks rules in priority
118
+ order, awaiting I/O rules in their turn.
119
+
120
+ ## Environment
121
+
122
+ `Environment` derives ~35 temporal fields from a single timestamp
123
+ (`is_weekend`, `quarter`, `day_period`, `week_position`, `is_pay_period`,
124
+ business-day counters...). `Environment.at(ts, tz=...)` evaluates at any
125
+ instant and timezone; `extra={...}` carries site-specific ambient values.
126
+
127
+ ## Loading rules from storage
128
+
129
+ ```python
130
+ from navrules.storages import FileStorage
131
+
132
+ async with FileStorage("payrate_rules.json") as storage:
133
+ for spec in await storage.load():
134
+ ruleset.add_rule(spec)
135
+ ruleset.compile()
136
+ ```
137
+
138
+ `AbstractStorage` is the extension point for DB-backed definitions.
139
+
140
+ ## Development
141
+
142
+ ```bash
143
+ # Python-only (fallback backend):
144
+ uv run --no-project --with pytest --with pytest-asyncio --with-editable . pytest tests
145
+
146
+ # Native backend:
147
+ make build-rust # maturin develop --release
148
+ cargo test # crate unit tests (rust/)
149
+ RUN_BENCH=1 pytest tests/benchmarks -s
150
+ ```
151
+
152
+ The wheel is built with maturin (`abi3-py311`: one wheel covers CPython
153
+ 3.11+). If the native extension cannot be imported, navrules silently runs
154
+ on the pure-Python matcher — set `NAVRULES_DISABLE_RUST=1` to force it.
155
+
@@ -0,0 +1,128 @@
1
+ # navrules
2
+
3
+ Generic, low-latency **rule evaluation engine** with priority-ordered
4
+ first-match, declarative conditions, and an optional **Rust backend** for the
5
+ CPU-bound hot path.
6
+
7
+ Extracted from a production rewards system; designed to be shared by any
8
+ consumer that evaluates "subject + environment vs. N rules": rewards/badges,
9
+ payrate selection at clock-out, eligibility checks, etc.
10
+
11
+ - **Zero mandatory dependencies** — stdlib only; `pandas` and `cel` are extras.
12
+ - **Two-phase rule contract** — `fits(ctx, env)` (sync, cheap pre-filter) and
13
+ `async evaluate(ctx, env)` (full evaluation, may do I/O).
14
+ - **Policies** — `ALL_MATCH`, `ANY_MATCH`, `FIRST_MATCH` (priority-ordered,
15
+ short-circuiting, typed result payload).
16
+ - **Rust hot path** — declarative rule sets compile once into a native
17
+ matcher (PyO3 + rayon, GIL released); pure-Python fallback is always
18
+ available (`navrules.HAS_RUST` tells you which one is active).
19
+
20
+ ## Quick start: payrate selection at clock-out
21
+
22
+ ```python
23
+ from navrules import ConditionRule, EvalContext, Environment, Policy, RuleSet
24
+
25
+ ruleset: RuleSet[dict] = RuleSet(
26
+ policy=Policy.FIRST_MATCH,
27
+ default={"payrate": 25.0, "code": "BASE"},
28
+ )
29
+ ruleset.add_rule({
30
+ "rule_type": "ConditionRule",
31
+ "name": "ot_weekend",
32
+ "priority": 100,
33
+ "conditions": {"env.is_weekend": True, "ctx.worked_hours": {"gt": 8}},
34
+ "result": {"payrate": 33.75, "code": "OT-WKND"},
35
+ })
36
+ ruleset.add_rule({
37
+ "rule_type": "ConditionRule",
38
+ "name": "night_diff",
39
+ "priority": 90,
40
+ "conditions": {
41
+ "env.day_period": {"in": ["evening", "night"]},
42
+ "ctx.job_code": {"in": ["TECH1", "TECH2"]},
43
+ },
44
+ "result": {"payrate": 29.50, "code": "NIGHT"},
45
+ })
46
+ ruleset.compile() # sorts by priority, compiles to Rust when available
47
+
48
+ # hot path — one call per clock-out, no async, no I/O:
49
+ env = Environment.at(clockout_ts, tz=site_tz)
50
+ ctx = EvalContext.from_user(employee, worked_hours=9.5, job_code="TECH1")
51
+ result = ruleset.evaluate_sync(ctx, env)
52
+ payrate = result.value["payrate"]
53
+
54
+ # end-of-day close, thousands of activities (rayon batch):
55
+ results = ruleset.evaluate_batch(contexts, env)
56
+ ```
57
+
58
+ ## Condition DSL
59
+
60
+ JSON-friendly dict, one entry per field (entries AND together):
61
+
62
+ ```json
63
+ {
64
+ "env.is_weekend": true,
65
+ "ctx.worked_hours": {"gte": 4, "lt": 12},
66
+ "ctx.job_code": {"in": ["TECH1", "TECH2"]},
67
+ "quarter": "Q4"
68
+ }
69
+ ```
70
+
71
+ - Scalar value ⇒ `eq` shorthand. A `{op: operand}` map applies operators;
72
+ multiple operators under one field AND together.
73
+ - `ctx.` prefix reads the evaluation context, `env.` the environment; bare
74
+ keys resolve ctx first, then env.
75
+ - Operators: `eq ne gt gte lt lte in not_in contains startswith endswith
76
+ regex between is_null`.
77
+ - Missing values match only `is_null`; `bool` never coerces to a number;
78
+ `int`/`float` coerce to each other. The Rust backend mirrors these
79
+ semantics bit-for-bit (enforced by the parity suite).
80
+ - Custom operators can be registered on an `OperatorRegistry`; rule sets
81
+ using them transparently fall back to the Python matcher.
82
+
83
+ ## Rule families
84
+
85
+ | Family | Use case | Backend |
86
+ |---|---|---|
87
+ | `ConditionRule` | Declarative conditions, CPU-bound (payrates, ambient rules) | Rust or Python |
88
+ | `AbstractRule` subclass | Code rules; `evaluate()` may hit a DB or service | Python (async) |
89
+ | `ComputedRule` | Pull pattern: the rule queries its own candidates | Python (async) |
90
+
91
+ Mixed sets work: `await ruleset.evaluate(ctx, env)` walks rules in priority
92
+ order, awaiting I/O rules in their turn.
93
+
94
+ ## Environment
95
+
96
+ `Environment` derives ~35 temporal fields from a single timestamp
97
+ (`is_weekend`, `quarter`, `day_period`, `week_position`, `is_pay_period`,
98
+ business-day counters...). `Environment.at(ts, tz=...)` evaluates at any
99
+ instant and timezone; `extra={...}` carries site-specific ambient values.
100
+
101
+ ## Loading rules from storage
102
+
103
+ ```python
104
+ from navrules.storages import FileStorage
105
+
106
+ async with FileStorage("payrate_rules.json") as storage:
107
+ for spec in await storage.load():
108
+ ruleset.add_rule(spec)
109
+ ruleset.compile()
110
+ ```
111
+
112
+ `AbstractStorage` is the extension point for DB-backed definitions.
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ # Python-only (fallback backend):
118
+ uv run --no-project --with pytest --with pytest-asyncio --with-editable . pytest tests
119
+
120
+ # Native backend:
121
+ make build-rust # maturin develop --release
122
+ cargo test # crate unit tests (rust/)
123
+ RUN_BENCH=1 pytest tests/benchmarks -s
124
+ ```
125
+
126
+ The wheel is built with maturin (`abi3-py311`: one wheel covers CPython
127
+ 3.11+). If the native extension cannot be imported, navrules silently runs
128
+ on the pure-Python matcher — set `NAVRULES_DISABLE_RUST=1` to force it.
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.7,<2.0"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "navrules"
7
+ version = "0.1.0"
8
+ requires-python = ">=3.11"
9
+ description = "Generic, low-latency rule evaluation engine (priority, first-match, all-match policies) with optional Rust backend"
10
+ readme = "README.md"
11
+ authors = [
12
+ {name = "Jesus Lara", email = "jesuslara@phenobarbital.info"}
13
+ ]
14
+ license = "MIT"
15
+ keywords = ["rules", "rule-engine", "policy", "first-match", "evaluation", "asyncio"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Operating System :: POSIX :: Linux",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3 :: Only",
25
+ "Framework :: AsyncIO",
26
+ "Typing :: Typed",
27
+ ]
28
+ # The core engine is dependency-free by design: consumers (rewards, payrate
29
+ # systems) must be able to install it without dragging frameworks along.
30
+ dependencies = []
31
+
32
+ [project.optional-dependencies]
33
+ pandas = ["pandas>=2.0"]
34
+ cel = ["cel-python>=0.1.5"]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/phenobarbital/ai-parrot"
38
+ Repository = "https://github.com/phenobarbital/ai-parrot"
39
+
40
+ [tool.maturin]
41
+ python-source = "src"
42
+ manifest-path = "rust/Cargo.toml"
43
+ module-name = "navrules._native._navrules"
44
+ features = ["pyo3/extension-module"]
45
+
46
+ [dependency-groups]
47
+ dev = [
48
+ "pytest>=8.0",
49
+ "pytest-asyncio>=0.24",
50
+ "pytest-benchmark>=4.0",
51
+ ]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
55
+ asyncio_mode = "auto"
@@ -0,0 +1,345 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "aho-corasick"
7
+ version = "1.1.4"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
10
+ dependencies = [
11
+ "memchr",
12
+ ]
13
+
14
+ [[package]]
15
+ name = "autocfg"
16
+ version = "1.5.1"
17
+ source = "registry+https://github.com/rust-lang/crates.io-index"
18
+ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
19
+
20
+ [[package]]
21
+ name = "cfg-if"
22
+ version = "1.0.4"
23
+ source = "registry+https://github.com/rust-lang/crates.io-index"
24
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
25
+
26
+ [[package]]
27
+ name = "crossbeam-deque"
28
+ version = "0.8.7"
29
+ source = "registry+https://github.com/rust-lang/crates.io-index"
30
+ checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
31
+ dependencies = [
32
+ "crossbeam-epoch",
33
+ "crossbeam-utils",
34
+ ]
35
+
36
+ [[package]]
37
+ name = "crossbeam-epoch"
38
+ version = "0.9.20"
39
+ source = "registry+https://github.com/rust-lang/crates.io-index"
40
+ checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
41
+ dependencies = [
42
+ "crossbeam-utils",
43
+ ]
44
+
45
+ [[package]]
46
+ name = "crossbeam-utils"
47
+ version = "0.8.22"
48
+ source = "registry+https://github.com/rust-lang/crates.io-index"
49
+ checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
50
+
51
+ [[package]]
52
+ name = "either"
53
+ version = "1.17.0"
54
+ source = "registry+https://github.com/rust-lang/crates.io-index"
55
+ checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
56
+
57
+ [[package]]
58
+ name = "heck"
59
+ version = "0.5.0"
60
+ source = "registry+https://github.com/rust-lang/crates.io-index"
61
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
62
+
63
+ [[package]]
64
+ name = "indoc"
65
+ version = "2.0.7"
66
+ source = "registry+https://github.com/rust-lang/crates.io-index"
67
+ checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
68
+ dependencies = [
69
+ "rustversion",
70
+ ]
71
+
72
+ [[package]]
73
+ name = "itoa"
74
+ version = "1.0.18"
75
+ source = "registry+https://github.com/rust-lang/crates.io-index"
76
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
77
+
78
+ [[package]]
79
+ name = "libc"
80
+ version = "0.2.189"
81
+ source = "registry+https://github.com/rust-lang/crates.io-index"
82
+ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
83
+
84
+ [[package]]
85
+ name = "memchr"
86
+ version = "2.8.3"
87
+ source = "registry+https://github.com/rust-lang/crates.io-index"
88
+ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
89
+
90
+ [[package]]
91
+ name = "memoffset"
92
+ version = "0.9.1"
93
+ source = "registry+https://github.com/rust-lang/crates.io-index"
94
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
95
+ dependencies = [
96
+ "autocfg",
97
+ ]
98
+
99
+ [[package]]
100
+ name = "navrules_native"
101
+ version = "0.1.0"
102
+ dependencies = [
103
+ "pyo3",
104
+ "rayon",
105
+ "regex",
106
+ "serde",
107
+ "serde_json",
108
+ ]
109
+
110
+ [[package]]
111
+ name = "once_cell"
112
+ version = "1.21.4"
113
+ source = "registry+https://github.com/rust-lang/crates.io-index"
114
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
115
+
116
+ [[package]]
117
+ name = "portable-atomic"
118
+ version = "1.14.0"
119
+ source = "registry+https://github.com/rust-lang/crates.io-index"
120
+ checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
121
+
122
+ [[package]]
123
+ name = "proc-macro2"
124
+ version = "1.0.107"
125
+ source = "registry+https://github.com/rust-lang/crates.io-index"
126
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
127
+ dependencies = [
128
+ "unicode-ident",
129
+ ]
130
+
131
+ [[package]]
132
+ name = "pyo3"
133
+ version = "0.24.2"
134
+ source = "registry+https://github.com/rust-lang/crates.io-index"
135
+ checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219"
136
+ dependencies = [
137
+ "cfg-if",
138
+ "indoc",
139
+ "libc",
140
+ "memoffset",
141
+ "once_cell",
142
+ "portable-atomic",
143
+ "pyo3-build-config",
144
+ "pyo3-ffi",
145
+ "pyo3-macros",
146
+ "unindent",
147
+ ]
148
+
149
+ [[package]]
150
+ name = "pyo3-build-config"
151
+ version = "0.24.2"
152
+ source = "registry+https://github.com/rust-lang/crates.io-index"
153
+ checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999"
154
+ dependencies = [
155
+ "once_cell",
156
+ "target-lexicon",
157
+ ]
158
+
159
+ [[package]]
160
+ name = "pyo3-ffi"
161
+ version = "0.24.2"
162
+ source = "registry+https://github.com/rust-lang/crates.io-index"
163
+ checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33"
164
+ dependencies = [
165
+ "libc",
166
+ "pyo3-build-config",
167
+ ]
168
+
169
+ [[package]]
170
+ name = "pyo3-macros"
171
+ version = "0.24.2"
172
+ source = "registry+https://github.com/rust-lang/crates.io-index"
173
+ checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9"
174
+ dependencies = [
175
+ "proc-macro2",
176
+ "pyo3-macros-backend",
177
+ "quote",
178
+ "syn 2.0.119",
179
+ ]
180
+
181
+ [[package]]
182
+ name = "pyo3-macros-backend"
183
+ version = "0.24.2"
184
+ source = "registry+https://github.com/rust-lang/crates.io-index"
185
+ checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a"
186
+ dependencies = [
187
+ "heck",
188
+ "proc-macro2",
189
+ "pyo3-build-config",
190
+ "quote",
191
+ "syn 2.0.119",
192
+ ]
193
+
194
+ [[package]]
195
+ name = "quote"
196
+ version = "1.0.47"
197
+ source = "registry+https://github.com/rust-lang/crates.io-index"
198
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
199
+ dependencies = [
200
+ "proc-macro2",
201
+ ]
202
+
203
+ [[package]]
204
+ name = "rayon"
205
+ version = "1.12.0"
206
+ source = "registry+https://github.com/rust-lang/crates.io-index"
207
+ checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
208
+ dependencies = [
209
+ "either",
210
+ "rayon-core",
211
+ ]
212
+
213
+ [[package]]
214
+ name = "rayon-core"
215
+ version = "1.13.0"
216
+ source = "registry+https://github.com/rust-lang/crates.io-index"
217
+ checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
218
+ dependencies = [
219
+ "crossbeam-deque",
220
+ "crossbeam-utils",
221
+ ]
222
+
223
+ [[package]]
224
+ name = "regex"
225
+ version = "1.13.1"
226
+ source = "registry+https://github.com/rust-lang/crates.io-index"
227
+ checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
228
+ dependencies = [
229
+ "aho-corasick",
230
+ "memchr",
231
+ "regex-automata",
232
+ "regex-syntax",
233
+ ]
234
+
235
+ [[package]]
236
+ name = "regex-automata"
237
+ version = "0.4.16"
238
+ source = "registry+https://github.com/rust-lang/crates.io-index"
239
+ checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
240
+ dependencies = [
241
+ "aho-corasick",
242
+ "memchr",
243
+ "regex-syntax",
244
+ ]
245
+
246
+ [[package]]
247
+ name = "regex-syntax"
248
+ version = "0.8.11"
249
+ source = "registry+https://github.com/rust-lang/crates.io-index"
250
+ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
251
+
252
+ [[package]]
253
+ name = "rustversion"
254
+ version = "1.0.23"
255
+ source = "registry+https://github.com/rust-lang/crates.io-index"
256
+ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
257
+
258
+ [[package]]
259
+ name = "serde"
260
+ version = "1.0.229"
261
+ source = "registry+https://github.com/rust-lang/crates.io-index"
262
+ checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
263
+ dependencies = [
264
+ "serde_core",
265
+ "serde_derive",
266
+ ]
267
+
268
+ [[package]]
269
+ name = "serde_core"
270
+ version = "1.0.229"
271
+ source = "registry+https://github.com/rust-lang/crates.io-index"
272
+ checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
273
+ dependencies = [
274
+ "serde_derive",
275
+ ]
276
+
277
+ [[package]]
278
+ name = "serde_derive"
279
+ version = "1.0.229"
280
+ source = "registry+https://github.com/rust-lang/crates.io-index"
281
+ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
282
+ dependencies = [
283
+ "proc-macro2",
284
+ "quote",
285
+ "syn 3.0.3",
286
+ ]
287
+
288
+ [[package]]
289
+ name = "serde_json"
290
+ version = "1.0.151"
291
+ source = "registry+https://github.com/rust-lang/crates.io-index"
292
+ checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
293
+ dependencies = [
294
+ "itoa",
295
+ "memchr",
296
+ "serde",
297
+ "serde_core",
298
+ "zmij",
299
+ ]
300
+
301
+ [[package]]
302
+ name = "syn"
303
+ version = "2.0.119"
304
+ source = "registry+https://github.com/rust-lang/crates.io-index"
305
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
306
+ dependencies = [
307
+ "proc-macro2",
308
+ "quote",
309
+ "unicode-ident",
310
+ ]
311
+
312
+ [[package]]
313
+ name = "syn"
314
+ version = "3.0.3"
315
+ source = "registry+https://github.com/rust-lang/crates.io-index"
316
+ checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
317
+ dependencies = [
318
+ "proc-macro2",
319
+ "quote",
320
+ "unicode-ident",
321
+ ]
322
+
323
+ [[package]]
324
+ name = "target-lexicon"
325
+ version = "0.13.5"
326
+ source = "registry+https://github.com/rust-lang/crates.io-index"
327
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
328
+
329
+ [[package]]
330
+ name = "unicode-ident"
331
+ version = "1.0.24"
332
+ source = "registry+https://github.com/rust-lang/crates.io-index"
333
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
334
+
335
+ [[package]]
336
+ name = "unindent"
337
+ version = "0.2.4"
338
+ source = "registry+https://github.com/rust-lang/crates.io-index"
339
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
340
+
341
+ [[package]]
342
+ name = "zmij"
343
+ version = "1.0.23"
344
+ source = "registry+https://github.com/rust-lang/crates.io-index"
345
+ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"