use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt::{Debug, Display};
use super::Word;
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Verb(VerbData);
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename = "Verb")] enum VerbData {
Regular {
infinitive: String,
},
Irregular {
infinitive: String,
present_singular: String,
past: String,
past_participle: String,
present_participle: String,
},
}
impl Verb {
pub fn new_irregular<S: Into<String>>(
infinitive: S,
present_singular: S,
past: S,
past_participle: S,
present_participle: S,
) -> Self {
let kind = VerbData::Irregular {
infinitive: infinitive.into(),
present_singular: present_singular.into(),
past: past.into(),
past_participle: past_participle.into(),
present_participle: present_participle.into(),
};
Self(kind)
}
pub fn new_regular<S: Into<String>>(infinitive: S) -> Self {
let kind = VerbData::Regular {
infinitive: infinitive.into(),
};
Self(kind)
}
pub fn present_singular<'a>(&'a self) -> Cow<'a, str> {
fn is_vowel(c: char) -> bool {
matches!(c, 'a' | 'e' | 'i' | 'o' | 'u')
}
match &self.0 {
VerbData::Regular { infinitive } => {
if infinitive.ends_with('y') {
let mut chars = infinitive.chars();
chars.next_back(); if let Some(before_y) = chars.next_back() {
if !is_vowel(before_y) {
return format!("{}ies", &infinitive[..infinitive.len() - 1]).into();
}
}
} else if infinitive.ends_with('s')
|| infinitive.ends_with("sh")
|| infinitive.ends_with("ch")
|| infinitive.ends_with('x')
|| infinitive.ends_with('z')
|| infinitive.ends_with('o')
{
return format!("{}es", infinitive).into();
}
format!("{}s", infinitive).into()
}
VerbData::Irregular {
present_singular, ..
} => present_singular.as_str().into(),
}
}
pub fn past<'a>(&'a self) -> Cow<'a, str> {
fn is_vowel(c: char) -> bool {
matches!(c, 'a' | 'e' | 'i' | 'o' | 'u')
}
match &self.0 {
VerbData::Regular { infinitive } => {
if infinitive.ends_with('e') {
return format!("{}d", infinitive).into();
}
if infinitive.ends_with('y') {
let mut chars = infinitive.chars();
chars.next_back(); if let Some(before_y) = chars.next_back() {
if !is_vowel(before_y) {
return format!("{}ied", &infinitive[..infinitive.len() - 1]).into();
}
}
}
if !infinitive.ends_with("en") && crate::language::utils::ends_cvc(infinitive) {
if let Some(last) = infinitive.chars().last() {
return format!("{}{}ed", infinitive, last).into();
}
}
format!("{}ed", infinitive).into()
}
VerbData::Irregular { past, .. } => past.as_str().into(),
}
}
pub fn past_participle<'a>(&'a self) -> Cow<'a, str> {
match &self.0 {
VerbData::Regular { .. } => self.past(),
VerbData::Irregular {
past_participle, ..
} => past_participle.as_str().into(),
}
}
pub fn present_participle<'a>(&'a self) -> Cow<'a, str> {
match &self.0 {
VerbData::Regular { infinitive } => {
if infinitive == "be" {
return "being".into();
}
if infinitive.ends_with("ie") {
return format!("{}ying", &infinitive[..infinitive.len() - 2]).into();
}
if infinitive.ends_with('e') {
return format!("{}ing", &infinitive[..infinitive.len() - 1]).into();
}
if !infinitive.ends_with("en") && crate::language::utils::ends_cvc(infinitive) {
if let Some(last) = infinitive.chars().last() {
return format!("{}{}ing", infinitive, last).into();
}
}
format!("{}ing", infinitive).into()
}
VerbData::Irregular {
present_participle, ..
} => present_participle.as_str().into(),
}
}
}
impl AsRef<str> for Verb {
fn as_ref(&self) -> &str {
match &self.0 {
VerbData::Regular { infinitive } => infinitive,
VerbData::Irregular { infinitive, .. } => infinitive,
}
}
}
impl Display for Verb {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self.0 {
VerbData::Regular { infinitive } => write!(f, "{}", infinitive),
VerbData::Irregular { infinitive, .. } => write!(f, "{}", infinitive),
}
}
}
impl Debug for Verb {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self.0 {
VerbData::Regular { infinitive } => f
.debug_struct("Regular")
.field("infinitive", infinitive)
.finish(),
VerbData::Irregular {
infinitive,
present_singular,
past,
past_participle,
present_participle,
} => f
.debug_struct("Irregular")
.field("infinitive", infinitive)
.field("present_singular", present_singular)
.field("past", past)
.field("past_participle", past_participle)
.field("present_participle", present_participle)
.finish(),
}
}
}
impl Word for Verb {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verb_test() {
let regular_verb = Verb::new_regular("play");
assert_eq!(regular_verb.as_ref(), "play");
assert_eq!(regular_verb.present_singular(), "plays");
assert_eq!(regular_verb.past(), "played");
let irregular_verb = Verb::new_irregular("eat", "eats", "ate", "eaten", "eating");
assert_eq!(irregular_verb.as_ref(), "eat");
assert_eq!(irregular_verb.past_participle(), "eaten");
let study = Verb::new_regular("study");
assert_eq!(study.present_singular(), "studies");
assert_eq!(study.past(), "studied");
assert_eq!(Verb::new_regular("pass").present_singular(), "passes");
assert_eq!(Verb::new_regular("watch").present_singular(), "watches");
assert_eq!(Verb::new_regular("fix").present_singular(), "fixes");
let stop = Verb::new_regular("stop");
assert_eq!(stop.past(), "stopped");
assert_eq!(stop.present_participle(), "stopping");
assert_eq!(Verb::new_regular("bake").past(), "baked");
assert_eq!(Verb::new_regular("bake").present_participle(), "baking");
}
#[test]
fn verb_edge_case_test() {
let die = Verb::new_regular("die");
assert_eq!(die.present_participle(), "dying");
let lie = Verb::new_regular("lie");
assert_eq!(lie.present_participle(), "lying");
let echo = Verb::new_regular("echo");
assert_eq!(echo.present_singular(), "echoes");
let open = Verb::new_regular("open");
assert_eq!(open.past(), "opened");
assert_eq!(open.present_participle(), "opening");
let buzz = Verb::new_regular("buzz");
assert_eq!(buzz.present_singular(), "buzzes");
assert_eq!(buzz.past(), "buzzed"); assert_eq!(buzz.present_participle(), "buzzing");
}
}