Skip to main content

Crate markdown_contract

Crate markdown_contract 

Source
Expand description

The Rust engine for markdown-contract, the twin of the TypeScript package @osdlc/markdown-contract. The shared corpus in packages/ts/markdown-contract/corpus/ pins the two to the same findings for every YAML-expressible fixture: the same count and ids in order, and the level, line and hint where a fixture records them (tests/corpus.rs).

validate() runs every check in one pass: the projection, the structure plane, the content plane (frontmatter schema; table, list, code and maxWords leaves), the text constraints and the document rules. Contracts come from the builders (Contract, section, rule and the rest) or the YAML loader (load_contract); run_corpus routes documents to contracts by glob.

use markdown_contract::{load_contract, validate};

let contract = load_contract("\
mcVersion: 2
kind: contract
body:
  sections:
    - section: Context
")?;
let findings = validate("## Notes\n", &contract, "note.md");
assert_eq!(findings[0].id, "structure/section-missing");

The core takes strings and does no I/O, so it builds for wasm32-unknown-unknown. Reading files lives behind the native feature (on by default; build with default-features = false for wasm32).

A text rule’s regex and a frontmatter string’s pattern use the regex crate’s dialect, not JavaScript’s: no lookaround or backreferences, and \w, \d and \b are Unicode-aware. The loader rejects a source the crate cannot compile. The README lists the other differences from the TS package.

select picks part of a note the way an Obsidian link fragment does (#Heading, #Parent#Child, #^block-id), as a byte range or as text; DocTree has the same as methods, and every SectionNode and BlockNode carries its byte range in DocTree::body.

This file is a barrel only: it re-exports, it holds no logic.

Modules§

select
Selecting part of a note the way an Obsidian link fragment does: [[Note#Heading]], [[Note#Parent#Child]] and [[Note#^block-id]].

Structs§

CodeConfig
A code(...) leaf’s content config — the TS CodeConfig.
Contract
A compiled contract for one markdown class: frontmatter schema, body grammar, and doc-scoped rules.
CorpusConfig
The directory → contract config: a flat ORDERED list of rules. Rule order is significant — a file is validated against the FIRST rule it matches.
CorpusRule
One routing rule: the files that match include and not exclude are checked against contract.
Ctx
The rule author’s finding factory — the engine fills path and the id’s default level.
DocTree
The projected document: the frontmatter block (when present), the verbatim body after it, and the synthetic root whose sections are the top-level body sections and whose name is the leading-H1 title.
EditRange
A start–end source range for a TextEdit.
Finding
One validation finding — the unit of the interchange format.
FindingSpec
The input to Ctx::finding — id and message required, level/pos/hint optional.
Fix
The fix slot on a finding: a description, optionally with a concrete edit.
Frontmatter
The frontmatter block. data is the parsed YAML in the JSON value shape the TS yaml package’s toJS() gives, or None when the YAML does not parse. pos is the opening --- fence (line 1). The frontmatter plane finds the line of a key, and the error of YAML that does not parse, by reading raw.
FrontmatterSpec
The frontmatter plane’s compiled schema — an object Schema the plane runs over the parsed frontmatter data (the TS contract’s frontmatter Zod object).
FrontmatterSplit
The result of split_frontmatter.
GapSpec
A gap() window admitting unknown sections, optionally bounded.
GlobError
A glob set that failed to compile — a config error (exit-code-2 territory for the hosting layer).
LeafSpec
A content leaf: the structural kind-gate (checked by the structure plane) plus the typed config the content plane interprets.
LevelOpts
A level’s options; defaults mirror the TS engine (order: "none", allowUnknown: true).
ListConfig
A list(...) leaf’s content config, the TS ListConfig.
ListItem
One list item: flattened text, task-list checkbox state (when present), source position.
OneOfSpec
A oneOf slot: exactly one of several headings must appear.
Registry
The id → default-level registry, seeded with every plane’s defaults.
RunOptions
An optional global pre-filter, applied before rule matching (AND-narrowing): a file is considered only if it matches at least one include glob (when any are given) and no exclude glob.
RunOutcome
The run outcome: aggregated findings (each already deterministically sorted within its file, stamped with its file path), the exit code, and the run counts.
RunStats
Per-run counts returned alongside the findings. Always satisfies files_matched == matched_by_rule.iter().sum() and files_unmatched == files_scanned - files_matched.
SectionNode
A heading-delimited section: exact heading text, TRUE heading depth (a skipped level is preserved, not synthesized), heading position, nested subsections, heading-direct blocks (no hoisting), and section-level anchors.
SectionOpts
Per-section options — the TS SectionOpts.
SectionSeq
The body grammar of one level — the output of sections.
SectionSpec
A declared section slot: the heading and its aliases, plus options.
SourcePos
A single source point. 1-based line; col (1-based) rides along when known and is omitted from JSON when absent — matching the TS SourcePos.
TableConfig
A table(...) leaf’s content config — the TS TableConfig.
TextEdit
A machine-applicable repair an external tool could apply. Provisional — a Finding.fix only describes a remedy; this engine never edits documents.
TextMatchSpec
One match spec — the closed text-match vocabulary. Exactly one of pattern (a literal substring) or regex (a regex source) supplies the needle; normalize / ignore_case tune matching; min / max bound the count; id pins identity; note / level shape the finding.
TextRuleSpec
The document-scoped options for text_rule — requires / forbids lists, each a set of independent whole-document checks.
VaultRef
A recognized [[wikilink]] or ![[transclusion]] extracted from flattened text.

Enums§

BlockKind
The block kinds the structure plane’s kind-gate distinguishes.
BlockNode
A flattened content block directly under a section heading.
ConstValue
A literal value for const (YAML scalars only).
DeclarativeError
A declarative document that could not be compiled into a contract / config.
EveryItem
A list leaf’s per-item check — the checkbox gate, or a schema over each item’s text.
ExtraColumns
Whether undeclared table columns are tolerated or reported (content/table/column-extra).
FindingLevel
A finding’s severity. Each id has a default level in the registry; a rule or a text constraint’s level: can set another.
LeafConfig
The content checks a leaf carries: what the TS builders put on LeafSpec.config, with cell and item schemas compiled.
NativeRunError
What a directory run can fail on.
Order
order and allow_unknown are independent knobs over a level’s content model.
Schema
One compiled schema node. Bounds are f64 because JSON Schema bounds are numbers.
SectionContent
A section’s content declaration: a single leaf, or named leaves bound by ^anchor.
Spec
One element of a level’s ordered content model — the output of the builder functions.
StringFormat
The format names, each replicating zod v4’s validator for that format.
VaultRefKind
Whether a reference is a link or a transclusion.

Traits§

DocRule
A doc-scoped named rule, run once over the whole projected tree.
Rule
A node-scoped named rule, run against each declared, present section that carries it.

Functions§

compile_contract_object
Build a Contract from a { description?, frontmatter?, body? } mapping with no envelope, as an inline config contract is written. An mcVersion other than 2 and any other key are rejected.
doc_rule
A doc-scoped named rule from a closure — the TS docRule(id, fn).
extract_vault_refs
Every [[wikilink]] and ![[transclusion]] in already-flattened text, in document order. The alias is everything after the first |; the fragment is everything after the first # in what is left, so [[Note|alias#Part]] has the alias alias#Part and no fragment. The text between the brackets cannot contain [.
forbids
Forbid each listed phrase from appearing in the bound section’s subtree text — a node-local Rule. Each entry emits a text/forbids finding at the offending line for every hit (or text/count at the heading when a positive max cap is exceeded).
gap
An unbounded gap window admitting unknown sections.
gap_bounds
A bounded gap window — gap({ min, max }).
load_config
Compile a declarative YAML config (text) into a CorpusConfig. resolver reads a referenced .yaml / .yml contract file’s text (the fs seam).
load_config_file
Read a YAML config file and compile it. Contract refs resolve relative to the config file’s directory, which is made absolute without resolving symlinks, as Node’s path.resolve does.
load_contract
Compile a declarative YAML contract (text) into a runtime Contract.
load_contract_file
Read a *.contract.yaml file and compile it into a Contract.
one_of
One required slot with several interchangeable spellings — oneOf([...]).
one_of_with
A oneOf slot with options.
optional
Mark a spec optional — optional(section("Appendix")).
parse_document
Parse raw markdown (frontmatter + body) into the positioned projection. Positions in the returned tree are absolute document lines, matching the TS engine. The frontmatter’s data mirrors the TS yaml package’s toJS() value shape; it is None when the YAML does not parse.
requires
Require each listed phrase to be PRESENT in the bound section’s subtree text — a node-local Rule for a section’s rules slot. Each entry emits its own text/requires finding at the section heading when its phrase is absent (or text/count when a min / max bound is violated).
rule
A per-node named rule from a closure — the TS rule(id, fn).
run_corpus
Run a config across a file set and aggregate findings.
run_corpus_dir
Walk root and run the config over its files, reading only the files a rule routes. Invalid UTF-8 is replaced with U+FFFD, as Node’s utf8 read does.
section
A required section with a single exact heading — section("Overview").
section_with
A section with options — section("Summary", { anchor: "summary", … }).
sections
A level’s ordered content model — sections(opts, [ … ]).
split_frontmatter
Pure, byte-exact frontmatter/body split — no YAML parse, no projection. Same recognizer as the TS splitFrontmatter: the opening --- must be the very first line (after a byte-order mark, which is skipped), the block must be closed by a --- line (else the whole doc is body), and body begins after exactly one line terminator (CRLF or LF) past the closing fence.
text_rule
Attach required / forbidden phrase checks to the WHOLE document — a cross-plane DocRule for a contract’s rules slot. A requires miss is document-level (no position); each forbids hit anchors at the exact offending source line.
validate
The string-in convenience: parse (frontmatter split + projection), then validate every plane in the TS orchestration order.
validate_tree
Validate a pre-parsed tree against a contract: outline scan, structure plane (with node-local rules), content plane (frontmatter schema + content leaves), doc-scoped rules, deterministic sort.
walk_dir
Every file under root, as paths relative to root with / separators. Directory entries are sorted by name (in UTF-16 order, as JS compares strings) before recursion, so the order matches the TS walkSync. .git and node_modules are skipped.

Type Aliases§

ContractResolver
Resolves a contract-file ref (a .yaml / .yml path, possibly relative) to its YAML text.