mongol_norm/lib.rs
1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3//! # mongol-norm
4//!
5//! Shape-aware normalizer for Traditional Mongolian (Hudum) script — the engine behind the
6//! `mongol-norm` PyPI package, whose PyO3 binding lives in the same repository under `python/`.
7//!
8//! Traditional Mongolian in Unicode has a fundamental problem: the same visible word can be
9//! encoded in many different code-point sequences (letters share glyphs, FVS selectors create
10//! equivalent spellings, joiners and MVS suffixes collapse more letters together). This crate
11//! implements a lightweight shaping engine — the UTN #57 v4 Mongolian-specific phase, driven only
12//! by the rule data of UTN #57 / mongfontbuilder, no font needed — and, on top of it, a canonical
13//! normalizer: within the bundled table's domain, `shape(x) == shape(y)` implies
14//! `normalize(x) == normalize(y)`, and `shape(normalize(x)) == shape(x)`.
15//!
16//! * [`Shaper::shape`] — text → written-unit sequence (`Mvs` / `Nirugu` / `Zwj` appear verbatim),
17//! with the nine duplicate encodings unified (see [`Shaper::shape_raw`])
18//! * [`Shaper::same_shape`] — do two encodings render identically?
19//! * [`Shaper::normalize`] / [`Shaper::normalize_text`] — canonical, FVS-pinned Unicode
20//! * [`Shaper::normalize_written_units`] / [`Shaper::normalize_positioned_written_units`] —
21//! encode written units directly
22//! * [`Shaper::trace`] — per-rule condition transitions, for debugging and the golden fixtures
23//!
24//! The crate has no dependencies and builds for `wasm32-unknown-unknown`. Its data tables are
25//! generated from the repository's JSON by `python/scripts/gen_rust_tables.py`, and the Python
26//! package of the same version calls straight into this crate. Design:
27//! <https://github.com/Satsrag/mongol-norm/blob/main/docs/superpowers/specs/2026-09-01-rust-core-design.md>.
28
29/// The crate README is compiled and run as a doctest, so its example cannot rot.
30#[cfg(doctest)]
31#[doc = include_str!("../README.md")]
32struct ReadmeDoctests;
33
34mod duplicates;
35mod error;
36// clippy::all silences style lints on generated code (see the design doc).
37#[allow(clippy::all)]
38mod generated;
39mod normalize;
40mod rules;
41mod shaper;
42mod tables;
43mod token;
44mod unicode;
45mod written_units;
46
47#[doc(hidden)]
48pub mod cli;
49
50pub use error::Error;
51pub use generated::enums::{Alias, Condition, WrittenUnit};
52pub use shaper::{ConditionChange, RuleTransition, ShapeTrace, Shaper, TokenDetail};
53pub use tables::{Fvs, Locale, Position, UnitPosition};
54pub use unicode::{is_mongolian_letter, is_mongolian_word_char};
55pub use written_units::{PositionedWrittenUnit, MAX_POSITIONED_RECORDS};
56
57/// Crate version (the Cargo package version; lockstep with the Python package version).
58pub fn version() -> &'static str {
59 env!("CARGO_PKG_VERSION")
60}