use crate::language::Word;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt::Debug;
#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)]
pub struct Adjective(AdjectiveData);
#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)]
enum AdjectiveData {
Regular {
base: String,
},
Irregular {
base: String,
comparative: String,
superlative: String,
},
Absolute {
base: String,
},
}
impl Adjective {
pub fn new_regular<S: Into<String>>(base: S) -> Self {
Adjective(AdjectiveData::Regular { base: base.into() })
}
pub fn new_irregular<S: Into<String>>(base: S, comparative: S, superlative: S) -> Self {
Adjective(AdjectiveData::Irregular {
base: base.into(),
comparative: comparative.into(),
superlative: superlative.into(),
})
}
pub fn new_absolute<S: Into<String>>(base: S) -> Self {
Adjective(AdjectiveData::Absolute { base: base.into() })
}
pub fn is_absolute(&self) -> bool {
matches!(&self.0, AdjectiveData::Absolute { .. })
}
pub fn comparative<'a>(&'a self) -> Cow<'a, str> {
fn is_vowel(c: char) -> bool {
matches!(c, 'a' | 'e' | 'i' | 'o' | 'u' | 'h')
}
match &self.0 {
AdjectiveData::Regular { base } => {
if base.ends_with('e') {
return format!("{}r", base).into();
}
if let Some(stem) = base.strip_suffix('y') {
if let Some(before_y) = stem.chars().last() {
if !is_vowel(before_y) {
return format!("{}ier", stem).into();
}
}
}
if crate::language::utils::ends_cvc(base) {
if let Some(last) = base.chars().last() {
return format!("{}{}er", base, last).into();
}
}
format!("{}er", base).into()
}
AdjectiveData::Irregular { comparative, .. } => comparative.as_str().into(),
AdjectiveData::Absolute { base } => base.as_str().into(),
}
}
pub fn superlative<'a>(&'a self) -> Cow<'a, str> {
match &self.0 {
AdjectiveData::Regular { .. } => {
let comparative = self.comparative();
let stem = comparative.strip_suffix("er").unwrap_or(&comparative);
format!("{}est", stem).into()
}
AdjectiveData::Irregular { superlative, .. } => superlative.as_str().into(),
AdjectiveData::Absolute { base } => base.as_str().into(),
}
}
}
impl AsRef<str> for Adjective {
fn as_ref(&self) -> &str {
match &self.0 {
AdjectiveData::Regular { base } => base,
AdjectiveData::Irregular { base, .. } => base,
AdjectiveData::Absolute { base } => base,
}
}
}
impl std::fmt::Display for Adjective {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.0 {
AdjectiveData::Regular { base } => write!(f, "{}", base),
AdjectiveData::Irregular { base, .. } => write!(f, "{}", base),
AdjectiveData::Absolute { base } => write!(f, "{}", base),
}
}
}
impl Word for Adjective {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn adjective_test() {
let regular_adj = Adjective::new_regular("quick");
assert_eq!(regular_adj.comparative(), "quicker");
assert_eq!(regular_adj.superlative(), "quickest");
let large_adj = Adjective::new_regular("large");
assert_eq!(large_adj.comparative(), "larger");
assert_eq!(large_adj.superlative(), "largest");
let happy_adj = Adjective::new_regular("happy");
assert_eq!(happy_adj.comparative(), "happier");
assert_eq!(happy_adj.superlative(), "happiest");
let big_adj = Adjective::new_regular("big");
assert_eq!(big_adj.comparative(), "bigger");
assert_eq!(big_adj.superlative(), "biggest");
let irregular_adj = Adjective::new_irregular("good", "better", "best");
assert_eq!(irregular_adj.comparative(), "better");
assert_eq!(irregular_adj.superlative(), "best");
}
#[test]
fn adjective_edge_case_test() {
let grey_adj = Adjective::new_regular("grey");
assert_eq!(grey_adj.comparative(), "greyer");
assert_eq!(grey_adj.superlative(), "greyest");
let shy_adj = Adjective::new_regular("shy");
assert_eq!(shy_adj.comparative(), "shyer");
assert_eq!(shy_adj.superlative(), "shyest");
let slow_adj = Adjective::new_regular("slow");
assert_eq!(slow_adj.comparative(), "slower");
assert_eq!(slow_adj.superlative(), "slowest");
let thin_adj = Adjective::new_regular("thin");
assert_eq!(thin_adj.comparative(), "thinner");
assert_eq!(thin_adj.superlative(), "thinnest");
}
}