formulaa 0.1.0

WYSIWYG TUI math editor rendering Unicode/ASCII-art formulas
Documentation
//! The inline super/subscript bijections (`x²`, `aᵢ`): the two forward
//! tables and one merged reverse table, phf like every other symbol
//! table. A test pins them to each other, so a pair cannot be added to
//! one side only (phf itself rejects duplicate keys).

/// base -> superscript.
pub static SUPERSCRIPTS: phf::Map<char, char> = phf::phf_map! {
    '0' => '', '1' => '¹', '2' => '²', '3' => '³', '4' => '',
    '5' => '', '6' => '', '7' => '', '8' => '', '9' => '',
    '+' => '', '-' => '', '=' => '', '(' => '', ')' => '',
    'n' => '', 'i' => '',
};

/// base -> subscript.
pub static SUBSCRIPTS: phf::Map<char, char> = phf::phf_map! {
    '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' => '',
};

/// superscript | subscript -> base (the merged reverse table; which
/// script a char is comes from the forward tables).
pub static UNSCRIPTS: phf::Map<char, char> = phf::phf_map! {
    '' => '0', '¹' => '1', '²' => '2', '³' => '3', '' => '4',
    '' => '5', '' => '6', '' => '7', '' => '8', '' => '9',
    '' => '+', '' => '-', '' => '=', '' => '(', '' => ')',
    '' => 'n', '' => 'i',
    '' => '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',
};

pub fn superscript_char(c: char) -> Option<char> {
    SUPERSCRIPTS.get(&c).copied()
}

pub fn subscript_char(c: char) -> Option<char> {
    SUBSCRIPTS.get(&c).copied()
}

pub fn unsuperscript_char(c: char) -> Option<char> {
    UNSCRIPTS
        .get(&c)
        .copied()
        .filter(|&b| SUPERSCRIPTS.get(&b) == Some(&c))
}

pub fn unsubscript_char(c: char) -> Option<char> {
    UNSCRIPTS
        .get(&c)
        .copied()
        .filter(|&b| SUBSCRIPTS.get(&b) == Some(&c))
}

#[cfg(test)]
mod tests {
    use super::*;
    /// The three tables are one bijection: every forward pair reverses,
    /// every reverse pair is claimed by exactly one forward table.
    #[test]
    fn script_tables_are_bijections() {
        for (fwd, other) in [(&SUPERSCRIPTS, &SUBSCRIPTS), (&SUBSCRIPTS, &SUPERSCRIPTS)] {
            for (&base, &script) in fwd.entries() {
                assert_eq!(UNSCRIPTS.get(&script), Some(&base), "{script} reverses");
                // The two scripts never share a glyph (the reverse
                // table could not tell them apart).
                assert!(
                    !other.values().any(|&v| v == script),
                    "{script} is in both scripts"
                );
            }
        }
        for (&script, &base) in UNSCRIPTS.entries() {
            assert!(
                SUPERSCRIPTS.get(&base) == Some(&script) || SUBSCRIPTS.get(&base) == Some(&script),
                "{script} -> {base} has no forward pair"
            );
        }
    }
}