Skip to main content

Shaper

Struct Shaper 

Source
pub struct Shaper { /* private fields */ }
Expand description

The UTN #57 shaping engine and the canonical normalizer for Hudum (Locale::Mng, the only locale).

Construction is cheap (microseconds) and the value is Send + Sync, so one instance can be shared behind a reference for the lifetime of a program.

Implementations§

Source§

impl Shaper

Source

pub fn canonical_version(&self) -> Option<&'static str>

Version of the canonical Unicode selection policy ("mng-canonical/3"). Persist it next to stored normalized keys. Always Some: the Option remains from the locales without a normalize table the crate used to have.

Source

pub fn normalize(&self, text: &str) -> Result<String, Error>

Canonical, FVS-pinned encoding of one Mongolian word: within the normalize table’s domain, shape(x) == shape(y) ⟹ normalize(x) == normalize(y), and shape(normalize(x)) == shape(x).

Strict (the Python default): an uncovered shape is Error::NormalizationFallback. Errors with Error::NonMongolianChar on mixed-script input — see Shaper::normalize_text.

Source

pub fn normalize_allow_fallback(&self, text: &str) -> Result<String, Error>

Like Shaper::normalize, but an uncovered shape returns the input unchanged (Python strict=False).

Source

pub fn normalize_text(&self, text: &str) -> Result<String, Error>

Normalize free-form text: every Mongolian word run is normalized independently, everything else (spaces, punctuation, Latin, …) is copied verbatim. Strict like Shaper::normalize.

Source

pub fn normalize_text_allow_fallback(&self, text: &str) -> Result<String, Error>

Like Shaper::normalize_text, but an uncovered word is preserved unchanged.

Source§

impl Shaper

Source

pub fn new(locale: Locale) -> Shaper

Build the shaper for locale from the generated tables.

Source

pub fn locale(&self) -> Locale

The locale this shaper was built for.

Source

pub fn rule_names(&self) -> Vec<&'static str>

The names of the shaping rules, in the order they run.

Source

pub fn shape(&self, text: &str) -> Result<Vec<WrittenUnit>, Error>

Shape text into its written-unit sequence. MVS and nirugu appear verbatim as WrittenUnit::Mvs and WrittenUnit::Nirugu; a ZWJ appears as WrittenUnit::Zwj only where it changes the ink — between two letters that already join, next to a nirugu, doubled, or at a word edge whose joined and unjoined forms are the same shape, it is dropped (see redundant_zwj.rs for the rules and the reviewed edge table). An MVS is a segment edge, so a ZWJ next to it is judged like one at the word edge.

Nine written units render as exactly the same ink as a sequence of other units, and each is unified with that sequence here. Five expand — Dd (both positions), medial H, medial Hx and initial Cr come out as O A, A A, N N and O O. The other four contract, because their expansion ends in Aa, which is itself a duplicate: a chain-final A Aa becomes Aa (or A when it is the whole chain), O Aa becomes B2 and I Aa becomes G. That is what makes shape a fingerprint of the visible word — ᠠᠷᠠᠳ and ᠠᠷᠠᠤᠠ are one word and shape identically. The standard’s own unit sequence, which keeps all nine apart, is Shaper::shape_raw. docs/internals.md (“Duplicate encodings”) has the evidence and the termination argument.

Errors with Error::NonMongolianChar on anything but Mongolian letters, FVS, MVS, NNBSP, nirugu and ZWJ — use Shaper::normalize_text for mixed-script text.

Source

pub fn shape_str(&self, text: &str) -> Result<String, Error>

Shaper::shape joined with + (S+A+I+I+A), the CLI’s output format.

Source

pub fn same_shape(&self, a: &str, b: &str) -> Result<bool, Error>

Do a and b render the same glyph sequence?

Source

pub fn shape_detailed(&self, text: &str) -> Result<Vec<TokenDetail>, Error>

Per-token shaping breakdown (Python shape_detailed).

Source

pub fn trace(&self, text: &str) -> Result<ShapeTrace, Error>

Run the pipeline one rule at a time and record every condition change.

Source§

impl Shaper

Source

pub fn normalize_written_units( &self, units: &[WrittenUnit], ) -> Result<String, Error>

Encode an ordered written-unit sequence (e.g. the output of Shaper::shape) as canonical Unicode. Letter positions are inferred from order and the structural tokens; ZWJ is emitted only where Zwj is present in the request and changes the ink — the request goes through the same redundant-ZWJ filter and duplicate unification as Shaper::shape, so [L, I, Zwj, Aa] is encoded as L G, exactly like the text ᠯᠢ\u{200D}ᠡ᠋. The result is accepted only if it reshapes to that folded sequence.

An empty sequence returns "".

§Errors
  • Error::UnsupportedWrittenUnit — units[index] is outside the table’s vocabulary. The first offender is reported. Every WrittenUnit is in the bundled table, so this is a guard rather than an expected outcome.
  • Error::NoCanonicalEncoding — the table covers every unit, but the sequence has no encoding that reshapes back to it.
Source

pub fn normalize_positioned_written_units( &self, records: &[PositionedWrittenUnit], ) -> Result<String, Error>

Encode explicit HUD-position records as canonical Unicode (the API zvvnmod-utn57 uses). A complete multi-record chain runs init…fina; an incomplete edge gets an implicit ZWJ; a single init record is encoded bare where that bare letter reads back as the same record — every consonant, whose isolated form is its initial unit — and takes a trailing ZWJ where it would not: A and I, which the inventory also carries at isol, and O, which has no bare spelling; single medi / fina records get the joining context their position needs. Mvs and Nirugu require Control; explicit Zwj is rejected; at most MAX_POSITIONED_RECORDS records.

§Validation order

Checks run in a fixed order, and it is observable — callers such as zvvnmod-utn57 dispatch on the variant, so the first failing check decides which one they see:

  1. record limit — 2000 records containing a Zwj is Error::TooManyRecords, not Error::ExplicitZwj;
  2. explicit Zwj anywhere in the request;
  3. empty request — returns "";
  4. per record, the control/inventory check (Mvs and Nirugu need Control; every other (unit, position) must be in the HUD inventory), reporting the first offender;
  5. per multi-record chain, that the declared positions match the padded chain;
  6. delegation to Shaper::normalize_written_units.
§Errors
Source

pub fn parse_written_units(&self, text: &str) -> Result<Vec<WrittenUnit>, Error>

Parse the CLI’s written-unit spelling: explicit +-separated names (B+Aa) or a compact PascalCase string with exactly one segmentation over this shaper’s known units (BZwj). One trailing newline is tolerated; an ambiguous compact string is rejected.

Every name — explicit or compact — must belong to this shaper’s normalize table, not merely to WrittenUnit. That keeps the reported index identical to Python’s, which defers the same check to normalize_written_units.

§Errors
  • Error::InvalidUnitSpec — the spec contains whitespace or an empty + field, or the compact string has more than one segmentation.
  • Error::UnknownWrittenUnit — a name is outside the table’s vocabulary; on the compact path an unsegmentable string is reported whole, at index 0.
Source§

impl Shaper

Source

pub fn known_written_units(&self) -> Result<Vec<WrittenUnit>, Error>

Every written unit this shaper’s normalize table can encode, plus the structural controls (Mvs, Nirugu, Zwj), sorted by name.

These are exactly the names Shaper::normalize_written_units accepts; the Python bindings use the list to validate their input with Python-formatted messages.

§Errors

None: the Result remains from the locales without a normalize table the crate used to have.

Source

pub fn positioned_written_units( &self, ) -> Result<Vec<(WrittenUnit, Position)>, Error>

The HUD positioned inventory: every (unit, position) pair that Shaper::normalize_positioned_written_units accepts for a letter record (letter positions only — controls are not part of the inventory), sorted by unit name then position.

§Errors

None: the Result remains from the locales without a normalize table the crate used to have.

Trait Implementations§

Source§

impl Debug for Shaper

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Shaper

Source§

fn default() -> Shaper

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.