accent_proust/lib.rs
1//! A Rust implementation of the [Markdoc](https://markdoc.dev) language.
2//!
3//! Markdoc is CommonMark plus a tag syntax that turns documents into
4//! structured, validatable content instead of pre-rendered HTML:
5//!
6//! ```markdown
7//! {% callout type="note" %}
8//! Tags nest, take typed attributes, and are validated against a schema.
9//! {% /callout %}
10//! ```
11//!
12//! This crate implements that language as a pipeline of pure stages:
13//!
14//! ```text
15//! parse -> AST -> validate -> transform -> renderable tree -> format
16//! ```
17//!
18//! # What this crate does not do
19//!
20//! It performs no I/O, reads no configuration, and decides no HTML policy. It
21//! has no concept of a file, a theme, a template, or a plugin. Everything
22//! host-specific arrives as data the caller passes in, or through a trait the
23//! caller implements:
24//!
25//! - [`Tokenizer`](parse) segments Markdown. A default implementation over
26//! pulldown-cmark ships behind the `pulldown-cmark-tokenizer` feature, so a
27//! host that already owns a CommonMark parser can supply its own rather than
28//! compile a second one.
29//! - [`SchemaSource`](validate::SchemaSource) answers "what is the schema for
30//! this tag name?". Whether that answer comes from a file, a constant, or a
31//! sandboxed guest is the host's business, not this crate's;
32//! [`MapSchemaSource`](validate::MapSchemaSource) is the answer for a host
33//! that assembles it by hand.
34//! - [`TagRenderer`](render::TagRenderer) turns a validated tag into markup.
35//! Escaping, void elements, and HTML policy live there; the walk over the
36//! tree does not, which keeps the document's depth off the host's stack.
37//!
38//! That boundary is deliberate and is enforced by a CI job that builds and
39//! tests this crate with nothing else present.
40//!
41//! # Compatibility
42//!
43//! Ported from upstream Markdoc at revision `afee1a4` (v0.5.9). The tag
44//! language and the validation error ids are the contract; CommonMark edge
45//! behaviour is not, because upstream is built on markdown-it and this crate is
46//! built on pulldown-cmark. Every deliberate difference is recorded in
47//! `DIVERGENCES.md` at the repository root, which is normative rather than a
48//! changelog.
49//!
50//! # Conventions this crate commits to
51//!
52//! - **Public enums are `#[non_exhaustive]`.** Markdoc gained node types across
53//! its 0.5.x line; spelling them exhaustively would turn each new one into a
54//! breaking release. The one exception is
55//! [`SchemaKey`](validate::SchemaKey): its two variants are the two ways a
56//! node is looked up, not a list that grows with Markdoc, and a source that
57//! implements [`SchemaSource`](validate::SchemaSource) should stop compiling
58//! if a third appeared rather than silently answer `None`.
59//! - **Validation errors are data, not failures.** The validator returns a
60//! `Vec` of them. `Result::Err` is reserved for internal invariants.
61//! - **Output is deterministic.** Attribute order is authored order, never hash
62//! order, so two runs over the same input produce identical bytes.
63//! - **Panic-freedom is a promise.** Property tests assert the parser never
64//! panics on arbitrary input, and fuzzing precedes publication. An open
65//! parser is a claim about its attack surface.
66//!
67//! The promise covers values a **caller** builds as well as documents this
68//! crate parses, and it covers every way of touching one. Each public
69//! recursive type -- [`ast::Node`], [`ast::Value`], [`renderable::Tag`] and
70//! [`renderable::Scalar`] -- writes out all four of its traversals:
71//! [`Drop`], [`Clone`], [`PartialEq`] and [`Debug`]. A derived
72//! implementation of any of them recurses once per level, and a stack
73//! overflow aborts rather than panics, so a caller could otherwise kill the
74//! process with a value it assembled through the public API. Nothing here is
75//! `unsafe`: `Drop` and `PartialEq` walk a worklist, `Clone` walks
76//! post-order onto a plan and rebuilds bottom-up, and `Debug` emits from a
77//! token stack.
78//!
79//! Three costs, stated because they are invisible until met. A variant's
80//! contents are taken with [`std::mem::take`] rather than moved out, since a
81//! type with a manual `Drop` forbids the partial move. `Debug` output is
82//! observable, so the emitters are pinned against a mirror type that still
83//! derives it, in both `{:?}` and `{:#?}`. And equality over an
84//! [`indexmap::IndexMap`] field stays unordered, matching what that map's own
85//! `PartialEq` does rather than what a positional walk would be tempted to.
86
87pub mod ast;
88pub mod builtins;
89pub mod format;
90pub mod functions;
91pub mod grammar;
92pub mod parse;
93pub mod render;
94pub mod renderable;
95pub mod tags;
96pub mod transform;
97pub mod validate;