# H2M
[![Crates.io][crates-badge]][crates-url]
[![Docs.rs][docs-badge]][docs-url]
[![CI][ci-badge]][ci-url]
[![License][license-badge]][license-url]
[![Rust][rust-badge]][rust-url]
[crates-badge]: https://img.shields.io/crates/v/h2m.svg
[crates-url]: https://crates.io/crates/h2m
[docs-badge]: https://img.shields.io/docsrs/h2m.svg
[docs-url]: https://docs.rs/h2m
[ci-badge]: https://github.com/qntx-labs/h2m/actions/workflows/rust.yml/badge.svg
[ci-url]: https://github.com/qntx-labs/h2m/actions/workflows/rust.yml
[license-badge]: https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg
[license-url]: LICENSE-MIT
[rust-badge]: https://img.shields.io/badge/rust-edition%202024-orange.svg
[rust-url]: https://doc.rust-lang.org/edition-guide/
**Fast, extensible HTML-to-Markdown converter for Rust — CommonMark + GFM, plugin architecture, zero `unsafe`.**
H2M converts HTML into clean Markdown with full CommonMark compliance and GitHub Flavored Markdown extensions. It uses a plugin-based rule system, supports reference-style links, relative URL resolution, and ships with a CLI that can fetch and convert web pages directly.
## Quick Start
### Install the CLI
**Shell** (macOS / Linux):
```sh
**PowerShell** (Windows):
```powershell
Or via Cargo:
```bash
cargo install h2m-cli
```
### CLI Usage
```bash
# Convert a URL directly
h2m https://example.com
# Extract only the article content
h2m --selector article https://blog.example.com/post
# Local file with GFM + referenced links, save to file
h2m --gfm --link-style referenced page.html -o output.md
# Pipe from stdin
# All formatting options
h2m --gfm --heading-style setext --strong underscores --fence tilde page.html
```
### Library Usage
```rust
// One-liner with CommonMark defaults
let md = h2m::convert("<h1>Hello</h1><p>World</p>").unwrap();
assert_eq!(md, "# Hello\n\nWorld");
```
```rust
// Full control with builder
use h2m::{Converter, Options};
use h2m::plugins::Gfm;
use h2m::rules::CommonMark;
let converter = Converter::builder()
.options(Options::default())
.use_plugin(CommonMark)
.use_plugin(Gfm)
.domain("example.com")
.build();
let md = converter.convert(r#"<a href="/about">About</a>"#).unwrap();
assert_eq!(md, "[About](http://example.com/about)");
```
## Design
- **CommonMark compliant** — headings, paragraphs, emphasis, strong, code blocks, links, images, lists, blockquotes, horizontal rules, line breaks
- **GFM extensions** — tables (with column alignment), strikethrough, task lists
- **Reference-style links** — full (`[text][1]`), collapsed (`[text][]`), and shortcut (`[text]`) styles
- **Domain resolution** — resolve relative URLs to absolute via the `url` crate (WHATWG compliant)
- **Plugin architecture** — extend with custom rules via the `Rule` trait; register with `Converter::builder().use_plugin()`
- **Keep / Remove** — selectively preserve raw HTML tags or strip them entirely
- **CSS selector extraction** — CLI `--selector` flag to convert only matching elements
- **Zero-copy fast paths** — `Cow<str>` for escaping and whitespace normalization; no allocation when input needs no transformation
- **`Send + Sync`** — `Converter` is immutable after build, safe to share across threads (compile-time assertion)
- **Strict linting** — Clippy `pedantic` + `nursery` + `correctness` (deny), zero warnings
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
- MIT License ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project shall be dual-licensed as above, without any additional terms or conditions.
---
<div align="center">
A **[QNTX](https://qntx.fun)** open-source project.
<a href="https://qntx.fun"><img alt="QNTX" width="369" src="https://raw.githubusercontent.com/qntx/.github/main/profile/qntx-banner.svg" /></a>
Code is law. We write both.
</div>