# ezu-style
**Ezu Style Spec** parser for the [`ezu`](../../README.md) workspace.
A pure `serde`-based reader for the node-DAG style language. Parsing
this crate produces a [`Document`] — a data structure, not an
evaluator. To execute a document, feed it to
[`ezu-graph::build_graph`](../ezu-graph) with a `NodeRegistry`.
## Spec at a glance
```json
{
"name": "watercolor-basic",
"tile-size": 512,
"pad": 24,
"sources": {
"glazing": { "type": "brush", "src": "builtin:watercolor_glazing" }
},
"nodes": {
"bg": { "op": "solid", "color": "#fbf6e6" },
"water_feat": { "op": "features", "name": "tile.water" },
"water": { "op": "fill-dabs", "features": "@water_feat",
"color": "#5876a0", "opacity": 0.22,
"radius-px": 7, "spacing-px": 3 },
"out": { "op": "blend", "base": "@bg", "over": "@water" }
},
"output": "@out"
}
```
References inside node fields use a prefix:
- `@name` — node reference (input wiring)
- `$name` — `params` substitution at build time
Each `features` node references a host-bound layer by `name`
(`tile.<layer>` for per-tile MVT/GeoJSON data) and carries an optional
`filter` (entries AND-combined; values are single literals or membership
lists) and an optional `min-zoom-field`.
The `sources` block is the single home for **every** external
resource the renderer pulls in — document-scoped files (brushes,
images) sit next to tile-scoped pyramids (MVT, PMTiles, DEM). Each
entry's `type` discriminates the variant; document-scoped variants
carry a `src` URI, tile-scoped variants a `url` template.
`src` URIs are explicit-scheme — pick one of:
- `builtin:NAME` — bundled brush / image included in `ezu-paint`'s
built-in bank, or a host-registered resource of the same name.
- `file:PATH` — local file resolved against `--assets-dir` (or
absolute). `.myb` / `.png` / `.webp` extensions are inferred from
the source `type` if omitted.
- `http(s)://...` — fetched by the host (CLI native, `prefetch_doc_assets`)
before the first render, then cached in the in-memory bank.
Tile-scoped source kinds:
- `dem` — raster-DEM tile pyramid (terrarium or mapbox-rgb encoding).
The host stitches the 3×3 neighbourhood into a per-tile
`ScalarField` (with `geo_scale` populated) and binds it under
`tile.<source-name>`, ready for the `dem` source node to pick up.
- `mvt` — XYZ MVT URL template (or a TileJSON document). The host
fetches one tile per render, decodes every layer, and binds each
one under `tile.<layer-name>` — the same names existing
`features` nodes already reference.
- `pmtiles` — PMTiles archive (local path or `http(s)://` URL).
Decoded layers bind the same way as `mvt`.
```json
"sources": {
"terrain": { "type": "dem", "encoding": "terrarium",
"url": "https://terrain.reearth.land/mapterhorn-egm08/terrarium/ellipsoid/{z}/{x}/{y}.webp",
"tile-size": 512, "max-zoom": 14 },
"basemap": { "type": "mvt",
"url": "https://example.com/tiles/{z}/{x}/{y}.pbf" }
}
```
For `mvt` / `pmtiles`, the source key (`basemap` above) is a label —
bindings still use the layer names from the decoded tile. Declare only
one MVT-flavoured source per style; later entries are ignored.
## Types
```rust
pub struct Document {
pub name: String,
pub tile_size: u32,
pub pad: u32,
pub params: IndexMap<String, ParamDecl>,
/// Document- and tile-scoped external data: brushes, images,
/// MVT / PMTiles / DEM pyramids.
pub sources: IndexMap<String, SourceDecl>,
pub nodes: IndexMap<String, NodeSpec>,
pub output: NodeRef,
}
pub struct NodeSpec { pub op: String, pub fields: serde_json::Map<String, Value> }
pub type FeatureFilter = HashMap<String, FilterMatch>;
pub enum FilterMatch { One(FilterAtom), Any(Vec<FilterAtom>) }
pub enum FilterAtom { Bool, Int, Float, Str }
```
## Example
```rust
let doc = ezu_style::Document::from_json(&json_text)?;
println!("{} ({} nodes)", doc.name, doc.nodes.len());
```
## License
MIT or Apache-2.0, at your option.