1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! **moss-core is the pure-Rust content engine behind [moss](https://mosspub.com),**
//! a desktop publishing app. It owns every transformation that turns a folder of
//! markdown into a website: parsing, wikilink resolution, HTML rendering,
//! frontmatter typing, and schema validation.
//!
//! Everything is **data in, data out** — strings and structs in; parsed ASTs,
//! diagnostics, and rendered HTML out. **Zero I/O, zero async, no global state.**
//! The filesystem, the network, and the async runtime all live one layer up, in
//! moss's host; this crate never touches them. That makes it deterministic,
//! trivially unit-testable, and embeddable in any Rust program — not just moss.
//!
//! # How it's laid out
//!
//! The modules cluster into four areas, plus the contract surface:
//!
//! - **Parse & render** — [`ast`] turns markdown into a typed tree (over
//! `pulldown-cmark`) and renders it back to HTML through interceptable hooks;
//! [`render`] emits media HTML (image/video/audio/iframe/pdf) for embeds.
//! - **Frontmatter & schema** — [`frontmatter`] parses YAML while preserving the
//! body byte-for-byte; [`frontmatter_typed`] is the canonical `FrontMatter`
//! struct; [`schema_fields`] is the single source of truth for built-in fields;
//! [`validation`] produces LSP-style diagnostics against a schema.
//! - **Links & content model** — [`resolve`] is the one place wikilinks and
//! embeds (`[[...]]`) become ordinary markdown links; [`content_graph`] does the
//! Obsidian-style fuzzy path matching underneath.
//! - **Utilities** — small stateless helpers the editor and build share:
//! [`slug`], [`date`], [`sort`], [`home`], [`page_kind`], [`heading`],
//! [`html_entities`] (the one decoder for text that arrives HTML-escaped), and
//! [`inert_regions`] (the one answer to "which byte ranges of this markdown
//! are code or comment, and therefore not live syntax?", shared by every
//! pre-parse scanner in moss).
//!
//! Plus [`contract`]: the design surface (W3C design tokens + the `moss-*` HTML
//! class table) that theme authors and codegen depend on.
//!
//! # Getting started
//!
//! Every entry point is a free function — pick the module and call it:
//!
//! ```
//! use moss_core::frontmatter;
//!
//! let raw = "---\ntitle: Hello\n---\n\nBody text";
//! let doc = frontmatter::parse(raw);
//! assert_eq!(doc.frontmatter.get("title").and_then(|v| v.as_str()), Some("Hello"));
//! assert_eq!(doc.body.trim(), "Body text"); // body preserved verbatim
//! ```
//!
//! From there: [`ast`] for the body tree, [`resolve`] to flatten wikilinks,
//! [`validation`] to lint frontmatter, and [`heading`] for anchors.
//!
//! # Guarantees
//!
//! Total functions: bad input degrades to a best-effort value, never an `Err` or
//! a panic. No `unsafe` (`#![forbid(unsafe_code)]`). Schema problems are reported
//! out-of-band as [`validation`] diagnostics, not return values.
//!
//! moss ships this crate in a host built with `panic = "abort"` (release
//! profile), so a panic on user input crashes the whole desktop app (see the
//! `date.rs` fix for the
//! editor-mount panic on Chinese filenames). The lint attributes below enforce
//! the panic-free contract — `string_slice` plus `unwrap_used`/`expect_used`,
//! all denied outside tests — each with a per-site escape-hatch rule.
// `clippy::string_slice` flags `&s[..n]` byte-indexed slicing on `&str`. That
// pattern crashed the editor on `纽约诸法门.md` — `len() < 10` is bytes, not
// chars, so the guard let the slice cut inside `法`. Safe call sites must
// carry a per-site `#[allow(clippy::string_slice)]` with a one-line rationale
// (e.g. "char-aligned: pos came from `find('/')`"). Audited at PR time, not
// "we hope no one writes the bug shape again."
//
// Exempted in tests via `cfg_attr(not(test), ...)`, for the same reason as the
// `unwrap_used`/`expect_used` pair below: the contract protects PRODUCTION —
// a panic there crashes the desktop app on user input. A test that slices past
// a char boundary just fails, which is the outcome a test wants. Prod code is
// at zero without a single `#[allow]`; keeping the deny unconditional would
// only tax assertion messages like `&html[..html.len().min(300)]`.
// `clippy::unwrap_used` / `clippy::expect_used` enforce the second half of the
// panic-free contract: production code must never `.unwrap()` / `.expect()`
// a value that could be `None`/`Err` at runtime. Test code (`#[cfg(test)]
// mod tests`) is exempted via `cfg_attr(not(test), ...)` because tests
// legitimately want to fail fast on assertion violations. Safe call sites
// must annotate with `#[allow(clippy::unwrap_used)]` + per-site rationale,
// same pattern as `clippy::string_slice`.
pub
pub use ;
pub use PageKind;
pub use ;