verba 0.5.1

A library for working with Latin words.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
use std::fmt;

use crate::adjective::{Adjective};

use crate::adjective as A;
use crate::verb::endings as E;
use crate::unicode as U;

use super::{Number, Voice, Tense, Mood, Group, Person};

pub struct Regular {
    present_active_indicative: String,
    present_active_infinitive: String,
    perfect_active_indicative: String,
    perfect_passive_participle: A::Regular, // Creating the perfect passive participle requires declining the adjective form of the verb so it the form is stored as an adjective.
}

impl fmt::Display for Regular {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        // In order to display the dictionary form of a verb, the perfect 
        // passive participle, the fourth principle part, needs to be declined. 
        match &self.perfect_passive_participle.decline(Number::Singular, A::Case::Nominative, A::Gender::Neuter) {
            Some(declension) => {
                match declension.first() {
                    Some(first) => write!(f, "{}, {}, {}, {}", &self.present_active_indicative, &self.present_active_infinitive, &self.perfect_active_indicative, first),
                    None => write!(f, "{} {} {}", &self.present_active_indicative, &self.present_active_infinitive, &self.perfect_active_indicative),
                }
                
            },
            // Since Regular::new verifies that the adjective::Regular 
            // was created successfully and since there are no number, case, 
            // gender combinations that return None for a regular adjective,
            // this case should never be reached. 
            None => unreachable!(),
        }

        
    }
}

#[derive(Clone, Debug)]
pub enum RegularError {
    InvalidPresentActiveIndicativeEnding(),
    InvalidPresentActiveInfinitiveEnding(),
    InvalidPerfectActiveIndicativeEnding(),
    InvalidPerfectPassiveParticipleEnding(),
    FailedToCreateParticiple(A::RegularError),
}

impl fmt::Display for RegularError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            RegularError::InvalidPresentActiveIndicativeEnding() => {
                write!(f, "The ending of the present active indiciative form is incorrect.")
            },
            RegularError::InvalidPresentActiveInfinitiveEnding() => {
                write!(f, "The ending of the present active infinitive form does not match that of any conjugation group.")
            },
            RegularError::InvalidPerfectActiveIndicativeEnding() => {
                write!(f, "The ending of the perfect active indiciative form is incorrect.")
            },
            RegularError::InvalidPerfectPassiveParticipleEnding() => {
                write!(f, "The ending of the perfect passive participle form is does not match that of any first and second declension adjective gender.")
            },
            RegularError::FailedToCreateParticiple(adjective_error) => {
                write!(f, "Failed to create the participle form. Received the following error from crate::adjective::Regular::new: {}", adjective_error)
            },
        }
    }
}

impl Regular {
    fn create_participle_dictionary(stem: &str) -> A::DictionaryForm {            
        A::DictionaryForm::Three(format!("{}{}", stem, "us"), format!("{}{}", stem, "a"), format!("{}{}", stem, "um"))
    }

    /// Creates a regular Latin verb.
    /// 
    /// The dictionary form of a regular Latin verb consists of four principle
    /// parts: the present active indicative, the present active infinitive, 
    /// the perfect active indiciative, and the perfect passive participle. For
    /// example, the dictionary form of the Latin verb laudāre, to praise, is
    /// laudō, laudāre, laudāvī, laudātum. 
    /// 
    /// This function takes a present active indicative, present active 
    /// infinitive, perfect active indicative, and perfect passive participle 
    /// and returns a regular verb that can be conjugated. 
    /// 
    /// # Example
    /// ```
    /// use verba::verb as V;
    /// 
    /// let verb = V::Regular::new("laudō".to_string(), "laudāre".to_string(),"laudāvī".to_string(), "laudātum".to_string());
    /// ```
    pub fn new(present_active_indicative: String, present_active_infinitive: String, perfect_active_indicative: String, perfect_passive_participle: String) -> Result<Regular, RegularError> {
        // Make sure all arguments are in NFC form before continuing. 
        let present_active_indicative = U::normalize(present_active_indicative);
        let present_active_infinitive = U::normalize(present_active_infinitive);
        let perfect_active_indicative = U::normalize(perfect_active_indicative);
        let perfect_passive_participle = U::normalize(perfect_passive_participle);

        // Verify that the persent active indiciative has a valid ending.
        if !present_active_indicative.ends_with("ō") {
            return Err(RegularError::InvalidPresentActiveIndicativeEnding())
        }

        // Verify that the present active infinitive matches a conjugation 
        // group.
        if super::not_normalized_group(&present_active_infinitive).is_none() {
            return Err(RegularError::InvalidPresentActiveInfinitiveEnding())
        }

        // Verify that the perfect active indicative has a valid ending.
        if super::not_normalized_perfect_stem(&perfect_active_indicative).is_none() {
            return Err(RegularError::InvalidPerfectActiveIndicativeEnding())
        }

        // The perfect passive participle ending is verified by calling the
        // not_normalized_participle_stem function. 
        match super::not_normalized_participle_stem(&perfect_passive_participle) {
            Some(stem) => {
                match A::Regular::new(Regular::create_participle_dictionary(stem)) {
                    Ok(adjective) => {
                        Ok(Regular {
                            present_active_indicative,
                            present_active_infinitive,
                            perfect_active_indicative,
                            perfect_passive_participle: adjective,
                        })
                    },
                    Err(error) => Err(RegularError::FailedToCreateParticiple(error))
                }
            },
            None => Err(RegularError::InvalidPerfectPassiveParticipleEnding())
        }
    }

    // A convenience function that takes a tense and returns the appropriate 
    // stem based on the tense. 
    fn stem(&self, tense: Tense) -> Option<&str> {
        match tense {
            Tense::Perfect | Tense::Pluperfect | Tense::FuturePerfect => {
                super::not_normalized_perfect_stem(&self.perfect_active_indicative)
            },
            Tense::Present | Tense::Imperfect | Tense::Future => {
                super::not_normalized_present_stem(&self.present_active_infinitive)
            },
        }
    }

    /// Third conjugation verbs with a present active indicative form ending in
    /// -iō decline differently from other third conjugation verbs in certain 
    /// cases. This function returns true if a verb is a third conjugation -iō
    /// verb. 
    fn is_third_io(&self) -> bool {
        // This only applies to third conjugation verbs. 
        if let Some(Group::Third) = super::not_normalized_group(&self.present_active_infinitive) {
            self.present_active_indicative.ends_with("")
        } else {
            false
        }
        
    }

    /// This function takes a [`Person`], [`Number`], [`Tense`], [`Voice`] and
    /// [`Mood`]; retrieves the appropriate endings; and combines them with 
    /// the stem. 
    /// 
    /// Truth be told this function merely obtains the calling verb's group and
    /// calls [`endings_for_group`] with that group and that function persons
    /// the actual work. 
    fn stem_with_endings(&self, person: Person, number: Number, tense: Tense, voice: Voice, mood: Mood) -> Option<super::Conjugation> {
        let group = super::not_normalized_group(&self.present_active_infinitive)?;

        self.stem_with_endings_for_group(group, person, number, tense, voice, mood)
    }

    /// This function exists for the convenience of conjugating third 
    /// conjugation verbs with a persent active indicative ending in -iō. 
    /// Although they are third conjugation verbs, they often conjugate like
    /// fourth conjugation verbs. 
    /// 
    /// When I need to conjugate a third conjugation verb as a fourth 
    /// conjugation verb, I can call this function with Group::Fourth to make
    /// it happen. 
    /// 
    /// Beyond that specific situation, [`endings`] sould be called instead.
    fn stem_with_endings_for_group(&self, group: Group, person: Person, number: Number, tense: Tense, voice: Voice, mood: Mood) -> Option<super::Conjugation> {
        let stem = self.stem(tense)?;

        self.endings_for_group(stem, group, person, number, tense, voice, mood)
    }

    /// This function exists for the convenience of conjugating forms with
    /// irregular stems, such as the imperfect active subjunctive form, which
    /// users the perfect active infinitive form with the final 'e' replaced 
    /// with an 'ē'. 
    /// 
    /// When I need to conjugate imperfect active subjunctive forms, I can call
    /// this method and supply the irregular stem. 
    fn endings_for_group(&self, stem: &str, group: Group, person: Person, number: Number, tense: Tense, voice: Voice, mood: Mood) -> Option<super::Conjugation> {
        let suffixes = E::endings(group, person, number, tense, voice, mood)?;

        let mut combined = Vec::new();

        for fragments in suffixes {
            combined.push(super::conjugate_from_fragments(stem, &fragments));
        }


        Some(super::Conjugation::Complete(combined))
    }

    fn perfect_passive(&self, person: Person, number: Number, tense: Tense, mood: Mood) -> Option<super::Conjugation> {
        let sum = E::perfect_passive_sum(person, number, tense, mood)?;

        Some(super::Conjugation::PassivePerfect(&self.perfect_passive_participle, sum))
    }
}

impl super::Verb for Regular {
    /// Returns the present stem. 
    /// 
    /// # Example
    /// ```
    /// use verba::verb as V;
    /// use verba::verb::{Verb};
    /// 
    /// let verb = V::Regular::new("laudō".to_string(), "laudāre".to_string(),"laudāvī".to_string(), "laudātum".to_string());
    /// 
    /// if let Ok(v) = verb {
    ///     assert_eq!(v.present_stem(), Some("laud"));
    /// }
    fn present_stem(&self) -> Option<&str> {
        super::not_normalized_present_stem(&self.present_active_infinitive)
    }

    /// Returns the perfect stem. 
    /// 
    /// # Example
    /// ```
    /// use verba::verb as V;
    /// use verba::verb::{Verb};
    /// 
    /// let verb = V::Regular::new("laudō".to_string(), "laudāre".to_string(),"laudāvī".to_string(), "laudātum".to_string());
    /// 
    /// if let Ok(v) = verb {
    ///     assert_eq!(v.perfect_stem(), Some("laudāv"));
    /// }
    fn perfect_stem(&self) -> Option<&str> {
        super::not_normalized_perfect_stem(&self.perfect_active_indicative)
    }

    /// Provides a verb's conjugation group.
    /// 
    /// # Example
    /// ```
    /// use verba::verb as V;
    /// use verba::verb::{Verb};
    /// 
    /// let verb = V::Regular::new("laudō".to_string(), "laudāre".to_string(),"laudāvī".to_string(), "laudātum".to_string());
    /// 
    /// if let Ok(v) = verb {
    ///     assert_eq!(v.group(), Some(V::Group::First));
    /// }
    fn group(&self) -> Option<Group> {
        super::not_normalized_group(&self.present_active_infinitive)
    }

    fn conjugate(&self, person: Person, number: Number, tense: Tense, voice: Voice, mood: Mood) -> Option<super::Conjugation> {
        match (mood, voice, tense, person, number) {
            // Overrides for conjugated forms that cannot be automatically 
            // generated by the functionality available in 
            // crate::verb::endings.
            
            // Third conjugation verbs with a present active indicative ending
            // in -iō conjugate as fourth conjugation verbs in many instances.
            (Mood::Indicative, _, Tense::Present, Person::First, Number::Singular) if self.is_third_io() => self.stem_with_endings_for_group(Group::Fourth, person, number, tense, voice, mood),
            (Mood::Indicative, _, Tense::Present, Person::Third, Number::Plural) if self.is_third_io() => self.stem_with_endings_for_group(Group::Fourth, person, number, tense, voice, mood),
            (Mood::Indicative, _, Tense::Imperfect, _, _) if self.is_third_io() => self.stem_with_endings_for_group(Group::Fourth, person, number, tense, voice, mood),
            (Mood::Indicative, _, Tense::Future, _, _) if self.is_third_io() => self.stem_with_endings_for_group(Group::Fourth, person, number, tense, voice, mood),
            (Mood::Subjunctive, _, Tense::Present, _, _) if self.is_third_io() => self.stem_with_endings_for_group(Group::Fourth, person, number, tense, voice, mood),

            (Mood::Indicative, Voice::Passive, Tense::Perfect, _, _) |
            (Mood::Indicative, Voice::Passive, Tense::Pluperfect, _, _) |
            (Mood::Indicative, Voice::Passive, Tense::FuturePerfect, _, _) => self.perfect_passive(person, number, tense, mood),
            
            (Mood::Subjunctive, Voice::Passive, Tense::Perfect, _, _) |
            (Mood::Subjunctive, Voice::Passive, Tense::Pluperfect, _, _) =>self.perfect_passive(person, number, tense, mood),

            (Mood::Subjunctive, _, Tense::Imperfect, _, _) => self.endings_for_group(self.present_active_infinitive.trim_end_matches('e'), super::not_normalized_group(&self.present_active_infinitive)?, person, number, tense, voice, mood),

            _ => self.stem_with_endings(person, number, tense, voice, mood),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    use unicode_normalization::{is_nfc};
    use crate::verb::{Verb};

    use crate::adjective::{Adjective};
    use crate::adjective as A;

    /// Verifies that a Regular's nom and gen values are in NFC form.
    fn verify_normalization(verb: &Regular) {
        assert!(is_nfc(&verb.present_active_indicative), "The present active indicative form is not stored in NFC form.");
        assert!(is_nfc(&verb.present_active_infinitive), "The present active infinitive form is not stored in NFC form.");
        assert!(is_nfc(&verb.perfect_active_indicative), "The perfect active indicative form is not stored in NFC form.");
        // There is no need to verify the perfect passive participle since 
        // crate::adjective::Regular::new ensures that values output by 
        // Regular's decline function are in NFC form. There is also a unit
        // test that verifies this. 
    }

    /// Verifies the internal values of a struct are correct.
    fn verify_struct(verb: &Regular, present_active_indicative: &str, present_active_infinitive: &str, perfect_active_indicative: &str, perfect_passive_participle: &str) {
        assert_eq!(verb.present_active_indicative, present_active_indicative.to_string(), "The stored present active indicative is incorrect.");
        assert_eq!(verb.present_active_infinitive, present_active_infinitive.to_string(), "The stored present active infinitive is incorrect.");
        assert_eq!(verb.perfect_active_indicative, perfect_active_indicative.to_string(), "The stored perfect passive indicative is incorrect.");
        // Ensure that calling verb.perfect_passive_participle.decline for a
        // singular nominative neuter form, which is the same as what should be
        // provided to Regular::new, is correct. 
        match verb.perfect_passive_participle.decline(Number::Singular, A::Case::Nominative, A::Gender::Neuter) {
            Some(declension) => {
                match declension.first() {
                    Some(first) => assert_eq!(format!("{}", first), perfect_passive_participle.to_string(), "The stored perfect passive participle is incorrect."),
                    None => panic!("The declension for the singular nominative neuter form of the perfect passive participle contained no elements."),
                }
            },
            None => panic!("Received a None value when attempting to decline the singular nominative neuter form of the perfect passive participle."),
            
        }
    }

    fn verify_stem(verb: &Regular, tense: Tense, expected: Option<&str>) {
        let verb_stem = verb.stem(tense);
        assert_eq!(verb_stem, expected, "verb.stem({:?}) returned {:?} but {:?} was expected.", tense, verb_stem, expected);
    }

    fn verify_regular(present_active_indicative: &str, present_active_infinitive: &str, perfect_active_indicative: &str, perfect_passive_participle: &str, present_stem: Option<&str>, perfect_stem: Option<&str>, group: Group) {
        match Regular::new(present_active_indicative.to_string(), present_active_infinitive.to_string(), perfect_active_indicative.to_string(), perfect_passive_participle.to_string()) {
            Ok(verb) => {
                verify_normalization(&verb);
                verify_struct(&verb, present_active_indicative, present_active_infinitive, perfect_active_indicative, perfect_passive_participle);

                let verb_group = verb.group();
                assert_eq!(verb_group, Some(group), "verb.group() returned {:?} but {:?} was expected.", verb_group, group);

                let verb_present_stem = verb.present_stem();
                assert_eq!(verb_present_stem, present_stem, "verb.present_stem() returned {:?} but {:?} was expected.", verb_present_stem, present_stem);
                
                let verb_perfect_stem = verb.perfect_stem();
                assert_eq!(verb_perfect_stem, perfect_stem, "verb.perfect_stem() returned {:?} but {:?} was expected.", verb_perfect_stem, perfect_stem);

                verify_stem(&verb, Tense::Present, present_stem);
                verify_stem(&verb, Tense::Imperfect, present_stem);
                verify_stem(&verb, Tense::Future, present_stem);
                verify_stem(&verb, Tense::Perfect, perfect_stem);
                verify_stem(&verb, Tense::Pluperfect, perfect_stem);
                verify_stem(&verb, Tense::FuturePerfect, perfect_stem);
            },
            Err(error) => panic!("Failed to create regular verb {}, {}, {}, {}. Received the following error: {}", present_active_indicative, present_active_infinitive, perfect_active_indicative, perfect_passive_participle, error),
        }
    }

    /// Test the creation of first conjugation verbs and verify the inner
    /// inner workings of the struct.  
    #[test]
    fn test_new_first() {
        verify_regular("laudō", "laudāre", "laudāvī", "laudātum", Some("laud"), Some("laudāv"), Group::First)
    }

    /// Test the creation of second conjugation verbs and verify the inner
    /// workings of the struct.  
    #[test]
    fn test_new_second() {
        verify_regular("moneō", "monēre", "monuī", "monitum", Some("mon"), Some("monu"), Group::Second)
    }

    /// Test the creation of third conjugation verbs and verify the inner
    /// workings of the struct.  
    #[test]
    fn test_new_third() {
        verify_regular("agō", "agere", "ēgī", "āctum", Some("ag"), Some("ēg"), Group::Third)
    }

    /// Test the creation of third conjugation io verbs and verify the inner
    /// workings of the struct.  
    #[test]
    fn test_new_third_io() {
        verify_regular("capiō", "capere", "cēpī", "captum", Some("cap"), Some("cēp"), Group::Third)
    }

    /// Test the creation of fourth conjugation io verbs and verify the inner
    /// workings of the struct.  
    #[test]
    fn test_new_fourth() {
        verify_regular("audiō", "audīre", "audīvī", "audītum", Some("aud"), Some("audīv"), Group::Fourth)
    }

    #[test]
    fn test_is_third_io() {
        let present_active_indicative = "capiō";
        let present_active_infinitive = "capere";
        let perfect_active_indicative = "cēpī";
        let perfect_passive_participle = "captum";

        match Regular::new(present_active_indicative.to_string(), present_active_infinitive.to_string(), perfect_active_indicative.to_string(), perfect_passive_participle.to_string()) {
            Ok(verb) => {
                assert!(verb.is_third_io(), "is_third_io should return true for capiō.");
            },
            Err(error) => panic!("Failed to create regular verb {}, {}, {}, {}. Received the following error: {}", present_active_indicative, present_active_infinitive, perfect_active_indicative, perfect_passive_participle, error),
        }
    }
}