Skip to main content

weimo_wdf/
lib.rs

1//! `weimo-wdf` implements **WDF**, the Weimo Document Format: a small,
2//! strict, canonical model for rich-text documents, plus adapters that convert
3//! between the formats editors actually speak.
4//!
5//! # What it does
6//!
7//! Every conversion goes through one canonical in-memory model
8//! ([`WdfDocument`]). Adapters never convert formats into each other directly,
9//! which keeps N formats at N adapters instead of N×N bridges:
10//!
11//! | Format | Direction | API |
12//! | --- | --- | --- |
13//! | WDF JSON (`weimo.document`) | in/out | [`validate_and_canonicalize`], [`WdfDocument`] (serde) |
14//! | Markdown | in/out | [`from_markdown`], [`to_markdown`] |
15//! | Tiptap / ProseMirror JSON | in/out | [`from_tiptap`], [`to_tiptap`] |
16//! | Apple document JSON | in/out | [`from_apple`], [`to_apple`] |
17//! | Terminal text | out | [`render_plain`], [`render_ansi`] |
18//!
19//! A side-by-side reference of every supported concept across the four
20//! formats — including structural divergences, canonicalization edges and
21//! per-format strictness — is maintained in `docs/format-reference.md` in the
22//! crate repository.
23//!
24//! # The v0.1 subset
25//!
26//! WDF is intentionally tiny. v0.1 covers paragraphs, ATX headings (level
27//! 1–6), blockquotes, bullet and ordered lists, and `bold` as the only inline
28//! mark. Tables, links, images, inline code, nested lists and other marks are
29//! **not** modeled — on input they are rejected with structured diagnostics
30//! instead of being silently dropped or mangled.
31//!
32//! # Strictness by design
33//!
34//! Unknown nodes, unknown marks, unknown properties, empty lists and invalid
35//! heading levels are hard errors. Every failure is returned as a
36//! [`Diagnostic`] with a stable machine-readable `code` (for example
37//! `wdf.unsupported_mark`, `markdown.unsupported_code`), a `source` naming the
38//! adapter, and an optional JSON `path` — ready to surface as an HTTP 422 body
39//! or an editor squiggle.
40//!
41//! # Examples
42//!
43//! Convert Markdown into Tiptap JSON for a web editor:
44//!
45//! ```
46//! use weimo_wdf::{from_markdown, to_tiptap};
47//!
48//! let doc = from_markdown("# Release notes\n\n- **bold** item\n")
49//!     .expect("supported markdown");
50//! let tiptap = to_tiptap(&doc);
51//! assert_eq!(tiptap["content"][0]["type"], "heading");
52//! assert_eq!(tiptap["content"][1]["type"], "bulletList");
53//! ```
54//!
55//! Validate untrusted WDF JSON and inspect the diagnostics:
56//!
57//! ```
58//! use serde_json::json;
59//! use weimo_wdf::validate_and_canonicalize;
60//!
61//! let input = json!({
62//!     "schema": "weimo.document",
63//!     "version": 1,
64//!     "root": { "type": "doc", "content": [
65//!         { "type": "paragraph", "content": [
66//!             { "type": "text", "text": "hi", "marks": [{"type": "italic"}] }
67//!         ]}
68//!     ]}
69//! });
70//! let diagnostics = validate_and_canonicalize(&input).unwrap_err();
71//! assert!(diagnostics.iter().any(|d| d.code == "wdf.unsupported_mark"));
72//! ```
73//!
74//! Accepted input is also *canonicalized*: `\r\n` becomes `\n`, empty text
75//! runs are dropped, adjacent runs with the same marks are merged, and ordered
76//! lists always carry an explicit `start`. The returned [`WdfDocument`] is
77//! serde-serializable, so `serde_json::to_value(&doc)` gives you canonical WDF
78//! JSON directly.
79
80pub mod adapters;
81pub mod cli;
82pub mod diagnostic;
83pub mod model;
84pub mod validate;
85
86pub use adapters::apple::{
87    AppleWireBlock, AppleWireDocument, AppleWireParagraph, AppleWireRun, from_apple, to_apple,
88};
89pub use adapters::markdown::{from_markdown, to_markdown};
90pub use adapters::tiptap::{from_tiptap, to_tiptap};
91pub use cli::{render_ansi, render_plain};
92pub use diagnostic::{Diagnostic, Diagnostics};
93pub use model::*;
94pub use validate::validate_and_canonicalize;