1use serde::de::{self, Deserializer, IgnoredAny, MapAccess, Visitor};
15use std::fmt;
16
17pub trait CodeEnum: Sized + Clone + 'static {
19 const VALUES: &'static [&'static str];
21
22 fn as_str(&self) -> &str;
24
25 fn from_code(code: &str) -> Option<Self>;
28}
29
30pub trait MapKey: CodeEnum + Ord {
36 const FIELD: &'static str;
38 const ALSO_FIELD: Option<&'static str>;
40 const CODES: &'static [(&'static str, &'static str, Option<&'static str>)];
43
44 fn of_element(element: &serde_json::Value) -> Result<Self, String> {
48 let value = code_str(element.get(Self::FIELD))
49 .ok_or_else(|| format!("element has no `{}` to derive its key from", Self::FIELD))?;
50 let also = Self::ALSO_FIELD.and_then(|f| code_str(element.get(f)));
51 let code = also
52 .and_then(|a| {
53 Self::CODES
54 .iter()
55 .find(|(_, w, al)| *w == value && *al == Some(a))
56 })
57 .or_else(|| Self::CODES.iter().find(|(_, w, _)| *w == value))
58 .map(|(code, _, _)| *code)
59 .unwrap_or(value);
60 Self::from_code(code)
61 .filter(|k| k.as_str() == code)
62 .ok_or_else(|| format!("`{}` = {value:?} is not a known qualifier", Self::FIELD))
63 }
64
65 fn fill_element(&self, element: &mut serde_json::Value) {
67 let Some(obj) = element.as_object_mut() else {
68 return;
69 };
70 let Some((_, written, also)) = Self::CODES.iter().find(|(c, _, _)| *c == self.as_str())
71 else {
72 return;
73 };
74 obj.entry(Self::FIELD)
75 .or_insert_with(|| serde_json::Value::String((*written).to_string()));
76 if let (Some(field), Some(also)) = (Self::ALSO_FIELD, also) {
77 obj.entry(field)
78 .or_insert_with(|| serde_json::Value::String((*also).to_string()));
79 }
80 }
81}
82
83fn code_str(value: Option<&serde_json::Value>) -> Option<&str> {
85 let value = value?;
86 value
87 .as_str()
88 .or_else(|| value.get("code").and_then(|c| c.as_str()))
89}
90
91pub fn deserialize_code<'de, D, T>(deserializer: D) -> Result<T, D::Error>
93where
94 D: Deserializer<'de>,
95 T: CodeEnum,
96{
97 struct CodeVisitor<T>(std::marker::PhantomData<T>);
98
99 impl<T: CodeEnum> CodeVisitor<T> {
100 fn resolve<E: de::Error>(code: &str) -> Result<T, E> {
101 T::from_code(code).ok_or_else(|| de::Error::unknown_variant(code, T::VALUES))
102 }
103 }
104
105 impl<'de, T: CodeEnum> Visitor<'de> for CodeVisitor<T> {
106 type Value = T;
107
108 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
109 f.write_str("a code string or a {code, meaning} object")
110 }
111
112 fn visit_str<E: de::Error>(self, v: &str) -> Result<T, E> {
113 Self::resolve(v)
114 }
115
116 fn visit_map<M: MapAccess<'de>>(self, mut map: M) -> Result<T, M::Error> {
117 let mut code: Option<String> = None;
118 while let Some(key) = map.next_key::<String>()? {
119 if key == "code" {
120 code = Some(map.next_value()?);
121 } else {
122 map.next_value::<IgnoredAny>()?;
123 }
124 }
125 let code = code.ok_or_else(|| de::Error::missing_field("code"))?;
126 Self::resolve(&code)
127 }
128 }
129
130 deserializer.deserialize_any(CodeVisitor(std::marker::PhantomData))
131}
132
133#[allow(unused_macros)]
151macro_rules! code_enum {
152 (
153 $(#[$meta:meta])*
154 $vis:vis enum $name:ident {
155 $(
156 $(#[$vmeta:meta])*
157 $variant:ident = $value:literal $(| $alias:literal)*
158 ),+ $(,)?
159 }
160 ) => {
161 $(#[$meta])*
162 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
163 $vis enum $name {
164 $( $(#[$vmeta])* $variant, )+
165 Other(String),
167 }
168
169 impl $crate::code_enum::CodeEnum for $name {
170 const VALUES: &'static [&'static str] = &[$($value),+];
171
172 fn as_str(&self) -> &str {
173 match self {
174 $(Self::$variant => $value,)+
175 Self::Other(code) => code,
176 }
177 }
178
179 fn from_code(code: &str) -> Option<Self> {
180 Some(match code {
181 $($value $(| $alias)* => Self::$variant,)+
182 other => Self::Other(other.to_string()),
183 })
184 }
185 }
186
187 $crate::code_enum::code_enum!(@serde $name);
188 };
189 (@serde $name:ident) => {
190 impl ::std::fmt::Display for $name {
191 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
192 f.write_str($crate::code_enum::CodeEnum::as_str(self))
193 }
194 }
195
196 impl ::serde::Serialize for $name {
197 fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
198 s.serialize_str($crate::code_enum::CodeEnum::as_str(self))
199 }
200 }
201
202 impl<'de> ::serde::Deserialize<'de> for $name {
203 fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
204 $crate::code_enum::deserialize_code(d)
205 }
206 }
207 };
208}
209
210#[allow(unused_macros)]
222macro_rules! key_enum {
223 (
224 $(#[$meta:meta])*
225 $vis:vis enum $name:ident {
226 $(
227 $(#[$vmeta:meta])*
228 $variant:ident = $value:literal
229 ),+ $(,)?
230 }
231 ) => {
232 $(#[$meta])*
233 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
234 $vis enum $name {
235 $( $(#[$vmeta])* $variant, )+
236 }
237
238 impl $crate::code_enum::CodeEnum for $name {
239 const VALUES: &'static [&'static str] = &[$($value),+];
240
241 fn as_str(&self) -> &str {
242 match self {
243 $(Self::$variant => $value,)+
244 }
245 }
246
247 fn from_code(code: &str) -> Option<Self> {
248 match code {
249 $($value => Some(Self::$variant),)+
250 _ => None,
251 }
252 }
253 }
254
255 $crate::code_enum::code_enum!(@serde $name);
256 };
257}
258
259#[allow(unused_macros)]
269macro_rules! map_key {
270 (
271 $name:ident: $field:literal $(, also $also:literal)? {
272 $($code:literal => $written:literal, $also_value:expr),+ $(,)?
273 }
274 ) => {
275 impl $crate::code_enum::MapKey for $name {
276 const FIELD: &'static str = $field;
277 const ALSO_FIELD: Option<&'static str> = $crate::code_enum::map_key!(@also $($also)?);
278 const CODES: &'static [(&'static str, &'static str, Option<&'static str>)] =
279 &[$(($code, $written, $also_value)),+];
280 }
281 };
282 (@also $also:literal) => { Some($also) };
283 (@also) => { None };
284}
285
286#[allow(unused_imports)]
287pub(crate) use code_enum;
288#[allow(unused_imports)]
289pub(crate) use key_enum;
290#[allow(unused_imports)]
291pub(crate) use map_key;
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use serde_json::json;
297
298 code_enum! {
299 pub enum Partnerrolle {
301 KundeMsb = "kundeMsb" | "Z39" | "Z40",
303 Korrespondenz = "korrespondenzKundeMsb" | "Z08",
305 Z99 = "Z99",
307 }
308 }
309
310 key_enum! {
311 pub enum TestKey {
312 Z08 = "Z08",
313 Z39 = "Z39",
314 Z40 = "Z40",
315 }
316 }
317
318 map_key! {
319 TestKey: "partnerrolle", also "datenqualitaet" {
320 "Z08" => "korrespondenzKundeMsb", None,
321 "Z39" => "kundeMsb", Some("base"),
322 "Z40" => "kundeMsb", Some("informative"),
323 }
324 }
325
326 #[test]
327 fn serializes_as_written_value() {
328 assert_eq!(json!(Partnerrolle::KundeMsb), json!("kundeMsb"));
329 assert_eq!(
330 Partnerrolle::Korrespondenz.to_string(),
331 "korrespondenzKundeMsb"
332 );
333 }
334
335 #[test]
336 fn deserializes_written_value_and_raw_code_aliases() {
337 for input in ["kundeMsb", "Z39", "Z40"] {
338 let v: Partnerrolle = serde_json::from_value(json!(input)).unwrap();
339 assert_eq!(v, Partnerrolle::KundeMsb, "{input}");
340 }
341 let v: Partnerrolle = serde_json::from_value(json!("Z99")).unwrap();
342 assert_eq!(v, Partnerrolle::Z99);
343 }
344
345 #[test]
346 fn keeps_codes_outside_the_code_list() {
347 let v: Partnerrolle = serde_json::from_value(json!("Z77")).unwrap();
348 assert_eq!(v, Partnerrolle::Other("Z77".into()));
349 assert_eq!(json!(v), json!("Z77"));
350 let err = serde_json::from_value::<TestKey>(json!("Z77")).unwrap_err();
351 assert!(err.to_string().contains("Z77"), "{err}");
352 }
353
354 #[test]
355 fn deserializes_enriched_object() {
356 let v: Partnerrolle = serde_json::from_value(
357 json!({"code": "korrespondenzKundeMsb", "meaning": "Korrespondenz", "enum": "X"}),
358 )
359 .unwrap();
360 assert_eq!(v, Partnerrolle::Korrespondenz);
361 let v: Option<Partnerrolle> =
362 serde_json::from_value(json!({"meaning": null, "code": "Z40"})).unwrap();
363 assert_eq!(v, Some(Partnerrolle::KundeMsb));
364 }
365
366 #[test]
367 fn map_key_resolves_many_to_one_via_also_field() {
368 let key = |v| TestKey::of_element(&v);
369 assert_eq!(
370 key(json!({"partnerrolle": "kundeMsb", "datenqualitaet": "informative"})),
371 Ok(TestKey::Z40)
372 );
373 assert_eq!(
374 key(
375 json!({"partnerrolle": {"code": "kundeMsb", "meaning": null}, "datenqualitaet": "base"})
376 ),
377 Ok(TestKey::Z39)
378 );
379 assert_eq!(key(json!({"partnerrolle": "kundeMsb"})), Ok(TestKey::Z39));
381 assert_eq!(key(json!({"partnerrolle": "Z40"})), Ok(TestKey::Z40));
383 assert!(key(json!({"name1": "x"})).is_err());
384 assert!(key(json!({"partnerrolle": "unbekannt"})).is_err());
385 }
386
387 #[test]
388 fn map_key_fills_missing_fields_only() {
389 let mut element = json!({"name1": "x"});
390 TestKey::Z40.fill_element(&mut element);
391 assert_eq!(
392 element,
393 json!({"name1": "x", "partnerrolle": "kundeMsb", "datenqualitaet": "informative"})
394 );
395 let mut element = json!({"partnerrolle": "Z40"});
396 TestKey::Z40.fill_element(&mut element);
397 assert_eq!(element["partnerrolle"], "Z40");
398 }
399}