use std::fmt;
use crate::affix::PartOfSpeech;
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum MorphInfo {
Stem(MorphStr),
Phonetic(MorphStr),
Allomorph(MorphStr),
Part(PartOfSpeech),
DerivSfx(MorphStr),
InflecSfx(MorphStr),
TerminalSfx(MorphStr),
DerivPfx(MorphStr),
InflecPfx(MorphStr),
TermPfx(MorphStr),
SurfacePfx(MorphStr),
CompPart(MorphStr),
Other(MorphStr),
}
impl MorphInfo {
#[inline]
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn many_from_str(s: &str) -> impl Iterator<Item = Self> + '_ {
s.split_whitespace().map(MorphInfo::from)
}
}
impl From<&str> for MorphInfo {
#[inline]
fn from(value: &str) -> Self {
let Some((tag, val)) = value.split_once(':') else {
return Self::Other(value.into());
};
match tag {
"st" => Self::Stem(val.into()),
"ph" => Self::Phonetic(val.into()),
"al" => Self::Allomorph(val.into()),
"po" => Self::Part(val.into()),
"ds" => Self::DerivSfx(val.into()),
"is" => Self::InflecSfx(val.into()),
"ts" => Self::TerminalSfx(val.into()),
"dp" => Self::DerivPfx(val.into()),
"ip" => Self::InflecPfx(val.into()),
"tp" => Self::TermPfx(val.into()),
"sp" => Self::SurfacePfx(val.into()),
"pa" => Self::CompPart(val.into()),
_ => Self::Other(value.into()),
}
}
}
impl fmt::Display for MorphInfo {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MorphInfo::Stem(v) => write!(f, "st:{v}"),
MorphInfo::Phonetic(v) => write!(f, "ph:{v}"),
MorphInfo::Allomorph(v) => write!(f, "al:{v}"),
MorphInfo::Part(v) => write!(f, "po:{v}"),
MorphInfo::DerivSfx(v) => write!(f, "ds:{v}"),
MorphInfo::InflecSfx(v) => write!(f, "is:{v}"),
MorphInfo::TerminalSfx(v) => write!(f, "ts:{v}"),
MorphInfo::DerivPfx(v) => write!(f, "dp:{v}"),
MorphInfo::InflecPfx(v) => write!(f, "ip:{v}"),
MorphInfo::TermPfx(v) => write!(f, "tp:{v}"),
MorphInfo::SurfacePfx(v) => write!(f, "sp:{v}"),
MorphInfo::CompPart(v) => write!(f, "pa:{v}"),
MorphInfo::Other(v) => write!(f, "{v}"),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct MorphStr(Box<str>);
impl AsRef<str> for MorphStr {
#[inline]
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl From<&str> for MorphStr {
#[inline]
fn from(value: &str) -> Self {
Self(value.into())
}
}
impl fmt::Display for MorphStr {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Debug for MorphStr {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn morph_single_ok() {
let tests = [
("st:stem", MorphInfo::Stem("stem".into())),
("ip:abc", MorphInfo::InflecPfx("abc".into())),
("pa:xyz", MorphInfo::CompPart("xyz".into())),
("foo:xyz", MorphInfo::Other("foo:xyz".into())),
];
for (input, expected) in tests {
assert_eq!(MorphInfo::from(input), expected, "failure parsing {input}");
}
}
#[test]
fn morph_string_ok() {
let input = "st:stem ip:abcd pa:xyz st:some-stem\tal:def";
let output = MorphInfo::many_from_str(input);
let expected = [
MorphInfo::Stem("stem".into()),
MorphInfo::InflecPfx("abcd".into()),
MorphInfo::CompPart("xyz".into()),
MorphInfo::Stem("some-stem".into()),
MorphInfo::Allomorph("def".into()),
];
assert_eq!(&output.collect::<Vec<_>>(), &expected);
}
}