accent-proust 0.11.0

A Rust implementation of the Markdoc language: parse, validate, transform, render, and format.
Documentation
# accent-proust

A Rust implementation of the [Markdoc](https://markdoc.dev) language: parse,
validate, transform, render, and format.

Markdoc is CommonMark plus a tag syntax that turns documents into structured,
validatable content instead of pre-rendered HTML:

```markdown
{% callout type="note" %}
Tags nest, take typed attributes, and are checked against a schema.
{% /callout %}
```

**[Documentation and a live playground](https://zoosky.github.io/accent-proust)**
-- the engine compiled to WebAssembly, running in your browser.

## Install

```sh
cargo add accent-proust
```

## Render a document

```rust
use accent_proust::{builtins, parse, render, transform};

let document = parse::parse("# Title\n\nSome *text*.\n");
let tree = transform::transform(&document, &builtins::config());

assert_eq!(
    render::render_all(&tree.into_vec()),
    "<article><h1>Title</h1><p>Some <em>text</em>.</p></article>"
);
```

## Define a tag

A tag needs a schema before it validates or renders. `render` names the element
to emit; declared attributes reach the output, undeclared ones are an error.

```rust
use std::sync::Arc;

use accent_proust::validate::{self, MapSchemaSource, Schema, SchemaAttribute, ValidationType};

let mut schemas = MapSchemaSource::builtin();
schemas.insert_tag(
    "callout",
    Schema::new().render("div").attribute(
        "type",
        SchemaAttribute {
            attribute_type: Some(ValidationType::String),
            required: true,
            ..SchemaAttribute::default()
        },
    ),
);
let config = builtins::config_with(Arc::new(schemas));

let document = parse::parse("{% callout type=\"note\" %}\nBody\n{% /callout %}\n");
assert!(validate::validate_tree(&document, &config).is_empty());

let tree = transform::transform(&document, &config);
assert_eq!(
    render::render_all(&tree.into_vec()),
    "<article><div type=\"note\"><p>Body</p></div></article>"
);
```

Validation errors are data, not failures: you get a `Vec`, so an editor shows
every problem at once instead of the first one.

```rust
let document = parse::parse("{% callout %}\nBody\n{% /callout %}\n");

for error in validate::validate_tree(&document, &config) {
    println!("{}: {}", error.error.id, error.error.message);
    // attribute-missing-required: Missing required attribute: 'type'
}
```

Error ids match upstream Markdoc exactly, so tooling written against its codes
works unchanged.

## Format

`format` prints a tree as canonical Markdoc source. It normalises spacing inside
a tag and leaves your own spellings alone, so `__bold__` stays `__bold__`.

```rust
use accent_proust::format;

let document = parse::parse("{% callout   type=\"note\"  %}\nBody\n{% /callout %}\n");
assert_eq!(
    format::format(&document),
    "{% callout type=\"note\" %}\nBody\n{% /callout %}\n"
);
```

`format(parse(s))` is idempotent, so a tool can rewrite a file in place, and
`parse(format(ast))` gives back the same tree, so formatting loses nothing.

## Command line

The same engine as a command, for a documentation repository that wants a CI
gate and for anyone with a Markdoc file to tidy:

```sh
cargo install --path crates/accent-proust-cli    # the binary is `accent-proust`
accent-proust fmt --check docs/*.md
accent-proust validate --config schema.yaml --partials docs/partials docs/*.md
accent-proust render --config schema.yaml --var channel=stable docs/page.md
```

`fmt` reprints canonical source and, with `--check`, prints a diff and exits 1
if anything would change. `validate` reports `path:line:column: level[id]:
message` per error, or one JSON object per file with `--format json`, and
exits 1 on an error. `render`, `transform` and `parse` print HTML, the
renderable tree and the syntax tree. The configuration is a YAML or JSON file
in the same vocabulary the WebAssembly bindings read from an object, so a
schema declared for one host is accepted by the other; `--partials` is a
directory of files, which is the thing the browser cannot do. The crate's
[README](crates/accent-proust-cli/README.md) has the rest, exit codes
included.

## Bring your own CommonMark parser

The bundled tokenizer uses `pulldown-cmark`, behind the default
`pulldown-cmark-tokenizer` feature. Turn it off and implement `Tokenizer` if you
already parse CommonMark, or if you pin `pulldown-cmark` to a git revision --
Cargo treats that as a different package, so you would compile two CommonMark
parsers into one binary and render some documents through each.

```toml
accent-proust = { version = "*", default-features = false }
```

A CI job builds and tests the crate in exactly that shape, so it is supported
rather than tolerated. `Tokenizer` is one of three seams; the crate does no I/O,
reads no configuration, and decides no HTML policy. `SchemaSource` answers where
a schema comes from, and `TagRenderer` owns escaping and HTML policy. All three
are yours.

## Compatibility

Ported from upstream Markdoc `v0.5.9` (revision `afee1a4`). The tag language and
the error ids are the contract. CommonMark edge behaviour is not: upstream builds
on markdown-it, this crate on pulldown-cmark. Every deliberate difference is
declared in [`DIVERGENCES.md`](DIVERGENCES.md), never emulated silently.

Upstream's 105-case corpus is vendored and run as the test suite. Nothing fails;
"annotated" is a case exercising a declared divergence, counted apart so that
giving something up stays visible.

```sh
cargo test --test conformance -- --nocapture
# conformance: 95 green, 10 annotated, 0 failing (of 105)
```

## Contributing

The library's minimum supported Rust version is 1.96. Develop on stable, which
the test suite needs. See [AGENT.md](AGENT.md) for the gates and the workflow.

## Licence

MIT. See [LICENSE](LICENSE).

A compatible reimplementation derived from the MIT-licensed Markdoc source.

## The name

Marcel Proust composed *A la recherche du temps perdu* on strips of paper glued
into the manuscript to extend it. Those *paperoles* are exactly what a formatter
does: parse, mutate, and print canonical source.

`accent-proust` belongs to the family of [Accent CMS](https://accentcms.dev)
crates.