use alloc::{string::String, vec::Vec};
use serde::{Deserialize, Serialize};
use svara::phoneme::Phoneme;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MorphemeKind {
Prefix,
Root,
Suffix,
Infix,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Morpheme {
text: String,
kind: MorphemeKind,
phonemes: Vec<Phoneme>,
}
impl Morpheme {
#[must_use]
pub fn new(text: &str, kind: MorphemeKind, phonemes: Vec<Phoneme>) -> Self {
Self {
text: alloc::string::ToString::to_string(text),
kind,
phonemes,
}
}
#[must_use]
pub fn text(&self) -> &str {
&self.text
}
#[must_use]
pub fn kind(&self) -> MorphemeKind {
self.kind
}
#[must_use]
pub fn phonemes(&self) -> &[Phoneme] {
&self.phonemes
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Decomposition {
morphemes: Vec<Morpheme>,
}
impl Decomposition {
#[must_use]
pub fn new(morphemes: Vec<Morpheme>) -> Self {
Self { morphemes }
}
#[must_use]
pub fn morphemes(&self) -> &[Morpheme] {
&self.morphemes
}
#[must_use]
pub fn composite_phonemes(&self) -> Vec<Phoneme> {
self.morphemes
.iter()
.flat_map(|m| m.phonemes.iter().copied())
.collect()
}
#[must_use]
pub fn text(&self) -> String {
self.morphemes.iter().map(|m| m.text.as_str()).collect()
}
#[must_use]
pub fn len(&self) -> usize {
self.morphemes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.morphemes.is_empty()
}
#[must_use]
pub fn root(&self) -> Option<&Morpheme> {
self.morphemes.iter().find(|m| m.kind == MorphemeKind::Root)
}
#[must_use]
pub fn prefixes(&self) -> Vec<&Morpheme> {
self.morphemes
.iter()
.filter(|m| m.kind == MorphemeKind::Prefix)
.collect()
}
#[must_use]
pub fn suffixes(&self) -> Vec<&Morpheme> {
self.morphemes
.iter()
.filter(|m| m.kind == MorphemeKind::Suffix)
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn example_decomposition() -> Decomposition {
Decomposition::new(alloc::vec![
Morpheme::new(
"un",
MorphemeKind::Prefix,
alloc::vec![Phoneme::VowelCupV, Phoneme::NasalN],
),
Morpheme::new(
"happi",
MorphemeKind::Root,
alloc::vec![
Phoneme::FricativeH,
Phoneme::VowelAsh,
Phoneme::PlosiveP,
Phoneme::VowelE,
],
),
Morpheme::new(
"ness",
MorphemeKind::Suffix,
alloc::vec![Phoneme::NasalN, Phoneme::VowelOpenE, Phoneme::FricativeS],
),
])
}
#[test]
fn test_morpheme_new() {
let m = Morpheme::new("un", MorphemeKind::Prefix, alloc::vec![Phoneme::VowelCupV]);
assert_eq!(m.text(), "un");
assert_eq!(m.kind(), MorphemeKind::Prefix);
assert_eq!(m.phonemes(), &[Phoneme::VowelCupV]);
}
#[test]
fn test_decomposition_composite() {
let d = example_decomposition();
let composite = d.composite_phonemes();
assert_eq!(composite.len(), 9); }
#[test]
fn test_decomposition_text() {
let d = example_decomposition();
assert_eq!(d.text(), "unhappiness");
}
#[test]
fn test_decomposition_root() {
let d = example_decomposition();
let root = d.root().unwrap();
assert_eq!(root.text(), "happi");
assert_eq!(root.kind(), MorphemeKind::Root);
}
#[test]
fn test_decomposition_prefixes_suffixes() {
let d = example_decomposition();
assert_eq!(d.prefixes().len(), 1);
assert_eq!(d.suffixes().len(), 1);
assert_eq!(d.prefixes()[0].text(), "un");
assert_eq!(d.suffixes()[0].text(), "ness");
}
#[test]
fn test_decomposition_len() {
let d = example_decomposition();
assert_eq!(d.len(), 3);
assert!(!d.is_empty());
}
#[test]
fn test_empty_decomposition() {
let d = Decomposition::new(alloc::vec![]);
assert!(d.is_empty());
assert!(d.root().is_none());
assert!(d.composite_phonemes().is_empty());
}
#[test]
fn test_morpheme_serde_roundtrip() {
let m = Morpheme::new(
"un",
MorphemeKind::Prefix,
alloc::vec![Phoneme::VowelCupV, Phoneme::NasalN],
);
let json = serde_json::to_string(&m).unwrap();
let m2: Morpheme = serde_json::from_str(&json).unwrap();
assert_eq!(m, m2);
}
#[test]
fn test_decomposition_serde_roundtrip() {
let d = example_decomposition();
let json = serde_json::to_string(&d).unwrap();
let d2: Decomposition = serde_json::from_str(&json).unwrap();
assert_eq!(d, d2);
}
#[test]
fn test_morpheme_kind_serde_roundtrip() {
for kind in [
MorphemeKind::Prefix,
MorphemeKind::Root,
MorphemeKind::Suffix,
MorphemeKind::Infix,
] {
let json = serde_json::to_string(&kind).unwrap();
let kind2: MorphemeKind = serde_json::from_str(&json).unwrap();
assert_eq!(kind, kind2);
}
}
}