Guide

Spec versioning

Generate schemas, migrate legacy specs, use deterministic expressions, and configure editor feedback.

View Markdown source

RusDox document specs have an explicit contract version:

version: 1

The same model is supported in YAML, JSON, and TOML. New specs and published examples must declare the version. During the pre-1.0 transition, a missing field is read as legacy version 1 so old files keep rendering, but the schema and editor tooling report the omission and recommend migration.

Compatibility policy

  • This release supports document spec version 1.
  • Unknown future versions fail semantic validation before any output is written.
  • Additive optional fields and new block variants may appear in compatible RusDox minor releases.
  • Existing field meaning, enum meaning, and escaping behavior do not change inside a spec version.
  • A removal, incompatible default, or expression-semantics change requires a new spec version and a migration path.
  • The Rust API and CLI still follow crate SemVer independently of the spec version.

This is the frozen v1 contract. Read Stability, versioning, and support for the cross-surface SemVer rules, deprecation window, MSRV, and release policy.

Generate the canonical schema directly from the installed binary:

rusdox schema rusdox schema --output rusdox-spec-v1.schema.json

The checked-in schema is generated from the Rust/serde types. Its public ID is https://othmaneblial.github.io/rusdox/schema/rusdox-spec-v1.schema.json and it applies to YAML, JSON, and TOML object shapes.

Migration

Inspect a file without changing it:

rusdox migrate legacy.yaml --check

Print the migrated form to stdout:

rusdox migrate legacy.yaml

Write atomically in place or to a separate file:

rusdox migrate legacy.yaml --in-place rusdox migrate legacy.json --output current.json

YAML migration preserves leading author comments. JSON and TOML are parsed and pretty-printed. Future versions are never silently downgraded.

Deterministic expressions

Nested paths work in values:

text: "{{ customer.address.city }}"

Supported filters are upper, lower, title, trim, and default("text"):

text: "{{ customer.name | trim | title }}" text: "{{ customer.owner | default(\"unassigned\") | upper }}"

Use doubled delimiters for literal braces:

text: "Show {{{{ customer.name }}}} literally"

This becomes Show {{ customer.name }} literally. Values are always inserted as document text and escaped by the DOCX/PDF writers. There is no raw OOXML, shell, function-call, network, or general-purpose evaluation mode.

The bounded conditional block selects one branch by truthiness or scalar equality:

  • type: when path: customer.active equals: true blocks:
  • type: body text: Active account otherwise:
  • type: body text: Inactive account

Conditions and filters share the same behavior in YAML, JSON, and TOML.

VS Code

The dependency-free extension in editors/vscode bundles the schema and provides block/field completion, hover help, enum suggestions, and inline diagnostics. Its local language-service tests run with:

npm test --prefix editors/vscode

The extension never uploads document content.