pklr
A pure Rust parser and evaluator for Apple's Pkl configuration language. No external binary or CLI required.
Features
- Lexer, parser, and evaluator written entirely in Rust
- Evaluates
.pklfiles toserde_json::Value - Import and amends resolution for local files
- Glob imports, as
import* "dir/*.pkl" as Modsdeclarations andimport*("dir/*.pkl")expressions - Persistent caching, cache preloading, and offline evaluation for
package://imports - Concurrent prefetching of remote
http(s)://andpackage://imports - String interpolation, lambdas, higher-order methods
- Rich error diagnostics via miette
Usage
use eval_to_json;
let json = eval_to_json?;
println!;
The API is synchronous. A default build uses the standard library for files
and ureq for HTTP, and creates no Tokio
runtime. Use EvaluatorBuilder::http_agent with a custom pklr::ureq::Agent
for proxy, certificate, or timeout configuration.
Before evaluating a module, pklr prefetches its remote http(s):// and
package:// imports, and theirs in turn, fetching each level in one batch.
The native capabilities run up to eight requests at once. Prefetching only
fills caches: a failed prefetch is retried when evaluation needs the module, so
results and errors are the same as fetching one module at a time.
Cargo features
| Feature | Default | What it adds |
|---|---|---|
eval-core |
via the others | The evaluator, without any host IO. Supply your own EvalCapabilities. |
native-io |
yes | NativeCapabilities, EvaluatorBuilder, eval_to_json, analyze_imports (std fs). |
http |
yes | HTTP imports and package downloads through ureq and rustls. |
package-zip |
yes | package:// zip archives. |
miette-diagnostics |
yes | Rich error diagnostics. |
async |
no | A reqwest HTTP backend on Tokio and async entry points. |
An embedder with its own IO can depend on the evaluator alone:
= { = "5", = false, = ["eval-core"] }
let mut evaluator = with_capabilities;
let value = evaluator.eval_file?;
EvalCapabilities methods are synchronous. A host with asynchronous IO can run
the evaluator on a blocking thread and block on its own futures inside them.
Override fetch_text_many and fetch_bytes_many to fetch prefetch batches
concurrently; the defaults fetch one URL after another. Both get a
FetchBudget (prefetching allows 64 MiB per evaluation) to charge response
bytes to: start no request once it is spent, and drop a body that no longer
fits.
Optional async support
The async feature is additive. It is for applications that already use
reqwest, and it fetches prefetch batches concurrently on Tokio:
= { = "5", = ["async"] }
let json = new
.http_client
.eval_to_json_async
.await?;
eval_async, eval_to_json_async and the free pklr::eval_to_json_async run
the synchronous evaluation on Tokio's blocking thread pool. With a reqwest
client, requests run on the caller's runtime under block_in_place when called
from a multi-threaded runtime, and on a private runtime otherwise, so the
synchronous methods also work with a reqwest client.
Package downloads can be shared across evaluator instances and reused without network access:
A host that already ships a copy of a package can seed the cache with it, so a config importing that package evaluates without any network round trip:
static PACKAGE: & = include_bytes!;
Cached content already on disk wins, so preloading never overrides a package fetched from the network, and a config pinning a different version still resolves that version normally.
License
MIT