jetro-dart
Dart / Flutter FFI bindings for jetro a compact query language and JSON processing engine written in Rust.
Install
# Pure Dart (CLI / server / scripts)
dart pub add jetro_dart
# Flutter
flutter pub add jetro_dart
flutter config --enable-native-assets # one-time per machine
After installation:
import 'package:jetro_dart/jetro_dart.dart';
final j = Jetro.fromString(jsonText);
final paid = j.collect(
r'$.orders.filter(status == "paid").map(total).sum()');
j.dispose();
A real example
import 'dart:convert';
import 'package:jetro_dart/jetro_dart.dart';
final bytes = utf8.encode(jsonString);
final j = Jetro.fromBytes(bytes);
final report = j.collect(r'''
{
"top_paid_premium": $.orders
.filter(status == "paid" and total >= 100)
.filter(customer.tier == "gold" or customer.tier == "platinum")
.sort_by(-total)
.take(2)
.map({
order_id: id,
who: customer.name,
amount: total,
label: f"order {@.id}: {customer.name} ({customer.tier}) USD {@.total}",
line_total: items.map(qty * price).sum(),
last_event: match events.last() with {
{kind: "delivered", at: t} -> {state: "ok", at: t},
{kind: "shipped", at: t} -> {state: "moving", at: t},
{kind: "refund", reason: r} -> {state: "refund", reason: r},
_ -> {state: "unknown"}
}
}),
"paid_total": $.orders
.filter(status == "paid")
.map(total)
.sum()
}
''');
j.dispose();
The equivalent hand-written Dart (typed access, null-safe casts,
manual sort, manual projection, manual last_event switch) is ~50
lines and runs ~10× slower.
What is jetro?
Jetro is a query language plus an execution engine for JSON. It reads like
the iterator chains you already know — filter, map, sort_by, take,
flat_map, sum, avg, unique, group_by, count_by, match, let … in, f-strings, and so on and compiles each expression into a planned,
demand-aware pipeline that runs on top of a SIMD-accelerated JSON tape.
The same expression that filters an array can reshape the result into nested objects, mutate the document in place, group / index by a field, or pattern-match on the shape of a sub-tree.
Quick Language Preview
Path navigation
$ 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 over the whole document
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 into a single response payload
{
"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, index, count
$.orders.group_by(status)
$.users.index_by(id)
$.events.count_by(type)
Mutate documents in place
$.user.name.set("Ada")
$.cart.items.filter(qty == 0).delete()
patch $ { .user.active: true }
Pattern match on shape
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
}
The full grammar lives at jetro/jetro-core/src/SYNTAX.md. the canonical user guide is The Jetro Book.
Benchmark (cold single run)
The harness ports jetro/jetro-core/examples/bench_cold.rs to Dart
verbatim. Every engine pays the full per-call cost: parse the
document, parse + compile the query, execute, serialise the result.
No warm-up, no iteration averaging.
Engines compared
| Engine | What |
|---|---|
native |
Hand-written Dart loops over jsonDecode'd JSON. |
jetro |
This package — Rust pipeline via dart:ffi cdylib. |
jsonata |
jsonata_dart (pure-Dart implementation of JSONata). |
Sample run (N = 8 000 records, ~3.9 MB doc, M1 / macOS / release / Dart JIT):
| # | Case | native | jetro | jsonata |
|---|---|---|---|---|
| 1 | active top-100 expensive-item revenue | 76.53ms | 13.14ms (0.17x) | 73.52ms (0.96x) |
| 2 | flatmap+sort all-items+take+project | 65.98ms | 9.84ms (0.15x) | 143.54ms (2.18x) |
| 3 | sort+skip+take+project | 51.84ms | 7.46ms (0.14x) | 93.53ms (1.80x) |
| 4 | filter+flatmap-tags+unique | 51.87ms | 9.81ms (0.19x) | 51.74ms (1.00x) |
| 5 | flatmap+filter+map-arith+sum | 60.64ms | 10.01ms (0.17x) | 61.65ms (1.02x) |
| 6 | filter+sort+take+fstring | 47.75ms | 8.42ms (0.18x) | 55.76ms (1.17x) |
| 7 | filter+flatmap+avg | 54.49ms | 8.84ms (0.16x) | 42.77ms (0.78x) |
| 8 | sort+take+nested-computed-projection | 64.19ms | 7.76ms (0.12x) | 77.57ms (1.21x) |
| 9 | 5-stage filter chain + count | 42.43ms | 8.51ms (0.20x) | 40.77ms (0.96x) |
| 10 | count_by(active) / group_by+map | 44.56ms | 7.88ms (0.18x) | N/A |
| 11 | sort+take+map+unique (top-300 zips) | 54.02ms | 7.89ms (0.15x) | 61.44ms (1.14x) |
| 12 | flatmap+map+unique+len (all prices) | 69.68ms | 8.82ms (0.13x) | 51.15ms (0.73x) |
| 13 | filter+map+sum | 48.68ms | 7.37ms (0.15x) | 57.35ms (1.18x) |
| 14 | flat_map+filter+count | 45.92ms | 8.33ms (0.18x) | 64.45ms (1.40x) |
| 15 | filter+flat_map+map+sum | 35.86ms | 7.46ms (0.21x) | 57.18ms (1.59x) |
| 16 | sort_by+take+map (top10) | 41.56ms | 7.21ms (0.17x) | 66.63ms (1.60x) |
| 17 | map+unique (cities) | 40.63ms | 7.19ms (0.18x) | 61.64ms (1.52x) |
| 18 | map (deep projection) | 56.54ms | 12.88ms (0.23x) | 73.59ms (1.30x) |
| 19 | map f-string | 50.76ms | 8.78ms (0.17x) | 51.51ms (1.01x) |
| 20 | flat_map+map (all prices) | 55.13ms | 8.17ms (0.15x) | 63.49ms (1.15x) |
| 21 | filter+first | 46.49ms | 6.18ms (0.13x) | 48.03ms (1.03x) |
| 22 | skip+take+map (pagination) | 51.39ms | 6.79ms (0.13x) | 43.45ms (0.85x) |
| 23 | filter+map+avg | 46.21ms | 7.27ms (0.16x) | 38.14ms (0.83x) |
| 24 | README showcase (3-filter+sort+take+match) | 41.89ms | 9.12ms (0.22x) | N/A |
(Nx) after each engine = engine time ÷ native Dart time on the same case.
Numbers vary ±20 % run-to-run depending on host load; engine ordering is stable.
How install actually works
jetro_dart ships using the Dart hooks (Native Assets) protocol.
The build pipeline is:
- Compute the target triple for your host (or Flutter target).
- Try to download a prebuilt
libjetro_dart.{dylib,so,dll}from the GitHub Release matching the package version. - If that fails (offline, unsupported target), fall back to
cargo build --releaseagainst the bundled Rust workspace inrust/. Requires a Rust toolchain (rustup). - Register the resulting library as a
CodeAssetkeyed topackage:jetro_dart/src/bindings.dart, so the@Nativelookups in the Dart facade resolve automatically.
Supported prebuilt targets
| OS / arch | Triple |
|---|---|
| macOS arm64 | aarch64-apple-darwin |
| macOS x86_64 | x86_64-apple-darwin |
| Linux x86_64 | x86_64-unknown-linux-gnu |
| Linux aarch64 | aarch64-unknown-linux-gnu |
| Windows x86_64 | x86_64-pc-windows-msvc |
| Android arm64-v8a | aarch64-linux-android |
| Android armeabi-v7a | armv7-linux-androideabi |
| Android x86_64 | x86_64-linux-android |
Other targets fall through to source-build (cargo build).
From source (contributors)
cargo build --release -p jetro-dart-ffi
dart pub get
dart test # 81 tests
dart run example/jetro_dart_example.dart # walk through the language
dart run bench/bench_cold.dart [N] # default N = 8000
C ABI
JetroHandle* jetro_new(const uint8_t* bytes, size_t len);
void jetro_free(JetroHandle*);
JetroResult* jetro_collect(JetroHandle*, const uint8_t* expr, size_t expr_len);
int32_t jetro_result_ok(const JetroResult*); // 1 = ok, 0 = error
const uint8_t* jetro_result_data(const JetroResult*); // JSON bytes (ok) or UTF-8 error
size_t jetro_result_len(const JetroResult*);
void jetro_result_free(JetroResult*);
Success bytes are the JSON encoding of Jetro::collect's Value;
error bytes are the UTF-8 error message.
Related
- The Jetro Book — guided tour, full grammar reference, every builtin with worked examples.
- jetro — upstream Rust engine.
- jetrocli — interactive use in the terminal.
- jetro-py — Python binding, same engine.
License
MIT
Libraries
- jetro_dart
- Dart / Flutter API for jetro.