use std::fmt;
use smallvec::{smallvec};
use crate::decline as D;
use crate::inflection as I;
use crate::noun::endings as E;
use crate::unicode as U;
use super::{Group, Number, Gender, Case};
#[derive(Clone, Debug)]
pub struct Regular {
nominative: String,
genitive: String,
gender: Gender,
i_stem: bool,
}
impl fmt::Display for Regular {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}, {}", &self.nominative, &self.genitive)
}
}
#[derive(Clone, Debug)]
pub enum RegularError {
InvalidGenitiveEnding(),
InvalidThirdDeclensionSingularGenitive(),
InvalidSingularNominative(),
InvalidSingularGenitive(),
InvalidSingularNominativeAndGenitive(),
InconsistenGrouping(Option<Group>),
}
impl fmt::Display for RegularError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
RegularError::InvalidGenitiveEnding() => {
write!(f, "The ending of the singular genitive form does not match that of any declension group.")
},
RegularError::InvalidThirdDeclensionSingularGenitive() => {
write!(f, "The ending of the singular genitive form is not valid for third declension nouns.")
},
RegularError::InvalidSingularNominative() => {
write!(f, "The provided singular nominative form is invalid.")
},
RegularError::InvalidSingularGenitive() => {
write!(f, "The provided singular genitive form is invalid.")
},
RegularError::InvalidSingularNominativeAndGenitive() => {
write!(f, "Both the provided singular nominative and singular genitive forms are invalid.")
},
RegularError::InconsistenGrouping(group) => {
match group {
Some(Group::First) => write!(f, "Singular nominative and genitive forms are inconsistent. First declension nouns should have a singular nominative ending in 'a'."),
Some(Group::Second) => write!(f, "Singular nominative and genitive forms are inconsistent. Second declension nouns should have a singular nominative ending in 'us' or 'um'."),
Some(Group::Third) => write!(f, "Singular nominative is empty."),
Some(Group::Fourth) => write!(f, "Singular nominative and genitive forms are inconsistent. Fourth declension nouns should have a singular nominative ending in 'us' or 'ū'."),
Some(Group::Fifth) => write!(f, "Singular nominative and genitive forms are inconsistent. Fifth declension nouns should have a singular nominative ending in 'ēs'."),
None => write!(f, "Singular genitive form did not match any regular declension group."),
}
}
}
}
}
impl Regular {
pub fn new(nominative: String, genitive: String, gender: Gender) -> Result<Regular, RegularError> {
let nominative = U::normalize(nominative);
let genitive = U::normalize(genitive);
match Regular::verify_nom_and_gen(&nominative, &genitive, gender) {
Ok(maybe) => {
if maybe {
Ok(Regular {
nominative,
genitive,
gender,
i_stem: false,
})
} else {
Err(RegularError::InconsistenGrouping(super::group(&genitive)))
}
}
Err(error) => Err(error),
}
}
pub fn new_third_i_stem(nominative: String, genitive: String, gender: Gender) -> Result<Regular, RegularError> {
let nominative = U::normalize(nominative);
let genitive = U::normalize(genitive);
match super::not_normalized_group(&genitive) {
Some(Group::Third) => {
if nominative.is_empty() {
Err(RegularError::InvalidSingularNominative())
} else {
Ok(Regular {
nominative,
genitive,
gender,
i_stem: true,
})
}
},
_ => Err(RegularError::InvalidThirdDeclensionSingularGenitive()),
}
}
fn verify_nom_and_gen(nominative: &str, genitive: &str, gender: Gender) -> Result<bool, RegularError> {
match super::group(&genitive) {
Some(Group::First) => Ok(Regular::nom_has_ending(nominative, E::first_endings(Number::Singular, Case::Nominative, gender))),
Some(Group::Second) => {
Ok(
Regular::nom_has_ending(nominative, E::second_endings(Number::Singular, Case::Nominative, gender)) ||
Regular::nom_has_ending(nominative, Some(smallvec!["r"]))
)
},
Some(Group::Third) => Ok(!nominative.is_empty()),
Some(Group::Fourth) => {
Ok(
Regular::nom_has_ending(nominative, E::fourth_endings(Number::Singular, Case::Nominative, gender)) ||
Regular::nom_has_ending(nominative, E::fourth_u_endings(Number::Singular, Case::Nominative, gender))
)
},
Some(Group::Fifth) => Ok(Regular::nom_has_ending(nominative, E::fifth_endings(Number::Singular, Case::Nominative, gender))),
None => Err(RegularError::InvalidSingularGenitive()),
}
}
fn nom_has_ending(nominative: &str, endings: Option<E::Suffixes>) -> bool {
match endings {
Some(endings) => endings.iter().any(|&ending| nominative.ends_with(ending)),
None => false,
}
}
fn stem_ends_with_vowel(&self) -> bool {
match super::not_normalized_stem(&self.nominative, &self.genitive) {
Some(stem) => D::does_end_with_vowel(stem),
None => false,
}
}
fn endings(&self, number: Number, case: Case) -> Option<E::Suffixes> {
match super::not_normalized_group(&self.genitive) {
Some(Group::First) => E::first_endings(number, case, self.gender),
Some(Group::Second) if D::is_ius(&self.nominative) => E::second_ius_endings(number, case, self.gender),
Some(Group::Second) if D::is_r(&self.nominative) => E::second_r_endings(number, case, self.gender),
Some(Group::Second) => E::second_endings(number, case, self.gender),
Some(Group::Third) if self.i_stem => E::third_i_stem_endings(number, case, self.gender),
Some(Group::Third) => E::third_endings(number, case, self.gender),
Some(Group::Fourth) if self.nominative.ends_with("ū") => E::fourth_u_endings(number, case, self.gender),
Some(Group::Fourth) => E::fourth_endings(number, case, self.gender),
Some(Group::Fifth) if self.stem_ends_with_vowel() => E::fifth_vowel_stem_endings(number, case, self.gender),
Some(Group::Fifth) => E::fifth_endings(number, case, self.gender),
None => None,
}
}
}
impl super::Noun for Regular {
fn gender(&self) -> Gender {
self.gender
}
fn group(&self) -> Option<Group> {
super::not_normalized_group(&self.genitive)
}
fn stem(&self) -> Option<&str> {
super::not_normalized_stem(&self.nominative, &self.genitive)
}
fn decline(&self, number: Number, case: Case) -> Option<Vec<String>> {
match (self.group(), number, case) {
(Some(Group::Second), Number::Singular, Case::Nominative) if D::is_r(&self.nominative) => Some(vec![self.nominative.to_string()]),
(Some(Group::Second), Number::Singular, Case::Vocative) if D::is_r(&self.nominative) => Some(vec![self.nominative.to_string()]),
(Some(Group::Third), Number::Singular, Case::Nominative) => Some(vec![self.nominative.to_string()]),
(Some(Group::Third), Number::Singular, Case::Vocative) => Some(vec![self.nominative.to_string()]),
(Some(Group::Third), Number::Singular, Case::Accusative) if self.gender() == Gender::Neuter => Some(vec![self.nominative.to_string()]),
_ => {
match self.endings(number, case) {
Some(suffixes) => {
match self.stem() {
Some(stem) => Some(I::stem_with_endings(stem, &suffixes)),
None => None,
}
},
None => None,
}
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::noun::{Noun, Group, Number, Gender, Case};
use unicode_normalization::{is_nfc};
fn verify_normalization(noun: &Regular) {
assert!(is_nfc(&noun.nominative), "The singular nominative form of {}, {} is not stored in NFC form.", noun.nominative, noun.genitive);
assert!(is_nfc(&noun.genitive), "The singular genitive form of {}, {} is not stored in NFC form.", noun.nominative, noun.genitive);
}
fn verify_struct(noun: &Regular, nominative: &str, genitive: &str, gender: D::Gender) {
assert_eq!(noun.nominative, nominative.to_string(), "The stored singular nominative is incorrect.");
assert_eq!(noun.genitive, genitive.to_string(), "The stored singular genitive is incorrect.");
assert_eq!(noun.gender, gender, "The stored gender is incorrect.");
}
fn verify_regular(nominative: &str, genitive: &str, stem: &str, gender: Gender, group: Group) {
match Regular::new(nominative.to_string(), genitive.to_string(), gender) {
Ok(noun) => {
verify_normalization(&noun);
verify_struct(&noun, nominative, genitive, gender);
assert_eq!(noun.gender(), gender);
assert_eq!(noun.group(), Some(group));
assert_eq!(noun.stem(), Some(stem));
},
Err(error) => panic!("Failed to create regular noun {}, {}. Received the following error: {}", nominative, genitive, error),
}
}
#[test]
fn test_new_first() {
verify_regular("porta", "portae", "port", Gender::Feminine, Group::First);
}
#[test]
fn test_new_second_masculine() {
verify_regular("dominus", "dominī", "domin", Gender::Masculine, Group::Second);
}
#[test]
fn test_new_second_neuter() {
verify_regular("bellum", "bellī", "bell", Gender::Neuter, Group::Second);
}
#[test]
fn test_new_second_r() {
verify_regular("puer", "puerī", "puer", Gender::Masculine, Group::Second);
}
#[test]
fn test_new_third_masculine() {
verify_regular("dux", "ducis", "duc", Gender::Masculine, Group::Third);
}
#[test]
fn test_new_third_neuter() {
verify_regular("nōmen", "nōminis", "nōmin", Gender::Neuter, Group::Third);
}
#[test]
fn test_new_fourth() {
verify_regular("portus", "portūs", "port", Gender::Masculine, Group::Fourth);
}
#[test]
fn test_new_fifth() {
verify_regular("rēs", "reī", "r", Gender::Feminine, Group::Fifth);
}
#[test]
fn test_new_fifth_ei() {
verify_regular("diēs", "diēī", "di", Gender::Feminine, Group::Fifth);
}
}