verba 0.5.1

A library for working with Latin words.
Documentation
use std::fmt;

use crate::decline as D;
use crate::adjective::endings as E;
use crate::inflection as I;

use super::{DictionaryForm, Termination, Group, Number, Gender, Case};

pub struct Regular {
    dictionary_form: super::DictionaryForm,
    // There are two instances where a regular adjective can have slightly 
    // different declined forms. The first are a group of first and second
    // declension adjectives often referred to as ŪNUS NAUTA. The second are
    // third declension adjectives that have consonant stem declined forms.  
    alternate_declension: bool,
}

impl fmt::Display for Regular {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        // Since DictionaryForm implements fmt::Display and since the desired
        // printed form is the dictionary form, self.dictionary can be written
        // to the formatter.
        write!(f, "{}", self.dictionary_form)
    }
}

#[derive(Clone, Debug)]
pub enum RegularError {
    InvalidDeclensionGroup(),
}

impl fmt::Display for RegularError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            RegularError::InvalidDeclensionGroup() => {
                write!(f, "The dictionary form does not match any regular adjective declension group.")
            },
        }
    }
}

impl Regular {
    /// Creates a regular Latin adjective. 
    /// 
    /// Latin adjectives have two declension groups. The first group declines
    /// like first and second declension Latin nouns, the second group declines
    /// like third declension Latin nouns. Third declension adjectives have an
    /// additional complication, the can be of one, two, or three terminations. 
    /// 
    /// Third declension adjectives with one termination require the singular
    /// nominative and genitive forms. For example, for the adjective atrōx,
    /// terrible, the dictionary form would be atrōx, atrōcis. 
    /// 
    /// Third declension adjectives with two terminations consists of two 
    /// forms: the masculine and feminine singular nominative and the neuter
    /// singular nominative. For example, for the adjective agilis, swift, the
    /// dictionary form would be agilis, agile. 
    /// 
    /// Third declension adjectives with three terminations consist of three
    /// forms: the masculine, feminine, and neuter singular nominatives. For 
    /// example, for the adjective alacer, lively, the dictionary form would be
    /// alacer, alacris, alacre. 
    /// 
    /// This function takes the dictionary form of a regular Latin adjective. 
    /// Depending on the form passed in, it will return a first and second 
    /// declension adjective or a third declension adjective with one, two, or
    /// three terminations. 
    /// 
    /// # Warning
    /// 
    /// Third declension adjectives can be either consonant-stem or i-stem. 
    /// Third declension adjectives created with this function will decline as
    /// i-stem. When creating a third declension consonant-stem adjective, use 
    /// [`regular_c_stem`] instead.
    /// 
    /// # Example
    /// ```
    /// use verba::adjective as A;
    /// 
    /// let dictionary_form = A::DictionaryForm::Two("atrōx".to_string(), "atrōcis".to_string());
    /// 
    /// let adjective = A::Regular::new(dictionary_form);
    /// ```
    pub fn new(dictionary_form: super::DictionaryForm) -> Result<Regular, RegularError> {
        let dictionary_form = super::normalize_dictionary_form(dictionary_form);

        if let Err(error) = Regular::verify_dictionary_form(&dictionary_form) {
            Err(error)
        } else {
            Ok(Regular {
                dictionary_form,
                alternate_declension: false,
            })
        }
    }

    /// Like [`new`] this function creates a first and second declension Latin 
    /// adjective have have a distinctive singular genitive and dative form. 
    /// Adjectives of this type have a singulra genitive form ending in -īus 
    /// and a singular dative form ending in -ī for all three genders. 
    /// 
    /// There are nine adjectives of the first and second declension group that
    /// decline differently from other first and second declension adjectives. 
    /// These adjectives, commonly referred to by the acronym ŪNUS NAUTA are: 
    /// 
    /// ūllus, ūlla, ūllum
    /// nūllus, nūlla, nūllum
    /// uter, utra, utrum
    /// sōlus, sōla, sōlum
    /// neuter, neutra, neutrum
    /// alius, alia, aliud
    /// ūnus, ūna, ūnum
    /// tōtus, tōta, tōtum
    /// alter, altera, alterum
    /// 
    /// Use this function when creating these adjectives. 
    /// 
    /// # Example
    /// ```
    /// use verba::adjective as A;
    /// 
    /// let dictionary_form = A::DictionaryForm::Three("ūllus".to_string(), "ūlla".to_string(), "ūllum".to_string());
    /// 
    /// let adjective = A::Regular::new_first_second_ius_genitive(dictionary_form);
    /// ```
    pub fn new_first_second_ius_genitive(dictionary_form: super::DictionaryForm) -> Result<Regular, RegularError> {
        let dictionary_form = super::normalize_dictionary_form(dictionary_form);

        match super::not_normalized_group(dictionary_form.first(), dictionary_form.second(), dictionary_form.third()) {
            Some(Group::FirstSecond) => {
                Ok(Regular {
                    dictionary_form,
                    alternate_declension: true,
                })
            },
            _ => Err(RegularError::InvalidDeclensionGroup())
        }
    }

    /// Like [`new`] this function creates a regular third declension Latin 
    /// adjective. However, adjectives created with this function will decline
    /// as consonant-stems instead of i-stems. 
    /// 
    /// Most third declension adjectives decline the same way as an i-stem 
    /// noun. However, there are a handful of third declension adjectives with
    /// one termination that decline like consonant stem nouns. For example, 
    /// the singular ablative of most third declension adjectives with one 
    /// termination ends in -ī but consonant stem adjectives such as vetus, 
    /// veteris end in -e. 
    /// 
    /// # Example
    /// ```
    /// use verba::adjective as A;
    /// 
    /// let dictionary_form = A::DictionaryForm::Two("vetus".to_string(), "veteris".to_string());
    /// 
    /// let adjective = A::Regular::new_thid_c_stem(dictionary_form);
    /// ```
    pub fn new_thid_c_stem(dictionary_form: super::DictionaryForm) -> Result<Regular, RegularError> {
        let dictionary_form = super::normalize_dictionary_form(dictionary_form);

        match super::not_normalized_group(dictionary_form.first(), dictionary_form.second(), dictionary_form.third()) {
            Some(Group::Third(_)) => {
                Ok(Regular {
                    dictionary_form,
                    alternate_declension: true,
                })
            },
            _ => Err(RegularError::InvalidDeclensionGroup())
        }
    }

    /// Verifies that the dictionary matches a regular adjective declension 
    /// group. 
    fn verify_dictionary_form(dictionary_form: &DictionaryForm) -> Result<(), RegularError> {
        if super::not_normalized_group(dictionary_form.first(), dictionary_form.second(), dictionary_form.third()).is_none() {
            Err(RegularError::InvalidDeclensionGroup())
        } else {
            Ok(())
        }
    }

    fn endings<'a>(&self, number: Number, case: Case, gender: Gender) -> Option<E::Suffixes<'a>> {
        match super::not_normalized_group(self.dictionary_form.first(), self.dictionary_form.second(), self.dictionary_form.third()) {
            Some(Group::FirstSecond) if self.alternate_declension => E::first_second_ius_genitive_endings(number, case, gender),
            Some(Group::FirstSecond) if D::is_r(self.dictionary_form.first()) => E::first_second_r_endings(number, case, gender),
            Some(Group::FirstSecond) => E::first_second_endings(number, case, gender),
            Some(Group::Third(_)) if self.alternate_declension => E::third_c_stem_endings(number, case, gender),
            Some(Group::Third(_)) => E::third_endings(number, case, gender),
            None => None
        }
    }
}

impl super::Adjective for Regular {
    fn stem(&self) -> Option<&str> {
        super::not_normalized_stem(self.dictionary_form.first(), self.dictionary_form.second(), self.dictionary_form.third())
    }

    fn group(&self) -> Option<Group> {
        super::not_normalized_group(self.dictionary_form.first(), self.dictionary_form.second(), self.dictionary_form.third())
    }

    fn decline(&self, number: Number, case: Case, gender: Gender) -> Option<Vec<String>> {
        match (self.group(), number, case, gender) {
            // The singular nominative and vocative masculine forms of first 
            // and  second declension adjectives cannot be created by combining
            // a stem with an ending. Therefore, the stored form must be 
            // returned. 
            (Some(Group::FirstSecond), Number::Singular, Case::Nominative, Gender::Masculine) |
            (Some(Group::FirstSecond), Number::Singular, Case::Vocative, Gender::Masculine) if D::is_r(self.dictionary_form.first()) => {
                match &self.dictionary_form {
                    super::DictionaryForm::Three(masculine, _, _) => Some(vec![masculine.to_string()]),
                    _ => None, // If self is a first or second declension adjective, self.dictionary_form should never be anything other than a DictionaryForm::Three so this should never be returned (should being the keyword).
                }
            },
            // The singular nominative and vocative forms (and accusative in 
            // the case of neuter adjectives) of third declension adjectives
            // also cannot be created by combining a stem with an ending. 
            (Some(Group::Third(Termination::Three)), Number::Singular, Case::Nominative, Gender::Masculine) |
            (Some(Group::Third(Termination::Three)), Number::Singular, Case::Vocative, Gender::Masculine) => {
                Some(vec![self.dictionary_form.first().to_string()])
            },
            (Some(Group::Third(Termination::Three)), Number::Singular, Case::Nominative, Gender::Feminine) |
            (Some(Group::Third(Termination::Three)), Number::Singular, Case::Vocative, Gender::Feminine) => {
                Some(vec![self.dictionary_form.second().to_string()])
            },
            (Some(Group::Third(Termination::Three)), Number::Singular, Case::Nominative, Gender::Neuter) |
            (Some(Group::Third(Termination::Three)), Number::Singular, Case::Accusative, Gender::Neuter) |
            (Some(Group::Third(Termination::Three)), Number::Singular, Case::Vocative, Gender::Neuter) => {
                match self.dictionary_form.third() {
                    Some(third) => Some(vec![third.to_string()]),
                    None => None, // This case should never happen for third declension adjectives with three terminations created with Regular::new. 
                }
            },
            (Some(Group::Third(Termination::Two)), Number::Singular, Case::Nominative, Gender::Neuter) |
            (Some(Group::Third(Termination::Two)), Number::Singular, Case::Accusative, Gender::Neuter) |
            (Some(Group::Third(Termination::Two)), Number::Singular, Case::Vocative, Gender::Neuter) => {
                Some(vec![self.dictionary_form.second().to_string()])
            },
            (Some(Group::Third(Termination::Two)), Number::Singular, Case::Nominative, _) | 
            (Some(Group::Third(Termination::Two)), Number::Singular, Case::Vocative, _) => {
                Some(vec![self.dictionary_form.first().to_string()])
            },
            (Some(Group::Third(Termination::One)), Number::Singular, Case::Accusative, Gender::Neuter) |
            (Some(Group::Third(Termination::One)), Number::Singular, Case::Nominative, _) |
            (Some(Group::Third(Termination::One)), Number::Singular, Case::Vocative, _) => {
                Some(vec![self.dictionary_form.first().to_string()])
            },
            _ => {
                match self.endings(number, case, gender) {
                    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::adjective as A;
    
    use crate::adjective::{Adjective, Group, Number, Gender, Case};
    use unicode_normalization::{is_nfc};

}