rusdox 1.0.0

Generate DOCX and PDF from YAML at Rust speed.
Documentation
# Spec Versioning and Authoring Tools

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](stability.md)
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.