A pure Rust parser and evaluator for Apple's Pkl configuration language. No external binary or CLI required.
- 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
use pklr::eval_to_json;
let json = eval_to_json(std::path::Path::new("config.pkl"))?;
println!("{}", json);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.
| 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:
pklr = { version = "5", default-features = false, features = ["eval-core"] }let mut evaluator = pklr::Evaluator::with_capabilities(MyCapabilities::new());
let value = evaluator.eval_file(std::path::Path::new("config.pkl"))?;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.
The async feature is additive. It is for applications that already use
reqwest, and it fetches prefetch batches concurrently on Tokio:
pklr = { version = "5", features = ["async"] }let json = pklr::EvaluatorBuilder::new()
.http_client(pklr::reqwest::Client::new())
.eval_to_json_async(std::path::Path::new("config.pkl"))
.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:
fn load_config() -> pklr::Result<serde_json::Value> {
pklr::EvaluatorBuilder::new()
.package_cache_dir(".pklr-cache")
.offline(true)
.eval_to_json(std::path::Path::new("config.pkl"))
}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: &[u8] = include_bytes!("pkg@1.0.0.zip");
fn load_bundled_config() -> pklr::Result<serde_json::Value> {
pklr::EvaluatorBuilder::new()
.package_cache_dir(".pklr-cache")
.preload_package("https://example.com/pkg@1.0.0.zip", "zip", PACKAGE)
.eval_to_json(std::path::Path::new("config.pkl"))
}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.
MIT