#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Accent {
Hat,
Tilde,
Bar,
Vec,
Dot,
Ddot,
Check,
Ring,
Underline,
Utilde,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DrawnForm {
Center(char),
Fill(char),
Dots,
}
pub struct AccentInfo {
pub name: &'static str,
pub latex: &'static str,
pub wide_latex: &'static str,
pub under: bool,
pub drawn: DrawnForm,
pub preview: char,
}
pub static ACCENT_NAMES: phf::Map<&'static str, Accent> = phf::phf_map! {
"hat" => Accent::Hat,
"tilde" => Accent::Tilde,
"bar" => Accent::Bar,
"vec" => Accent::Vec,
"dot" => Accent::Dot,
"ddot" => Accent::Ddot,
"check" => Accent::Check,
"ring" | "mathring" => Accent::Ring,
"underline" => Accent::Underline,
"utilde" => Accent::Utilde,
};
impl Accent {
pub const ALL: [Accent; 10] = [
Accent::Hat,
Accent::Tilde,
Accent::Bar,
Accent::Vec,
Accent::Dot,
Accent::Ddot,
Accent::Check,
Accent::Ring,
Accent::Underline,
Accent::Utilde,
];
pub const fn info(self) -> &'static AccentInfo {
match self {
Accent::Hat => &AccentInfo {
name: "hat",
latex: "hat",
wide_latex: "widehat",
under: false,
drawn: DrawnForm::Center('˰'),
preview: 'ˆ',
},
Accent::Tilde => &AccentInfo {
name: "tilde",
latex: "tilde",
wide_latex: "widetilde",
under: false,
drawn: DrawnForm::Fill('˷'),
preview: '˜',
},
Accent::Bar => &AccentInfo {
name: "bar",
latex: "bar",
wide_latex: "overline",
under: false,
drawn: DrawnForm::Fill('_'),
preview: '¯',
},
Accent::Vec => &AccentInfo {
name: "vec",
latex: "vec",
wide_latex: "overrightarrow",
under: false,
drawn: DrawnForm::Center('⇾'),
preview: '→',
},
Accent::Dot => &AccentInfo {
name: "dot",
latex: "dot",
wide_latex: "dot",
under: false,
drawn: DrawnForm::Center('․'),
preview: '˙',
},
Accent::Ddot => &AccentInfo {
name: "ddot",
latex: "ddot",
wide_latex: "ddot",
under: false,
drawn: DrawnForm::Dots,
preview: '¨',
},
Accent::Check => &AccentInfo {
name: "check",
latex: "check",
wide_latex: "widecheck",
under: false,
drawn: DrawnForm::Center('˯'),
preview: 'ˇ',
},
Accent::Ring => &AccentInfo {
name: "ring",
latex: "mathring",
wide_latex: "mathring",
under: false,
drawn: DrawnForm::Center('˳'),
preview: '˚',
},
Accent::Underline => &AccentInfo {
name: "underline",
latex: "underline",
wide_latex: "underline",
under: true,
drawn: DrawnForm::Fill('¯'),
preview: '_',
},
Accent::Utilde => &AccentInfo {
name: "utilde",
latex: "utilde",
wide_latex: "utilde",
under: true,
drawn: DrawnForm::Fill('˜'),
preview: '˷',
},
}
}
pub fn name(self) -> &'static str {
self.info().name
}
pub fn of_name(name: &str) -> Option<Accent> {
ACCENT_NAMES.get(name).copied()
}
pub fn latex(self) -> &'static str {
self.info().latex
}
pub fn wide_latex(self) -> &'static str {
self.info().wide_latex
}
pub fn under(self) -> bool {
self.info().under
}
pub fn drawn(self) -> DrawnForm {
self.info().drawn
}
pub fn glyph(self) -> char {
match self.drawn() {
DrawnForm::Center(g) | DrawnForm::Fill(g) => g,
DrawnForm::Dots => '․',
}
}
pub fn cells(self) -> Vec<char> {
match self.drawn() {
DrawnForm::Dots => vec!['․', '․'],
_ => vec![self.glyph()],
}
}
pub fn widen(self, next: char) -> Option<Accent> {
Accent::ALL
.into_iter()
.find(|a| a.cells() == [self.glyph(), next])
}
pub fn is_mark_material(c: char) -> bool {
Accent::ALL.iter().any(|a| a.glyph() == c)
}
pub fn of_over_glyph(c: char) -> Option<Accent> {
Accent::ALL
.into_iter()
.find(|a| !a.under() && a.drawn() != DrawnForm::Dots && a.glyph() == c)
}
pub fn of_under_glyph(c: char) -> Option<Accent> {
Accent::ALL
.into_iter()
.find(|a| a.under() && a.glyph() == c)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn previews_are_distinct() {
let mut seen = std::collections::HashSet::new();
for a in Accent::ALL {
assert!(
seen.insert(a.info().preview),
"{:?} reuses the preview {:?}",
a,
a.info().preview
);
}
}
#[test]
fn drawn_glyphs_are_unambiguous() {
for a in Accent::ALL {
assert_eq!(Accent::of_name(a.name()), Some(a));
assert_eq!(Accent::of_name(a.latex()), Some(a));
match (a.under(), a.drawn()) {
(_, DrawnForm::Dots) => assert_eq!(a, Accent::Ddot),
(false, _) => assert_eq!(Accent::of_over_glyph(a.glyph()), Some(a)),
(true, _) => assert_eq!(Accent::of_under_glyph(a.glyph()), Some(a)),
}
}
assert_eq!(Accent::of_over_glyph('˷'), Some(Accent::Tilde));
assert_eq!(Accent::of_under_glyph('˜'), Some(Accent::Utilde));
assert_eq!(Accent::of_over_glyph('_'), Some(Accent::Bar));
assert_eq!(Accent::of_under_glyph('¯'), Some(Accent::Underline));
}
}