#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize, de};
use std::collections::HashMap;
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub struct MultilingualConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub title_mode: Option<MultilingualMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name_mode: Option<MultilingualMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preferred_script: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preferred_transliteration: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub scripts: HashMap<String, ScriptConfig>,
}
#[derive(Debug, PartialEq, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub enum MultilingualMode {
Primary,
Transliterated,
Translated,
Combined,
Pattern(Vec<MultilingualSegment>),
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub struct MultilingualSegment {
pub view: MultilingualView,
#[serde(default, skip_serializing_if = "SegmentWrap::is_none")]
pub wrap: SegmentWrap,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub enum MultilingualView {
Original,
Transliterated,
Translated,
}
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub enum SegmentWrap {
#[default]
None,
Brackets,
Parentheses,
}
impl SegmentWrap {
pub fn is_none(&self) -> bool {
matches!(self, SegmentWrap::None)
}
pub fn apply(&self, text: &str) -> String {
match self {
SegmentWrap::None => text.to_string(),
SegmentWrap::Brackets => format!("[{text}]"),
SegmentWrap::Parentheses => format!("({text})"),
}
}
}
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub struct ScriptConfig {
#[serde(default)]
pub use_native_ordering: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub delimiter: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sort_separator: Option<String>,
}
impl<'de> de::Deserialize<'de> for MultilingualMode {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct ModeVisitor;
impl<'de> de::Visitor<'de> for ModeVisitor {
type Value = MultilingualMode;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"a multilingual mode string (\"primary\", \"transliterated\", \
\"translated\", \"combined\") or a pattern object {{pattern: [...]}}"
)
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
match v {
"primary" => Ok(MultilingualMode::Primary),
"transliterated" => Ok(MultilingualMode::Transliterated),
"translated" => Ok(MultilingualMode::Translated),
"combined" => Ok(MultilingualMode::Combined),
_ => Err(E::unknown_variant(
v,
&[
"primary",
"transliterated",
"translated",
"combined",
"pattern",
],
)),
}
}
fn visit_map<A: de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let key: String = map
.next_key()?
.ok_or_else(|| de::Error::custom("expected \"pattern\" key, got empty map"))?;
if key != "pattern" {
return Err(de::Error::unknown_field(&key, &["pattern"]));
}
let segments: Vec<MultilingualSegment> = map.next_value()?;
if map.next_key::<String>()?.is_some() {
return Err(de::Error::custom("unexpected extra key in pattern object"));
}
Ok(MultilingualMode::Pattern(segments))
}
fn visit_enum<A: de::EnumAccess<'de>>(self, data: A) -> Result<Self::Value, A::Error> {
use de::VariantAccess as _;
let (variant, access): (String, _) = data.variant()?;
if variant == "pattern" {
let segments: Vec<MultilingualSegment> = access.newtype_variant()?;
Ok(MultilingualMode::Pattern(segments))
} else {
Err(de::Error::unknown_variant(
&variant,
&[
"primary",
"transliterated",
"translated",
"combined",
"pattern",
],
))
}
}
}
deserializer.deserialize_any(ModeVisitor)
}
}