OxydeMark
Extensible Markdown pipelines powered by Rust.
Overview
OxydeMark is a Markdown processing engine built around an AST pipeline architecture. It combines a high-performance Rust core for parsing and rendering with Python bindings (via PyO3) for a flexible plugin system.
Markdown Input
-> Preprocessing Plugins (Python, text-level)
-> Rust Parser / rushdown (AST generation)
-> AST exposed to Python (AstNode tree)
-> AST Transformation Plugins (Python, AST-level)
-> Rust Renderer (HTML generation)
-> Postprocessing Plugins (Python, HTML-level)
-> Final Output
Installation
pip install oxydemark
Wheels are published for Linux, macOS and Windows. As an abi3-py312 build, a
single wheel per platform covers Python 3.12 and every later version.
To use the Rust core directly, without any PyO3 dependency:
cargo add oxydemark
To build from a checkout instead:
git clone https://github.com/noirbizarre/oxydemark.git
cd oxydemark
mise install
maturin develop
Quick Start
from oxydemark import OxydeEngine
engine = OxydeEngine()
md = """
# Hello OxydeMark
This is **extensible Markdown**.
"""
html = engine.render(md)
print(html)
For a one-shot conversion without the plugin pipeline, use
markdown_to_html;
to inspect or transform the tree, use
parse
and
render_ast;
to extract headings, a table of contents, a summary and typed frontmatter, use
parse_document.
Plugins
A plugin is any object implementing one or more of the preprocess,
transform and postprocess hooks. No base class, no registration:
from oxydemark import OxydeEngine
from oxydemark.contrib import AdmonitionPlugin, MentionPlugin
class Shouty:
def postprocess(self, html: str) -> str:
return html.upper()
engine = OxydeEngine(plugins=[AdmonitionPlugin(), MentionPlugin(), Shouty()])
print(engine.render("> [!NOTE]\n> Ping @alice\n"))
oxydemark.contrib ships four worked examples (admonitions, shortcodes,
mentions, lazy images). See the plugin guide for the full
authoring documentation, including the AST value-semantics rules.
Development
See CONTRIBUTING.md for the full development guide.
# Set up tools and hooks
mise install
mise run setup
# Common tasks
mise run build # Build the Rust crate
mise run test # Run tests
mise run lint # Run clippy
mise run fmt # Format code
mise run ci # Run all checks
mise run docs # Build the documentation site
Project Structure
oxydemark/
├── src/ # Rust core
│ ├── lib.rs # Crate root, public re-exports
│ ├── api.rs # Public API, parser/renderer wiring
│ ├── ast.rs # AstNode definition, arena-to-tree conversion
│ ├── extensions.rs # Comark parser/renderer extensions
│ ├── html_render.rs # AST-to-HTML renderer
│ ├── slug.rs # Anchor slug algorithm
│ ├── error.rs # OxydeError
│ └── python.rs # PyO3 binding layer (`python` feature)
├── python/oxydemark/ # Python package
│ ├── __init__.py # Re-exports from native module
│ ├── api.py # OxydeEngine, plugin protocols
│ ├── _core.pyi # Type stub for the native module
│ └── contrib/ # Example plugins (provisional surface)
├── tests/ # Rust and Python test suites
│ └── compliance/ # Shared fixtures, run by both harnesses
├── benchmarks/ # Markdown library comparison benchmarks
├── docs/ # Documentation site sources
│ ├── plugins.md # Plugin authoring guide
│ ├── api/ # API reference pages
│ └── specs/ # OMEPs (design decisions)
├── .github/workflows/ # CI, docs and release pipelines
├── Cargo.toml # Rust crate configuration
├── pyproject.toml # Python build config (maturin)
├── mise.toml # Task runner and tool versions
├── zensical.toml # Documentation site configuration
├── cliff.toml # Changelog generation
├── prek.toml # Pre-commit hooks
├── CONTRIBUTING.md # Contribution guidelines
└── LICENSE # MIT License
Design Decisions
Architectural and tooling decisions are documented as OMEPs (OxydeMark
Enhancement Proposals) in docs/specs/. See the
OMEP index for the full list.