Install
Or add to Cargo.toml:
[]
= "0.0.4"
Requires Rust 1.88.0 or later. Works on macOS, Linux, and Windows.
Overview
MDX Gen is an AST-first Markdown-to-HTML pipeline built on comrak with optional syntect highlighting and ammonia-backed sanitization. Every transformation happens on the comrak AST — not as post-render regex — which keeps the output predictable and fast.
Pipeline stages:
- Parse — Markdown → comrak AST (arena-allocated).
- Transform — custom blocks, mermaid diagrams, table enhancement.
- Collect — headings for table-of-contents callers.
- Render — AST → HTML, with class-based syntax highlighting.
- Sanitize — ammonia strips dangerous tags while preserving safe structural markup.
Features
| CommonMark + GFM | Tables, strikethrough, task lists, autolinks, footnotes, math |
| Class-based syntax highlighting | 20+ syntect themes, CSS stylesheet helper |
| Custom blocks | ::note, ::warning, ::tip, and user-defined variants |
| Enhanced tables | AST-level responsive wrappers and alignment classes |
| Table of contents | Vec<Heading> in document order with anchor ids |
| Streaming output | Write directly into any std::io::Write sink |
| Mermaid diagrams | Client-side hydration of fenced mermaid blocks |
| Plain-text extraction | For search indexes, excerpts, reading-time |
| Hardened sanitizer | No inline style, cached ammonia::Builder |
| Input size cap | Default 1 MiB, overridable |
| Fuzz harness | 3 cargo-fuzz targets exercising the public surface |
| Strict validation | Nine-check MarkdownOptions::validate |
Quick start
use ;
For a richer starting template (feature flags, options, pretty output), see examples/quickstart.rs.
Usage
Table of contents
use ;
let md = "# Intro\n\n## Background\n\n## Details\n";
let = process_markdown_with_toc.unwrap;
for h in &headings
// H1 Intro (#intro)
// H2 Background (#background)
// H2 Details (#details)
Each Heading { level, text, id } carries the anchor id that the rendered HTML actually uses, including comrak's dedup suffixes (-1, -2, …).
Streaming into a writer
use ;
use stdout;
let mut out = stdout.lock;
process_markdown_to_writer.unwrap;
process_markdown_to_writer and process_markdown_with_toc_to_writer skip the intermediate String — the sanitized path uses ammonia::Document::write_to() end-to-end.
Plain-text extraction
use ;
let text = process_markdown_to_plain_text.unwrap;
assert!;
assert!;
Useful for feeding search indexes, excerpt generators, or reading-time estimators.
Class-based syntax highlighting
use ;
let md = "```rust\nfn main() { println!(\"hi\"); }\n```\n";
let options = default.with_syntax_highlighting;
let html = process_markdown.unwrap;
// Serve this CSS alongside the HTML:
let css = theme_css.unwrap;
The highlighter emits <span class="…"> tokens (no inline style=), so you control the colour palette via CSS.
Mermaid diagrams
use ;
let md = "```mermaid\ngraph TD\n A --> B\n```\n";
let options = default.with_diagrams;
let body = process_markdown.unwrap;
// Drop the hydration script once, before </body>:
let script = hydration_script_html;
let page = format!;
Fenced mermaid blocks become sanitizer-safe <pre class="mermaid"> containers; the bundled hydration script loads mermaid.js from jsDelivr and turns every container into inline SVG. This mirrors how github.com renders mermaid diagrams in READMEs. examples/diagrams.rs covers all nine mermaid kinds — flowchart, sequence, ER, class, state, gantt, git graph, user journey, pie.
Custom blocks
use ;
let md = "\
::note
Heads up — custom blocks.
::
";
let options = default.with_custom_blocks;
let html = process_markdown.unwrap;
assert!;
Built-ins: note, info, warning, tip, important, caution. Class and title overrides come from CustomBlockConfig.
Enhanced tables
use Options;
use ;
let md = "| a | b |\n|---|---|\n| 1 | 2 |\n";
let mut comrak = default;
comrak.extension.table = true;
let options = default
.with_comrak_options
.with_enhanced_tables;
let html = process_markdown.unwrap;
assert!;
Sanitizer configuration
use ;
let cfg = new
.with_generic_attribute // opt back in for trusted content
.with_tag;
let options = default
.with_unsafe_html
.with_sanitizer_config;
let html = process_markdown.unwrap;
SanitizerConfig is the typed extension point for the ammonia allow-list — extra tags, per-tag attributes, generic attributes, per-tag allowed classes — without leaking ammonia::Builder through the public API.
Error handling
use ;
let huge = "x".repeat;
let options = default.with_max_input_size;
match process_markdown
MarkdownError covers ParseError, RenderError, CustomBlockError, InputTooLarge, IoError (writer path), InvalidOptionsError (from validate), and others. It implements From<std::io::Error> and From<ValidationError> so errors flow via ?.
Structured validation errors
MarkdownOptions::validate() returns Result<(), Vec<(String, ValidationError)>> — every failing check surfaces with its field name and a typed ValidationError variant. The pipeline folds this into a single MarkdownError::InvalidOptionsError when you call process_markdown, but callers can inspect the structured form directly before running the pipeline:
use ;
let options = default.with_header_ids;
if let Err = options.validate
Examples (14 standalone + 1 runner)
Each example is an independently-runnable binary (cargo run --example <name>):
| Group | Examples |
|---|---|
| Onboarding | basic, quickstart |
| Scenarios | docs, alerts, cms, security, diagrams |
| Output channels | styling, gallery, streaming, pipe |
| Integrators | search, bulk, errors |
| Runner | all |
Start with quickstart or docs for a realistic walkthrough; security doubles as a red-team regression suite (XSS, clickjacking, javascript: URLs, oversized input, blockquote bombs).
Feature flags
| Flag | Default | Description |
|---|---|---|
syntax_highlighting |
✓ | Enable syntect-backed highlighter, theme_css, apply_syntax_highlighting |
Minimal build: cargo build --no-default-features.
Breaking changes in 0.0.4
If you are upgrading from 0.0.3 (source only — 0.0.3 never shipped to crates.io):
yaml_supportfeature removed.mdx_gen::frontmatteris gone. The vendoredyaml_safeparser is parked on disk and will return through a standalone crate in a later release.commonsdependency removed.MarkdownOptions::validatenow returnsResult<(), Vec<(String, ValidationError)>>using the in-cratemdx_gen::validation::ValidationError. The oldFrom<commons::error::CommonError>andFrom<commons::validation::ValidationError>impls onMarkdownErrorare gone.MarkdownError::FrontmatterErrorvariant removed. No more frontmatter path = no variant to carry.
If you are upgrading from 0.0.2:
- MSRV raised to 1.88.
- Syntax highlighter is now class-based. Code blocks render as
<span class="…">tokens instead of inlinestyle="color:#…". Generate a matching stylesheet withmdx_gen::theme_css(theme_name). - Sanitizer no longer permits
styleon any tag. Opt back in (trusted content only) viaSanitizerConfig::with_generic_attribute("style"). MarkdownOptions::validateruns nine consistency checks; every failing check surfaces with its field name. The pipeline still converts the result into a singleMarkdownError::InvalidOptionsErrorfor callers ofprocess_markdown.
New surfaces since 0.0.2:
process_markdown_to_writer,process_markdown_with_toc,process_markdown_with_toc_to_writer,process_markdown_to_plain_text.MarkdownOptions::with_diagrams+hydration_script_html()— client-side mermaid rendering.SanitizerConfig,Heading,collect_headings,theme_css.MarkdownError::IoError, plusFrom<ValidationError>impls for composing validation results.fuzz/workspace with threelibfuzzer-systargets for ongoing parser hardening.
Full release notes: CHANGELOG.md.
Security
- Default sanitizer is on. Raw HTML flows through ammonia unless you set
allow_unsafe_html(true). styleattribute is stripped from every tag by default — explicit opt-in required.- Input size capped at 1 MiB by default; override via
with_max_input_size. cargo-fuzzharness underfuzz/with three targets (process_markdown,custom_blocks,sanitize_roundtrip). No crashes across ~2.3 M iterations on the initial smoke run.cargo-denyconfigured (deny.toml): license allow-list, advisory deny +yanked = "deny", wildcard bans, crates.io-only sources.
Report a vulnerability: SECURITY.md.
Development
Opt in to the pre-push hook (runs fmt + clippy + tests before every push):
See CONTRIBUTING.md for signed commits and PR guidelines.
THE ARCHITECT ᛫ Sebastien Rousseau THE ENGINE ᛞ EUXIS ᛫ Enterprise Unified Execution Intelligence System
License
Dual-licensed under Apache 2.0 or MIT, at your option.