use std::borrow::Cow;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct WeightType(Cow<'static, str>);
impl WeightType {
pub const TROPICAL: Self = Self(Cow::Borrowed("tropical"));
pub const LOG: Self = Self(Cow::Borrowed("log"));
pub const REAL: Self = Self(Cow::Borrowed("real"));
pub const MINMAX: Self = Self(Cow::Borrowed("minmax"));
#[inline]
pub const fn new(name: &'static str) -> Self {
Self(Cow::Borrowed(name))
}
#[inline]
pub fn new_dynamic(name: String) -> Self {
Self(Cow::Owned(name))
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for WeightType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ArcType(Cow<'static, str>);
impl ArcType {
pub const STANDARD: Self = Self(Cow::Borrowed("standard"));
pub const LOG: Self = Self(Cow::Borrowed("log"));
#[inline]
pub fn new_static(name: &'static str) -> Self {
Self(Cow::Borrowed(name))
}
#[inline]
pub fn new_dynamic(name: String) -> Self {
Self(Cow::Owned(name))
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl ArcType {
pub const ALL: &'static [Self] = &[Self::STANDARD, Self::LOG];
pub fn from_name(name: &str) -> Self {
Self::ALL
.iter()
.find(|candidate| candidate.as_str() == name)
.cloned()
.unwrap_or_else(|| Self::new_dynamic(name.to_string()))
}
}
impl fmt::Display for ArcType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FstType(&'static str);
macro_rules! define_fst_types {
( single { $($name:ident => $str:literal),* $(,)? } ) => {
$(
pub const $name: Self = Self($str);
)*
};
( sized { $($name:ident => $str:literal),* $(,)? } ) => {
pastey::paste! {
$(
pub const [<$name _32>]: Self = Self($str);
pub const [<$name _64>]: Self = Self(concat!($str, "64"));
)*
}
};
( compact { $($name:ident => $str:literal),* $(,)? } ) => {
pastey::paste! {
$(
pub const [<COMPACT_ $name _32>]: Self = Self(concat!("compact_", $str));
pub const [<COMPACT_ $name _64>]: Self = Self(concat!("compact64_", $str));
)*
}
};
}
impl FstType {
define_fst_types! {
single {
VECTOR => "vector",
ARC_MAP => "arc_map",
COMPLEMENT => "complement",
COMPOSE => "compose",
EDIT => "edit",
MERGE => "merge",
EXPANDER => "expander",
ARC_LOOKAHEAD => "arc_lookahead",
ILABEL_LOOKAHEAD => "ilabel_lookahead",
OLABEL_LOOKAHEAD => "olabel_lookahead",
}
}
define_fst_types! {
sized {
CONST => "const",
}
}
define_fst_types! {
compact {
STRING => "string",
WEIGHTED_STRING => "weighted_string",
ACCEPTOR => "acceptor",
UNWEIGHTED => "unweighted",
UNWEIGHTED_ACCEPTOR => "unweighted_acceptor",
}
}
#[inline]
pub const fn new(name: &'static str) -> Self {
Self(name)
}
#[inline]
pub const fn as_str(&self) -> &'static str {
self.0
}
}
impl FstType {
pub const ALL: &'static [Self] = &[
Self::VECTOR,
Self::EDIT,
Self::ARC_MAP,
Self::COMPLEMENT,
Self::COMPOSE,
Self::EXPANDER,
Self::ARC_LOOKAHEAD,
Self::ILABEL_LOOKAHEAD,
Self::OLABEL_LOOKAHEAD,
Self::CONST_32,
Self::CONST_64,
Self::COMPACT_STRING_32,
Self::COMPACT_STRING_64,
Self::COMPACT_WEIGHTED_STRING_32,
Self::COMPACT_WEIGHTED_STRING_64,
Self::COMPACT_ACCEPTOR_32,
Self::COMPACT_ACCEPTOR_64,
Self::COMPACT_UNWEIGHTED_32,
Self::COMPACT_UNWEIGHTED_64,
Self::COMPACT_UNWEIGHTED_ACCEPTOR_32,
Self::COMPACT_UNWEIGHTED_ACCEPTOR_64,
];
pub fn from_name(name: &str) -> Option<Self> {
Self::ALL
.iter()
.find(|candidate| candidate.as_str() == name)
.cloned()
}
}
impl fmt::Display for FstType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fst_type_names_round_trip() {
for expected in FstType::ALL {
assert_eq!(
FstType::from_name(expected.as_str()).as_ref(),
Some(expected),
"{expected} did not round trip"
);
}
}
#[test]
fn fst_type_names_are_distinct() {
for (i, left) in FstType::ALL.iter().enumerate() {
for right in &FstType::ALL[i + 1..] {
assert_ne!(left.as_str(), right.as_str(), "duplicate name {left}");
}
}
}
#[test]
fn an_unknown_fst_type_is_rejected() {
assert_eq!(FstType::from_name("not-an-fst"), None);
assert_eq!(FstType::from_name(""), None);
assert_eq!(FstType::from_name("Vector"), None);
}
#[test]
fn size_suffixes_follow_the_upstream_spelling() {
assert_eq!(FstType::CONST_32.as_str(), "const");
assert_eq!(FstType::CONST_64.as_str(), "const64");
assert_eq!(FstType::COMPACT_STRING_32.as_str(), "compact_string");
assert_eq!(FstType::COMPACT_STRING_64.as_str(), "compact64_string");
assert_eq!(
FstType::COMPACT_UNWEIGHTED_ACCEPTOR_64.as_str(),
"compact64_unweighted_acceptor"
);
}
#[test]
fn arc_type_names_round_trip_and_unknown_ones_are_kept() {
for expected in ArcType::ALL {
assert_eq!(&ArcType::from_name(expected.as_str()), expected);
}
let dynamic = ArcType::from_name("tropical64");
assert_eq!(dynamic.as_str(), "tropical64");
}
}