slugrs 0.5.0

A fast, locale-aware slugify library for Rust
Documentation
/// Supported locales for special-case transliterations.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Locale {
    /// German
    De,
    /// Turkish
    Tr,
    /// Arabic
    Ar,
}

impl Locale {
    /// Apply language-specific character replacements.
    pub fn apply(self, input: &str) -> String {
        match self {
            Locale::De => apply_de(input),
            Locale::Tr => apply_tr(input),
            Locale::Ar => apply_ar(input),
        }
    }
}

fn apply_de(input: &str) -> String {
    // Estimate capacity: German umlauts expand to 2 chars, ß to 2 chars
    // Conservative estimate: assume 10% of chars need expansion
    let estimated_capacity = input.len() + (input.len() / 10);
    let mut out = String::with_capacity(estimated_capacity);

    for ch in input.chars() {
        match ch {
            'ä' => out.push_str("ae"),
            'ö' => out.push_str("oe"),
            'ü' => out.push_str("ue"),
            'ß' => out.push_str("ss"),
            'Ä' => out.push_str("Ae"),
            'Ö' => out.push_str("Oe"),
            'Ü' => out.push_str("Ue"),
            _ => out.push(ch),
        }
    }
    out
}

fn apply_tr(input: &str) -> String {
    // Turkish chars are 1:1 replacements, so capacity should be same or smaller
    let mut out = String::with_capacity(input.len());

    for ch in input.chars() {
        match ch {
            'ı' => out.push('i'),
            'İ' => out.push('I'),
            'ğ' => out.push('g'),
            'Ğ' => out.push('G'),
            'ş' => out.push('s'),
            'Ş' => out.push('S'),
            'ç' => out.push('c'),
            'Ç' => out.push('C'),
            'ö' => out.push('o'),
            'Ö' => out.push('O'),
            'ü' => out.push('u'),
            'Ü' => out.push('U'),
            _ => out.push(ch),
        }
    }
    out
}

fn apply_ar(input: &str) -> String {
    // Arabic chars can expand significantly ("shi", "gha", etc.)
    // Conservative estimate: 50% expansion for Arabic text
    let estimated_capacity = input.len() + (input.len() / 2);
    let mut out = String::with_capacity(estimated_capacity);
    for ch in input.chars() {
        match ch {
            'ا' => out.push('a'),
            'أ' => out.push('a'),
            'إ' => out.push_str("ii"),
            'آ' => out.push_str("aa"),
            'ب' => out.push('b'),
            'ت' => out.push('t'),
            'ث' => out.push_str("th"),
            'ج' => out.push('j'),
            'ح' => out.push_str("ha"),
            'خ' => out.push_str("kh"),
            'د' => out.push('d'),
            'ذ' => out.push_str("dh"),
            'ر' => out.push('r'),
            'ز' => out.push('z'),
            'س' => out.push('s'),
            'ش' => out.push_str("shi"),
            'ص' => out.push('s'),
            'ض' => out.push('d'),
            'ط' => out.push('t'),
            'ظ' => out.push('z'),
            'ع' => out.push_str("ea"),
            'غ' => out.push_str("gha"),
            'ف' => out.push('f'),
            'ق' => out.push('q'),
            'ك' => out.push('k'),
            'ل' => out.push('l'),
            'م' => out.push_str("mu"),
            'ن' => out.push('n'),
            'ه' => out.push('h'),
            'و' => out.push('w'),
            'ي' => out.push_str("iy"),
            'ى' => out.push_str("aa"),
            'ؤ' => out.push('u'),
            'ئ' => out.push('y'),
            'ء' => {} // hamza -> drop
            'ة' => out.push_str("at"),
            'ـ' => {} // tatweel -> drop
            // Harakat/diacritics: drop
            '\u{064B}' | '\u{064C}' | '\u{064D}' | '\u{064E}' | '\u{064F}' | '\u{0650}'
            | '\u{0651}' | '\u{0652}' => {}
            _ => out.push(ch),
        }
    }
    out
}