obadh_engine 0.9.1

A linguistically accurate Roman to Bengali transliteration engine
Documentation
//! Definitions for Bengali consonants
//!
//! This file contains the mappings for Bengali consonants, organized by their phonetic categories,
//! as well as information about conjunct formation.

use super::insert_unique;
use std::collections::HashMap;
use std::sync::OnceLock;

/// A Roman input rule and its Bengali consonant output.
pub type ConsonantRule = (&'static str, &'static str);

/// Organizes consonants by their phonetic groups (vargas) and characteristics
pub struct ConsonantSystem {
    /// Velar consonants (k-varga)
    pub velars: &'static [ConsonantRule],
    /// Palatal consonants (c-varga)
    pub palatals: &'static [ConsonantRule],
    /// Retroflex consonants (ṭ-varga)
    pub retroflexes: &'static [ConsonantRule],
    /// Dental consonants (t-varga)
    pub dentals: &'static [ConsonantRule],
    /// Labial consonants (p-varga)
    pub labials: &'static [ConsonantRule],
    /// Semivowels and liquids
    pub semivowels: &'static [ConsonantRule],
    /// Fricatives and others
    pub fricatives: &'static [ConsonantRule],
    /// Special consonants
    pub special: &'static [ConsonantRule],
}

const VELARS: &[ConsonantRule] = &[
    ("k", ""),  // ka
    ("kh", ""), // kha
    ("Kh", ""), // titlecase aspirated kha alias
    ("KH", ""), // uppercase aspirated kha alias
    ("g", ""),  // ga
    ("gh", ""), // gha
    ("Gh", ""), // titlecase aspirated gha alias
    ("GH", ""), // uppercase aspirated gha alias
    ("Ng", ""), // nga
];

const PALATALS: &[ConsonantRule] = &[
    ("c", ""),   // ca
    ("ch", ""),  // cha
    ("chh", ""), // explicit aspirated cha alias
    ("C", ""),   // compact uppercase aspirated cha alias
    ("Ch", ""),  // titlecase aspirated cha alias
    ("CH", ""),  // uppercase aspirated cha alias
    ("Chh", ""), // titlecase aspirated cha alias
    ("CHH", ""), // uppercase aspirated cha alias
    ("J", ""),   // ja
    ("j", ""),   // ja
    ("jh", ""),  // jha
    ("Jh", ""),  // titlecase aspirated jha alias
    ("JH", ""),  // uppercase aspirated jha alias
    ("NG", ""),  // nya (palatized)
];

const RETROFLEXES: &[ConsonantRule] = &[
    ("T", ""),  // Ta
    ("Th", ""), // Tha
    ("TH", ""), // uppercase Tha alias
    ("D", ""),  // Da
    ("Dh", ""), // Dha
    ("DH", ""), // uppercase Dha alias
    ("N", ""),  // Na
];

const DENTALS: &[ConsonantRule] = &[
    ("t", ""),  // ta
    ("th", ""), // tha
    ("d", ""),  // da
    ("dh", ""), // dha
    ("n", ""),  // na (non-palatized)
];

const LABIALS: &[ConsonantRule] = &[
    ("p", ""),  // pa
    ("ph", ""), // pha
    ("Ph", ""), // titlecase aspirated pha alias
    ("PH", ""), // uppercase aspirated pha alias
    ("f", ""),  // alternative for pha
    ("b", ""),  // ba
    ("bh", ""), // bha
    ("Bh", ""), // titlecase aspirated bha alias
    ("BH", ""), // uppercase aspirated bha alias
    ("v", ""),  // alternative for bha
    ("m", ""),  // ma
];

const SEMIVOWELS: &[ConsonantRule] = &[
    ("z", ""), //    ("r", ""), //    ("l", ""), //];

const FRICATIVES: &[ConsonantRule] = &[
    ("sh", ""), // palatal śô
    ("S", ""),  // palatal śô
    ("Sh", ""), // retroflex ṣô
    ("SH", ""), // uppercase retroflex ṣô alias
    ("s", ""),  // dental sô
    ("h", ""),  //];

const SPECIAL: &[ConsonantRule] = &[
    ("R", "ড়"),  // ṛô
    ("Rh", "ঢ়"), // ṛhô
    ("y", "য়"),  // antastô yô
    ("Y", "য়"),  // antastô yô
    ("w", "ওয়"), // /w/ glide standalone (ওয়াটার); stays ব-ফলা inside a cluster
];

const CONSONANT_CATEGORIES: [&[ConsonantRule]; 8] = [
    VELARS,
    PALATALS,
    RETROFLEXES,
    DENTALS,
    LABIALS,
    SEMIVOWELS,
    FRICATIVES,
    SPECIAL,
];

const CONSONANT_RULE_COUNT: usize = VELARS.len()
    + PALATALS.len()
    + RETROFLEXES.len()
    + DENTALS.len()
    + LABIALS.len()
    + SEMIVOWELS.len()
    + FRICATIVES.len()
    + SPECIAL.len();

/// Returns a structured system of Bengali consonants
pub const fn consonant_system() -> ConsonantSystem {
    ConsonantSystem {
        velars: VELARS,
        palatals: PALATALS,
        retroflexes: RETROFLEXES,
        dentals: DENTALS,
        labials: LABIALS,
        semivowels: SEMIVOWELS,
        fricatives: FRICATIVES,
        special: SPECIAL,
    }
}

/// Returns ordered consonant rule categories for deterministic matcher construction.
pub const fn consonant_categories() -> [&'static [ConsonantRule]; 8] {
    CONSONANT_CATEGORIES
}

/// Look up a Bengali consonant from a Roman rule key without constructing or hashing a map.
pub fn consonant_value(roman: &str) -> Option<&'static str> {
    match roman {
        "k" => Some(""),
        "kh" | "Kh" | "KH" => Some(""),
        "g" => Some(""),
        "gh" | "Gh" | "GH" => Some(""),
        "Ng" => Some(""),
        "c" => Some(""),
        "ch" | "chh" | "C" | "Ch" | "CH" | "Chh" | "CHH" => Some(""),
        "J" | "j" => Some(""),
        "jh" | "Jh" | "JH" => Some(""),
        "NG" => Some(""),
        "T" => Some(""),
        "Th" | "TH" => Some(""),
        "D" => Some(""),
        "Dh" | "DH" => Some(""),
        "N" => Some(""),
        "t" => Some(""),
        "th" => Some(""),
        "d" => Some(""),
        "dh" => Some(""),
        "n" => Some(""),
        "p" => Some(""),
        "ph" | "Ph" | "PH" | "f" => Some(""),
        "b" => Some(""),
        "bh" | "Bh" | "BH" | "v" => Some(""),
        "m" => Some(""),
        "z" => Some(""),
        "r" => Some(""),
        "l" => Some(""),
        "sh" | "S" => Some(""),
        "Sh" | "SH" => Some(""),
        "s" => Some(""),
        "h" => Some(""),
        "R" => Some("ড়"),
        "Rh" => Some("ঢ়"),
        "y" | "Y" => Some("য়"),
        "w" => Some("ওয়"),
        _ => None,
    }
}

/// Return whether a Roman rule key is a known consonant.
pub fn is_consonant(roman: &str) -> bool {
    consonant_value(roman).is_some()
}

fn build_consonants() -> HashMap<&'static str, &'static str> {
    let mut map = HashMap::with_capacity(CONSONANT_RULE_COUNT);

    for category in CONSONANT_CATEGORIES {
        for &(roman, bengali) in category {
            insert_unique(&mut map, "consonant", roman, bengali);
        }
    }

    map
}

/// Returns a shared flattened map of all Bengali consonants.
pub fn consonants_static() -> &'static HashMap<&'static str, &'static str> {
    static INSTANCE: OnceLock<HashMap<&'static str, &'static str>> = OnceLock::new();
    INSTANCE.get_or_init(build_consonants)
}

/// Returns a flattened map of all Bengali consonants
pub fn consonants() -> HashMap<&'static str, &'static str> {
    consonants_static().clone()
}