ezu-style 0.3.0

Ezu Style Spec: declarative style specification for painterly map rendering
Documentation
# 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.