xapi_rs/data/
language_map.rs1use crate::data::{Canonical, DataError, MultiLingual, MyLanguageTag};
4use core::fmt;
5use serde::{Deserialize, Serialize};
6use std::{
7 collections::{btree_map::Keys, BTreeMap},
8 mem,
9};
10
11#[doc = include_str!("../../doc/LanguageMap.md")]
12#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
13pub struct LanguageMap(BTreeMap<MyLanguageTag, String>);
14
15pub const EMPTY_LANGUAGE_MAP: LanguageMap = LanguageMap(BTreeMap::new());
17
18impl LanguageMap {
19 pub fn new() -> Self {
21 LanguageMap(BTreeMap::new())
22 }
23
24 pub fn len(&self) -> usize {
26 self.0.len()
27 }
28
29 pub fn get(&self, k: &MyLanguageTag) -> Option<&str> {
32 self.0.get(k).map(|x| x.as_str())
33 }
34
35 pub fn is_empty(&self) -> bool {
37 self.0.is_empty()
38 }
39
40 pub fn append(&mut self, other: &mut Self) {
42 if other.is_empty() {
43 return;
44 }
45
46 if self.is_empty() {
47 mem::swap(self, other);
48 return;
49 }
50
51 self.0.append(&mut other.0)
52 }
53
54 pub fn insert(&mut self, k: &MyLanguageTag, v: &str) -> Option<String> {
57 self.0.insert(k.to_owned(), v.to_owned())
58 }
59
60 pub fn keys(&self) -> Keys<'_, MyLanguageTag, String> {
62 self.0.keys()
63 }
64
65 pub fn contains_key(&self, k: &MyLanguageTag) -> bool {
67 self.0.contains_key(k)
68 }
69
70 pub fn retain<F>(&mut self, mut f: F)
72 where
73 F: FnMut(&MyLanguageTag, &mut String) -> bool,
74 {
75 self.0.retain(|k, v| f(k, v))
76 }
77
78 pub fn extend(&mut self, other: Self) {
80 self.0.extend(other.0)
81 }
82}
83
84impl fmt::Display for LanguageMap {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 write!(f, "{}", serde_json::to_string(self).unwrap())
87 }
88}
89
90impl MultiLingual for LanguageMap {
91 fn add_label(&mut self, tag: &MyLanguageTag, label: &str) -> Result<&mut Self, DataError> {
92 self.insert(tag, label);
93
94 Ok(self)
95 }
96}
97
98impl Canonical for LanguageMap {
99 fn canonicalize(&mut self, tags: &[MyLanguageTag]) {
100 if !self.is_empty() {
101 if !tags.is_empty() {
102 for tag in tags {
103 if self.contains_key(tag) {
104 self.retain(|k, _| k == tag);
106 return;
107 }
108 }
109 }
111 if self.len() > 1 {
113 let t = self.keys().next().unwrap().clone();
114 self.retain(|k, _| k == t)
115 }
116 }
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::DataError;
124 use std::str::FromStr;
125 use tracing_test::traced_test;
126
127 #[test]
128 fn test_und_langtag() -> Result<(), DataError> {
129 let _ = MyLanguageTag::from_str("und")?;
130
131 Ok(())
132 }
133
134 #[traced_test]
135 #[test]
136 fn test_multilingual_trait() -> Result<(), DataError> {
137 let en = MyLanguageTag::from_str("en")?;
138 let de = MyLanguageTag::from_str("de")?;
139
140 let mut lm = LanguageMap::new();
141 lm.add_label(&en, "Good morning").unwrap();
142 lm.add_label(&de, "Gutten morgen").unwrap();
143 assert_eq!(lm.len(), 2);
144
145 lm.add_label(&de, "Gutten tag").unwrap();
146 assert_eq!(lm.len(), 2);
147
148 Ok(())
149 }
150
151 #[traced_test]
152 #[test]
153 fn test_canonicalize_trait() -> Result<(), DataError> {
154 let en = MyLanguageTag::from_str("en")?;
155 let de = MyLanguageTag::from_str("de")?;
156 let fr = MyLanguageTag::from_str("fr")?;
157
158 let language_tags = &[
159 MyLanguageTag::from_str("en-AU")?,
160 MyLanguageTag::from_str("en-US")?,
161 MyLanguageTag::from_str("en-GB")?,
162 en.clone(),
163 ];
164
165 let mut lm = LanguageMap::new();
166 lm.insert(&fr, "larry");
167 lm.insert(&en, "curly");
168 lm.insert(&de, "moe");
169 assert_eq!(lm.len(), 3);
170
171 lm.canonicalize(language_tags);
172
173 assert_eq!(lm.len(), 1);
174 assert_eq!(lm.get(&en).unwrap(), "curly");
175
176 Ok(())
177 }
178
179 #[traced_test]
180 #[test]
181 fn test_bad_json() {
182 const JSON: &str = r#"{"a12345678":"should error"}"#;
183
184 let res = serde_json::from_str::<LanguageMap>(JSON);
185 assert!(res.is_err());
186 }
187}