1 | // This file is part of ICU4X. For terms of use, please see the file |
2 | // called LICENSE at the top level of the ICU4X source tree |
3 | // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). |
4 | |
5 | use core::borrow::Borrow; |
6 | use core::iter::FromIterator; |
7 | use litemap::LiteMap; |
8 | |
9 | use super::Key; |
10 | use super::Value; |
11 | |
12 | /// A list of [`Key`]-[`Value`] pairs representing functional information |
13 | /// about content transformations. |
14 | /// |
15 | /// Here are examples of fields used in Unicode: |
16 | /// - `s0`, `d0` - Transform source/destination |
17 | /// - `t0` - Machine Translation |
18 | /// - `h0` - Hybrid Locale Identifiers |
19 | /// |
20 | /// You can find the full list in [`Unicode BCP 47 T Extension`] section of LDML. |
21 | /// |
22 | /// [`Unicode BCP 47 T Extension`]: https://unicode.org/reports/tr35/tr35.html#BCP47_T_Extension |
23 | /// |
24 | /// # Examples |
25 | /// |
26 | /// ``` |
27 | /// use icu::locid::extensions::transform::{key, Fields, Value}; |
28 | /// |
29 | /// let value = "hybrid" .parse::<Value>().expect("Failed to parse a Value." ); |
30 | /// let fields = [(key!("h0" ), value)].into_iter().collect::<Fields>(); |
31 | /// |
32 | /// assert_eq!(&fields.to_string(), "h0-hybrid" ); |
33 | /// ``` |
34 | #[derive (Clone, PartialEq, Eq, Debug, Default, Hash, PartialOrd, Ord)] |
35 | pub struct Fields(LiteMap<Key, Value>); |
36 | |
37 | impl Fields { |
38 | /// Returns a new empty list of key-value pairs. Same as [`default()`](Default::default()), but is `const`. |
39 | /// |
40 | /// # Examples |
41 | /// |
42 | /// ``` |
43 | /// use icu::locid::extensions::transform::Fields; |
44 | /// |
45 | /// assert_eq!(Fields::new(), Fields::default()); |
46 | /// ``` |
47 | #[inline ] |
48 | pub const fn new() -> Self { |
49 | Self(LiteMap::new()) |
50 | } |
51 | |
52 | /// Returns `true` if there are no fields. |
53 | /// |
54 | /// # Examples |
55 | /// |
56 | /// ``` |
57 | /// use icu::locid::locale; |
58 | /// use icu::locid::Locale; |
59 | /// |
60 | /// let loc1 = Locale::try_from_bytes(b"und-t-h0-hybrid" ).unwrap(); |
61 | /// let loc2 = locale!("und-u-ca-buddhist" ); |
62 | /// |
63 | /// assert!(!loc1.extensions.transform.fields.is_empty()); |
64 | /// assert!(loc2.extensions.transform.fields.is_empty()); |
65 | /// ``` |
66 | pub fn is_empty(&self) -> bool { |
67 | self.0.is_empty() |
68 | } |
69 | |
70 | /// Empties the [`Fields`] list. |
71 | /// |
72 | /// Returns the old list. |
73 | /// |
74 | /// # Examples |
75 | /// |
76 | /// ``` |
77 | /// use icu::locid::extensions::transform::{key, Fields, Value}; |
78 | /// |
79 | /// let value = "hybrid" .parse::<Value>().expect("Failed to parse a Value." ); |
80 | /// let mut fields = [(key!("h0" ), value)].into_iter().collect::<Fields>(); |
81 | /// |
82 | /// assert_eq!(&fields.to_string(), "h0-hybrid" ); |
83 | /// |
84 | /// fields.clear(); |
85 | /// |
86 | /// assert_eq!(fields, Fields::new()); |
87 | /// ``` |
88 | pub fn clear(&mut self) -> Self { |
89 | core::mem::take(self) |
90 | } |
91 | |
92 | /// Returns `true` if the list contains a [`Value`] for the specified [`Key`]. |
93 | /// |
94 | /// |
95 | /// # Examples |
96 | /// |
97 | /// ``` |
98 | /// use icu::locid::extensions::transform::{Fields, Key, Value}; |
99 | /// |
100 | /// let key: Key = "h0" .parse().expect("Failed to parse a Key." ); |
101 | /// let value: Value = "hybrid" .parse().expect("Failed to parse a Value." ); |
102 | /// let mut fields = [(key, value)].into_iter().collect::<Fields>(); |
103 | /// |
104 | /// let key: Key = "h0" .parse().expect("Failed to parse a Key." ); |
105 | /// assert!(&fields.contains_key(&key)); |
106 | /// ``` |
107 | pub fn contains_key<Q>(&self, key: &Q) -> bool |
108 | where |
109 | Key: Borrow<Q>, |
110 | Q: Ord, |
111 | { |
112 | self.0.contains_key(key) |
113 | } |
114 | |
115 | /// Returns a reference to the [`Value`] corresponding to the [`Key`]. |
116 | /// |
117 | /// |
118 | /// # Examples |
119 | /// |
120 | /// ``` |
121 | /// use icu::locid::extensions::transform::{key, Fields, Value}; |
122 | /// |
123 | /// let value = "hybrid" .parse::<Value>().unwrap(); |
124 | /// let fields = [(key!("h0" ), value.clone())] |
125 | /// .into_iter() |
126 | /// .collect::<Fields>(); |
127 | /// |
128 | /// assert_eq!(fields.get(&key!("h0" )), Some(&value)); |
129 | /// ``` |
130 | pub fn get<Q>(&self, key: &Q) -> Option<&Value> |
131 | where |
132 | Key: Borrow<Q>, |
133 | Q: Ord, |
134 | { |
135 | self.0.get(key) |
136 | } |
137 | |
138 | /// Sets the specified keyword, returning the old value if it already existed. |
139 | /// |
140 | /// # Examples |
141 | /// |
142 | /// ``` |
143 | /// use icu::locid::extensions::transform::{key, Value}; |
144 | /// use icu::locid::Locale; |
145 | /// |
146 | /// let lower = "lower" .parse::<Value>().expect("valid extension subtag" ); |
147 | /// let casefold = "casefold" .parse::<Value>().expect("valid extension subtag" ); |
148 | /// |
149 | /// let mut loc: Locale = "en-t-hi-d0-casefold" |
150 | /// .parse() |
151 | /// .expect("valid BCP-47 identifier" ); |
152 | /// let old_value = loc.extensions.transform.fields.set(key!("d0" ), lower); |
153 | /// |
154 | /// assert_eq!(old_value, Some(casefold)); |
155 | /// assert_eq!(loc, "en-t-hi-d0-lower" .parse().unwrap()); |
156 | /// ``` |
157 | pub fn set(&mut self, key: Key, value: Value) -> Option<Value> { |
158 | self.0.insert(key, value) |
159 | } |
160 | |
161 | /// Retains a subset of fields as specified by the predicate function. |
162 | /// |
163 | /// # Examples |
164 | /// |
165 | /// ``` |
166 | /// use icu::locid::extensions::transform::key; |
167 | /// use icu::locid::Locale; |
168 | /// |
169 | /// let mut loc: Locale = "und-t-h0-hybrid-d0-hex-m0-xml" .parse().unwrap(); |
170 | /// |
171 | /// loc.extensions |
172 | /// .transform |
173 | /// .fields |
174 | /// .retain_by_key(|&k| k == key!("h0" )); |
175 | /// assert_eq!(loc, "und-t-h0-hybrid" .parse().unwrap()); |
176 | /// |
177 | /// loc.extensions |
178 | /// .transform |
179 | /// .fields |
180 | /// .retain_by_key(|&k| k == key!("d0" )); |
181 | /// assert_eq!(loc, Locale::UND); |
182 | /// ``` |
183 | pub fn retain_by_key<F>(&mut self, mut predicate: F) |
184 | where |
185 | F: FnMut(&Key) -> bool, |
186 | { |
187 | self.0.retain(|k, _| predicate(k)) |
188 | } |
189 | |
190 | pub(crate) fn for_each_subtag_str<E, F>(&self, f: &mut F) -> Result<(), E> |
191 | where |
192 | F: FnMut(&str) -> Result<(), E>, |
193 | { |
194 | for (k, v) in self.0.iter() { |
195 | f(k.as_str())?; |
196 | v.for_each_subtag_str(f)?; |
197 | } |
198 | Ok(()) |
199 | } |
200 | |
201 | /// This needs to be its own method to help with type inference in helpers.rs |
202 | #[cfg (test)] |
203 | pub(crate) fn from_tuple_vec(v: Vec<(Key, Value)>) -> Self { |
204 | v.into_iter().collect() |
205 | } |
206 | } |
207 | |
208 | impl From<LiteMap<Key, Value>> for Fields { |
209 | fn from(map: LiteMap<Key, Value>) -> Self { |
210 | Self(map) |
211 | } |
212 | } |
213 | |
214 | impl FromIterator<(Key, Value)> for Fields { |
215 | fn from_iter<I: IntoIterator<Item = (Key, Value)>>(iter: I) -> Self { |
216 | LiteMap::from_iter(iter).into() |
217 | } |
218 | } |
219 | |
220 | impl_writeable_for_key_value!(Fields, "h0" , "hybrid" , "m0" , "m0-true" ); |
221 | |