use std::collections::HashMap;
use std::convert::From;
use std::default::Default;
use std::fmt::{self, Display};
use std::str::FromStr;
use strum::{Display, EnumString, IntoStaticStr, VariantNames};
use crate::utils::get_with_fallback;
#[derive(
Clone,
Copy,
Debug,
Default,
PartialEq,
Eq,
Hash,
Display,
EnumString,
VariantNames,
IntoStaticStr,
)]
#[strum(serialize_all = "kebab_case", ascii_case_insensitive)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "kebab-case")
)]
pub enum Variant {
#[default]
Zh,
#[cfg_attr(feature = "serde", serde(alias = "zh-Hant"))]
ZhHant,
#[cfg_attr(feature = "serde", serde(alias = "zh-Hans"))]
ZhHans,
#[cfg_attr(feature = "serde", serde(rename = "zh-tw", alias = "zh-TW"))]
ZhTW,
#[cfg_attr(feature = "serde", serde(rename = "zh-hk", alias = "zh-HK"))]
ZhHK,
#[cfg_attr(feature = "serde", serde(rename = "zh-mo", alias = "zh-MO"))]
ZhMO,
#[cfg_attr(feature = "serde", serde(rename = "zh-my", alias = "zh-MY"))]
ZhMY,
#[cfg_attr(feature = "serde", serde(rename = "zh-sg", alias = "zh-SG"))]
ZhSG,
#[cfg_attr(feature = "serde", serde(rename = "zh-cn", alias = "zh-CN"))]
ZhCN,
}
impl Variant {
#[inline(always)]
pub fn get_name(self) -> &'static str {
use Variant::*;
match self {
Zh => "原文", ZhHant => "繁体",
ZhHans => "简体",
ZhTW => "臺灣",
ZhHK => "香港",
ZhMO => "澳門",
ZhMY => "大马",
ZhSG => "新加坡",
ZhCN => "大陆", }
}
}
#[derive(Debug, Clone)]
pub struct VariantMap<T>(pub HashMap<Variant, T>);
impl VariantMap<String> {
#[inline(always)]
pub fn get_text(&self, target: Variant) -> Option<&str> {
self.0.get(&target).map(String::as_str)
}
pub fn get_text_with_fallback(&self, target: Variant) -> Option<&str> {
use Variant::*;
match_fallback!(
self.0,
target,
Zh -> [ZhHans, ZhHant, ZhCN, ZhTW, ZhHK, ZhSG, ZhMO, ZhMY],
ZhHans -> [ ZhCN, ZhSG, ZhMY ],
ZhHant -> [ ZhTW, ZhHK, ZhMO ],
ZhCN -> [ ZhHans, ZhSG, ZhMY ],
ZhSG -> [ ZhHans, ZhCN, ZhMY ],
ZhMY -> [ ZhHans, ZhSG, ZhCN ],
ZhTW -> [ ZhHant, ZhHK, ZhMO ],
ZhHK -> [ ZhHant, ZhMO, ZhTW ],
ZhMO -> [ ZhHant, ZhHK, ZhTW ],
)
}
pub fn get_conv_pairs(&self, target: Variant) -> impl Iterator<Item = (&str, &str)> {
use Variant::*;
let mut it = None;
match target {
Zh | ZhHant | ZhHans => (),
_ => {
let to = match_fallback!(
self.0,
target,
ZhCN -> [ ZhHans, ZhSG, ZhMY ],
ZhSG -> [ ZhHans, ZhCN, ZhMY ],
ZhMY -> [ ZhHans, ZhSG, ZhCN ],
ZhTW -> [ ZhHant, ZhHK, ZhMO ],
ZhHK -> [ ZhHant, ZhMO, ZhTW ],
ZhMO -> [ ZhHant, ZhHK, ZhTW ],
);
if let Some(to) = to {
it = Some(self.0.iter().filter_map(move |(_variant, from)| {
if from.is_empty() {
None
} else {
Some((from.as_ref(), to))
}
}));
}
}
}
it.into_iter().flatten()
}
}
impl VariantMap<Vec<(String, String)>> {
pub fn get_conv_pairs(&self, target: Variant) -> &[(String, String)] {
self.0.get(&target).map(|p| p.as_slice()).unwrap_or(&[])
}
}
impl<T> VariantMap<T> {
pub fn into_inner(self) -> HashMap<Variant, T> {
self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty() }
}
impl FromStr for VariantMap<String> {
type Err = ();
#[allow(clippy::needless_as_bytes)]
fn from_str(s: &str) -> Result<VariantMap<String>, Self::Err> {
let s = s.trim();
let mut map = HashMap::new();
let mut parse_single = |s: &str| -> Result<(), Self::Err> {
let (v, t) = s.split_at(s.find(':').ok_or(())?);
let t = &t[1..]; map.insert(
Variant::from_str(v.trim()).map_err(|_| ())?,
t.trim().to_owned(),
);
Ok(())
};
let mut i = 0;
let mut ampersand = None;
for (j, &c) in s.as_bytes().iter().enumerate() {
match c {
b'&' => {
ampersand = Some(j);
}
b';' => {
if !(ampersand.is_some() && j - ampersand.unwrap() > 1) {
parse_single(&s[i..j])?;
i = j + 1;
}
}
_ => {
if ampersand.is_some() & !(b'#' == c || char::from(c).is_ascii_alphanumeric()) {
ampersand = None;
}
}
}
}
if i != s.as_bytes().len() {
parse_single(&s[i..])?;
}
Ok(VariantMap(map))
}
}
impl Display for VariantMap<String> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (v, t) in self.0.iter() {
write!(f, "{}:{};", v.get_name(), t)?;
}
Ok(())
}
}
impl Display for VariantMap<Vec<(String, String)>> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (variant, pairs) in self.0.iter() {
for (from, to) in pairs.iter() {
write!(f, "{}⇒{}: {}", from, variant, to)?;
}
}
Ok(())
}
}
impl<T> From<HashMap<Variant, T>> for VariantMap<T> {
fn from(hm: HashMap<Variant, T>) -> Self {
Self(hm)
}
}
macro_rules! match_fallback {
( $map:expr, $target:expr, $($t:tt)* ) => {
match_fallback!(@build $map, $target, (), $($t)*)
};
(@build $map:expr, $target:expr, ($($arms:tt)*), $variant:ident -> [ $($fallbacks:tt)* ], $($others:tt)* ) => {
match_fallback!(@build $map, $target, ($($arms)* $variant => get_with_fallback!($map, $variant, $($fallbacks)*),), $($others)*)
};
(@build $map:expr, $target:expr, ($($arms:tt)*) $(,)? ) => {
match $target {
$($arms)*
#[allow(unreachable_patterns)]
_ => None
}.map(String::as_str)
};
}
use match_fallback;