Skip to main content

moss_core/
lib.rs

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