jetro
Fast JSON querying and shaping language
Jetro is a compact expression engine for JSON. It accepts JSON bytes, evaluates
a Jetro expression, and returns a serde_json::Value.
📖 The Jetro Book is the best place to start — guided tour, full grammar reference, every builtin with working examples, recipes, and a known-limitations page. Read it before the API docs.
use Jetro;
use json;
let data = br#"
{
"orders": [
{
"id": "ord_1001",
"status": "paid",
"total": 184.50,
"customer": {"name": "Ada", "tier": "gold"}
},
{
"id": "ord_1002",
"status": "refunded",
"total": 42.00,
"customer": {"name": "Grace", "tier": "silver"}
},
{
"id": "ord_1003",
"status": "paid",
"total": 312.20,
"customer": {"name": "Alan", "tier": "gold"}
}
]
}
"#;
let jetro = from_bytes?;
let report = jetro.collect?;
assert_eq!;
# Ok::
Why Jetro?
-
Byte-first API
The public API is intentionally small:Jetro::from_bytes(bytes)andcollect(expr). With default features, Jetro usessimd-json. -
Query and shape in one expression
Return scalars, arrays, objects, nested shapes, filters, projections, aggregates, and patches from the same expression language. -
Demand-aware execution
Jetro avoids adding hand-written fused functions for every useful chain. Builtins expose metadata about streaming, ordering, barriers, and demand, so the planner can compose optimizations algorithmically.$.orders .filter(status == "paid") .sort_by(-total) .take_while(customer.tier == "gold") .take(10) .map({id, customer: customer.name, total}) -
Lazy materialization
Raw bytes, tape data, structural indexes, and owned value trees are built only when a query needs them. -
Optimized execution paths
Eligible queries can run through structural indexes, borrowed value views, streaming pipelines, columnar paths, or the VM fallback.
Install
[]
= "0.5.6"
API
use Jetro;
let jetro = from_bytes?;
let value = jetro.collect?;
That is the stable top-level API.
Language Preview
$ root document
@ current item inside map/filter/lambda
$.user.name field access
$.user?.name null-safe field access
$.items[0] index
$.items[1:5] slice
$..price recursive descent
Query
$.books.filter(price > 10)
$.books.sort_by(-rating).take(5)
$.orders.filter(status == "paid").map(total).sum()
Shape
$.books.map({title, price})
$.orders.map({
id,
customer: customer.name,
city: customer.address.city,
total
})
Compose
{
"featured": $.books
.filter(rating >= 4.5)
.sort_by(-price)
.take(3)
.map({title, author, price}),
"stats": {
"count": $.books.count(),
"avg_price": $.books.map(price).avg(),
"tags": $.books.flat_map(tags).unique().sort()
}
}
Bind and Format
let min_total = 100 in
$.orders
.filter(total >= min_total)
.map({
id,
label: f"{customer.name}: ${total}"
})
Group and Index
$.orders.group_by(status)
$.users.index_by(id)
$.events.count_by(type)
Patch
$.user.name.set("Ada")
$.cart.items.filter(qty == 0).delete()
patch $ { .user.active: true }
Pattern Match
match $.user with {
{role: "admin"} -> "full",
{role: "user", verified: true} -> "limited",
{role: r, ...*rest} -> {...*rest, role: r},
_ -> "denied"
}
$..match {
{tag: "click", id: i} -> i,
_ -> false
}
Full syntax reference: jetro-core/src/SYNTAX.md
Examples
Pick fields
let result = jetro.collect?;
Filter, sort, and limit
let result = jetro.collect?;
Build a response object
let result = jetro.collect?;
Deep search
let result = jetro.collect?;
Patch-style update
let result = jetro.collect?;
Pattern match
let result = jetro.collect?;
Execution Model
Jetro lowers expressions into a physical plan before execution.
expression
-> parser
-> planner
-> physical plan
-> structural index / value-view pipeline / streaming pipeline / VM fallback
-> serde_json::Value
Optimized paths are implementation details. They preserve the same language semantics as the fallback executor.
CLI
For interactive use, see jetrocli.
Learn More
- INDEPTH.md - API and language examples
- jetro-core/src/SYNTAX.md - syntax reference
- SAFETY.md - unsafe inventory and safety notes
- CHANGELOG.md - release notes
License
MIT