use unicode_segmentation::UnicodeSegmentation;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Gender {
Masculine,
Feminine,
Neuter,
}
impl fmt::Display for Gender {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Gender::Masculine => write!(f, "masculine"),
Gender::Feminine => write!(f, "feminine"),
Gender::Neuter => write!(f, "neuter"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Case {
Nominative,
Genitive,
Dative,
Accusative,
Ablative,
Vocative,
}
impl fmt::Display for Case {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Case::Nominative => write!(f, "nominative"),
Case::Genitive => write!(f, "genitive"),
Case::Dative => write!(f, "dative"),
Case::Accusative => write!(f, "accusative"),
Case::Ablative => write!(f, "ablative"),
Case::Vocative => write!(f, "vocative"),
}
}
}
pub(crate) fn is_ius(nominative: &str) -> bool {
nominative.ends_with("ius") || nominative.ends_with("ium")
}
pub(crate) fn is_r(nominative: &str) -> bool {
nominative.ends_with('r')
}
pub fn is_i_stem_parisyllabic(nominative: &str, genitive: &str, gender: Gender) -> bool {
match gender {
Gender::Masculine | Gender::Feminine => {
if nominative.ends_with("is") || nominative.ends_with("ēs") {
syllables(nominative) == syllables(genitive)
} else {
false
}
},
Gender::Neuter => false
}
}
pub fn is_i_stem_two_consonant_ending(nominative: &str, stem: &str, gender: Gender) -> bool {
match gender {
Gender::Masculine | Gender::Feminine => {
if (nominative.ends_with('s') || nominative.ends_with('x')) && syllables(nominative) == 1 {
stem.graphemes(true).rev().take(2).all(|g| !is_vowel(g))
} else if (nominative.ends_with("ns") || nominative.ends_with("rs")) && syllables(nominative) > 1 {
true
} else {
false
}
},
Gender::Neuter => false,
}
}
pub fn is_i_stem_neuter(nominative: &str, gender: Gender) -> bool {
match gender {
Gender::Neuter if nominative.ends_with("al") || nominative.ends_with("ar") || nominative.ends_with('e') => true,
_ => false,
}
}
pub(crate) fn is_vowel(character: &str) -> bool {
match character {
"a" | "ā" | "e" | "ē" | "i" | "ī" | "o" | "ō" | "u" | "ū" => true,
_ => false,
}
}
pub(crate) fn does_end_with_vowel(stem: &str) -> bool {
let mut graphemes = stem.graphemes(true).rev();
match graphemes.next() {
Some(grapheme) => is_vowel(grapheme),
None => false,
}
}
pub(crate) fn syllables(word: &str) -> usize {
let mut iter = word.graphemes(true).peekable();
let mut count: usize = 0;
while let Some(c) = iter.next() {
if is_vowel(c) {
count += 1;
if let Some(next) = iter.peek() {
if is_diphthong(c, *next) {
iter.next();
}
}
}
}
count
}
pub(crate) fn is_diphthong(first: &str, second: &str ) -> bool {
match first {
"a" | "ā" => {
match second {
"e" | "ē" | "u" | "ū" => true,
_ => false,
}
},
"e" | "ē" => {
match second {
"i" | "ī" | "u" | "ū" => true,
_ => false,
}
},
"o" | "ō" => {
match second {
"e" | "ē" => true,
_ => false,
}
},
"u" | "ū" => {
match second {
"i" | "ī" => true,
_ => false,
}
},
_ => false,
}
}
pub fn is_i_stem(nominative: &str, genitive: &str, stem: &str, gender: Gender) -> bool {
is_i_stem_parisyllabic(&nominative, &genitive, gender) ||
is_i_stem_two_consonant_ending(&nominative, stem, gender) ||
is_i_stem_neuter(&nominative, gender)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_is_vowel() {
let vowels = "aāeēiīoōuū";
let consonants = "bcdfghjklmnpqrstvwxyz";
for v in vowels.graphemes(true) {
assert_eq!(is_vowel(v), true);
}
for c in consonants.graphemes(true) {
assert_eq!(is_vowel(c), false);
}
}
#[test]
fn test_syllables() {
assert_eq!(syllables("animalia"), 5);
assert_eq!(syllables("aenimalia"), 5);
assert_eq!(syllables("aneimalia"), 5);
assert_eq!(syllables("aniemalia"), 6);
}
#[test]
fn test_is_i_stem_hostis() {
let nom = "hostis";
let gen = "hostis";
assert_eq!(is_i_stem_parisyllabic(nom, gen, Gender::Masculine), true);
}
#[test]
fn test_is_i_stem_navis() {
let nom = "nāvis";
let gen = "nāvis";
assert_eq!(is_i_stem_parisyllabic(nom, gen, Gender::Feminine), true);
}
#[test]
fn test_is_i_stem_moles() {
let nom = "mōlēs";
let gen = "mōlis";
assert_eq!(is_i_stem_parisyllabic(nom, gen, Gender::Feminine), true);
}
#[test]
fn test_is_i_stem_civis() {
let nom = "cīvis";
let gen = "cīvis";
assert_eq!(is_i_stem_parisyllabic(nom, gen, Gender::Masculine), true);
}
#[test]
fn test_is_i_stem_nubes() {
let nom = "nūbēs";
let gen = "nūbis";
assert_eq!(is_i_stem_parisyllabic(nom, gen, Gender::Feminine), true);
}
#[test]
fn test_is_i_stem_animal() {
let nom = "animal";
assert_eq!(is_i_stem_neuter(nom, Gender::Neuter), true);
}
#[test]
fn test_is_i_stem_exemplar() {
let nom = "exemplar";
assert_eq!(is_i_stem_neuter(nom, Gender::Neuter), true);
}
#[test]
fn test_is_i_stem_mare() {
let nom = "mare";
assert_eq!(is_i_stem_neuter(nom, Gender::Neuter), true);
}
#[test]
fn test_is_i_stem_ars() {
let nom = "ars";
let stem = "art";
assert_eq!(is_i_stem_two_consonant_ending(nom, stem, Gender::Feminine), true);
}
#[test]
fn test_is_i_stem_dens() {
let nom = "dēns";
let stem = "dent";
assert_eq!(is_i_stem_two_consonant_ending(nom, stem, Gender::Masculine), true);
}
#[test]
fn test_is_i_stem_nox() {
let nom = "nox";
let stem = "noct";
assert_eq!(is_i_stem_two_consonant_ending(nom, stem, Gender::Feminine), true);
}
#[test]
fn test_is_i_stem_urbs() {
let nom = "urbs";
let stem = "urb";
assert_eq!(is_i_stem_two_consonant_ending(nom, stem, Gender::Feminine), true);
}
#[test]
fn test_is_i_stem_virtus() {
let nom = "virtūs";
let stem = "virtūt";
assert_eq!(is_i_stem_two_consonant_ending(nom, stem, Gender::Feminine), false);
}
#[test]
fn test_is_i_stem() {
assert_eq!(is_i_stem("amnis", "amnis", "amn", Gender::Masculine), true, "is_i_stem failed to detect the pure i-stem masculine noun amnis, amnis.");
assert_eq!(is_i_stem("animal", "animālis", "animāl", Gender::Neuter), true, "is_i_stem failed to detect the pure i-stem neuter noun animal, animālis.");
assert_eq!(is_i_stem("pars", "partis", "part", Gender::Feminine), true, "is_i_stem failed to detect the mixed i-stem feminine noun pars, partis.");
}
}