e62rs 1.5.0

An in-terminal E621/926 browser.
Documentation
//! sub/superscript stuff
use {once_cell::sync::Lazy, std::collections::HashMap};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// script mode (super/subscript)
pub enum ScriptMode {
    /// superscript
    Superscript,
    /// subscript
    Subscript,
}

/// mappings for converting ascii to superscript
static SUPERSCRIPT_MAP: Lazy<HashMap<char, char>> = Lazy::new(|| {
    [
        ('0', ''),
        ('1', '¹'),
        ('2', '²'),
        ('3', '³'),
        ('4', ''),
        ('5', ''),
        ('6', ''),
        ('7', ''),
        ('8', ''),
        ('9', ''),
        ('+', ''),
        ('-', ''),
        ('=', ''),
        ('(', ''),
        (')', ''),
        ('a', ''),
        ('b', ''),
        ('c', ''),
        ('d', ''),
        ('e', ''),
        ('f', ''),
        ('g', ''),
        ('h', 'ʰ'),
        ('i', ''),
        ('j', 'ʲ'),
        ('k', ''),
        ('l', 'ˡ'),
        ('m', ''),
        ('n', ''),
        ('o', ''),
        ('p', ''),
        ('r', 'ʳ'),
        ('s', 'ˢ'),
        ('t', ''),
        ('u', ''),
        ('v', ''),
        ('w', 'ʷ'),
        ('x', 'ˣ'),
        ('y', 'ʸ'),
        ('z', ''),
        ('A', ''),
        ('B', ''),
        ('D', ''),
        ('E', ''),
        ('G', ''),
        ('H', ''),
        ('I', ''),
        ('J', ''),
        ('K', ''),
        ('L', ''),
        ('M', ''),
        ('N', ''),
        ('O', ''),
        ('P', ''),
        ('R', 'ᴿ'),
        ('T', ''),
        ('U', ''),
        ('V', ''),
        ('W', ''),
    ]
    .into_iter()
    .collect()
});

/// mappings for converting ascii to subscript
static SUBSCRIPT_MAP: Lazy<HashMap<char, char>> = Lazy::new(|| {
    [
        ('0', ''),
        ('1', ''),
        ('2', ''),
        ('3', ''),
        ('4', ''),
        ('5', ''),
        ('6', ''),
        ('7', ''),
        ('8', ''),
        ('9', ''),
        ('+', ''),
        ('-', ''),
        ('=', ''),
        ('(', ''),
        (')', ''),
        ('a', ''),
        ('e', ''),
        ('h', ''),
        ('i', ''),
        ('j', ''),
        ('k', ''),
        ('l', ''),
        ('m', ''),
        ('n', ''),
        ('o', ''),
        ('p', ''),
        ('r', ''),
        ('s', ''),
        ('t', ''),
        ('u', ''),
        ('v', ''),
        ('x', ''),
    ]
    .into_iter()
    .collect()
});

/// convert between super/subscript
///
/// # Arguments
///
/// * `text` - the text to convert
/// * `mode` - the mode to convert to (superscript/subscript)
pub fn convert_script(text: &str, mode: ScriptMode) -> String {
    let map = match mode {
        ScriptMode::Superscript => &SUPERSCRIPT_MAP,
        ScriptMode::Subscript => &SUBSCRIPT_MAP,
    };

    text.chars()
        .map(|c| map.get(&c).copied().unwrap_or(c))
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_superscript_conversion() {
        assert_eq!(convert_script("x2", ScriptMode::Superscript), "ˣ²");
        assert_eq!(convert_script("a+b", ScriptMode::Superscript), "ᵃ⁺ᵇ");
    }

    #[test]
    fn test_subscript_conversion() {
        assert_eq!(convert_script("H2O", ScriptMode::Subscript), "H₂O");
        assert_eq!(convert_script("x0", ScriptMode::Subscript), "ₓ₀");
    }

    #[test]
    fn test_unmapped_chars() {
        assert_eq!(convert_script("xyz", ScriptMode::Subscript), "ₓyz");
    }
}