citum_schema_style/options/
multilingual.rs1#[cfg(feature = "schema")]
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize, de};
9use std::collections::HashMap;
10
11#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
13#[cfg_attr(feature = "schema", derive(JsonSchema))]
14#[serde(rename_all = "kebab-case")]
15pub struct MultilingualConfig {
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub title_mode: Option<MultilingualMode>,
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub name_mode: Option<MultilingualMode>,
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pub preferred_script: Option<String>,
25 #[serde(skip_serializing_if = "Option::is_none")]
28 pub preferred_transliteration: Option<Vec<String>>,
29 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
31 pub scripts: HashMap<String, ScriptConfig>,
32}
33
34#[derive(Debug, PartialEq, Clone, Serialize)]
36#[cfg_attr(feature = "schema", derive(JsonSchema))]
37#[serde(rename_all = "kebab-case")]
38pub enum MultilingualMode {
39 Primary,
41 Transliterated,
43 Translated,
45 Combined,
48 Pattern(Vec<MultilingualSegment>),
66}
67
68#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
70#[cfg_attr(feature = "schema", derive(JsonSchema))]
71#[serde(rename_all = "kebab-case")]
72pub struct MultilingualSegment {
73 pub view: MultilingualView,
75 #[serde(default, skip_serializing_if = "SegmentWrap::is_none")]
77 pub wrap: SegmentWrap,
78}
79
80#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
82#[cfg_attr(feature = "schema", derive(JsonSchema))]
83#[serde(rename_all = "kebab-case")]
84pub enum MultilingualView {
85 OriginalScript,
87 Transliterated,
89 Translated,
91}
92
93#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
95#[cfg_attr(feature = "schema", derive(JsonSchema))]
96#[serde(rename_all = "kebab-case")]
97pub enum SegmentWrap {
98 #[default]
100 None,
101 Brackets,
103 Parentheses,
105}
106
107impl SegmentWrap {
108 pub fn is_none(&self) -> bool {
110 matches!(self, SegmentWrap::None)
111 }
112
113 pub fn apply(&self, text: &str) -> String {
115 match self {
116 SegmentWrap::None => text.to_string(),
117 SegmentWrap::Brackets => format!("[{text}]"),
118 SegmentWrap::Parentheses => format!("({text})"),
119 }
120 }
121}
122
123#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
125#[cfg_attr(feature = "schema", derive(JsonSchema))]
126#[serde(rename_all = "kebab-case")]
127pub struct ScriptConfig {
128 #[serde(default)]
130 pub use_native_ordering: bool,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub delimiter: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub sort_separator: Option<String>,
137}
138
139impl<'de> de::Deserialize<'de> for MultilingualMode {
150 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
151 where
152 D: de::Deserializer<'de>,
153 {
154 struct ModeVisitor;
155
156 impl<'de> de::Visitor<'de> for ModeVisitor {
157 type Value = MultilingualMode;
158
159 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
160 write!(
161 f,
162 "a multilingual mode string (\"primary\", \"transliterated\", \
163 \"translated\", \"combined\") or a pattern object {{pattern: [...]}}"
164 )
165 }
166
167 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
168 match v {
169 "primary" => Ok(MultilingualMode::Primary),
170 "transliterated" => Ok(MultilingualMode::Transliterated),
171 "translated" => Ok(MultilingualMode::Translated),
172 "combined" => Ok(MultilingualMode::Combined),
173 _ => Err(E::unknown_variant(
174 v,
175 &[
176 "primary",
177 "transliterated",
178 "translated",
179 "combined",
180 "pattern",
181 ],
182 )),
183 }
184 }
185
186 fn visit_map<A: de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
187 let key: String = map
188 .next_key()?
189 .ok_or_else(|| de::Error::custom("expected \"pattern\" key, got empty map"))?;
190 if key != "pattern" {
191 return Err(de::Error::unknown_field(&key, &["pattern"]));
192 }
193 let segments: Vec<MultilingualSegment> = map.next_value()?;
194 if map.next_key::<String>()?.is_some() {
195 return Err(de::Error::custom("unexpected extra key in pattern object"));
196 }
197 Ok(MultilingualMode::Pattern(segments))
198 }
199
200 fn visit_enum<A: de::EnumAccess<'de>>(self, data: A) -> Result<Self::Value, A::Error> {
203 use de::VariantAccess as _;
204 let (variant, access): (String, _) = data.variant()?;
205 if variant == "pattern" {
206 let segments: Vec<MultilingualSegment> = access.newtype_variant()?;
207 Ok(MultilingualMode::Pattern(segments))
208 } else {
209 Err(de::Error::unknown_variant(
210 &variant,
211 &[
212 "primary",
213 "transliterated",
214 "translated",
215 "combined",
216 "pattern",
217 ],
218 ))
219 }
220 }
221 }
222
223 deserializer.deserialize_any(ModeVisitor)
224 }
225}