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, Key, 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::extensions::transform::Fields; |
58 | /// use icu::locid::locale; |
59 | /// use icu::locid::Locale; |
60 | /// |
61 | /// let loc1 = Locale::try_from_bytes(b"und-t-h0-hybrid" ).unwrap(); |
62 | /// let loc2 = locale!("und-u-ca-buddhist" ); |
63 | /// |
64 | /// assert!(!loc1.extensions.transform.fields.is_empty()); |
65 | /// assert!(loc2.extensions.transform.fields.is_empty()); |
66 | /// ``` |
67 | pub fn is_empty(&self) -> bool { |
68 | self.0.is_empty() |
69 | } |
70 | |
71 | /// Empties the [`Fields`] list. |
72 | /// |
73 | /// Returns the old list. |
74 | /// |
75 | /// # Examples |
76 | /// |
77 | /// ``` |
78 | /// use icu::locid::extensions::transform::{key, Fields, Value}; |
79 | /// |
80 | /// let value = "hybrid" .parse::<Value>().expect("Failed to parse a Value." ); |
81 | /// let mut fields = [(key!("h0" ), value)].into_iter().collect::<Fields>(); |
82 | /// |
83 | /// assert_eq!(&fields.to_string(), "h0-hybrid" ); |
84 | /// |
85 | /// fields.clear(); |
86 | /// |
87 | /// assert_eq!(fields, Fields::new()); |
88 | /// ``` |
89 | pub fn clear(&mut self) -> Self { |
90 | core::mem::take(self) |
91 | } |
92 | |
93 | /// Returns `true` if the list contains a [`Value`] for the specified [`Key`]. |
94 | /// |
95 | /// |
96 | /// # Examples |
97 | /// |
98 | /// ``` |
99 | /// use icu::locid::extensions::transform::{Fields, Key, Value}; |
100 | /// |
101 | /// let key: Key = "h0" .parse().expect("Failed to parse a Key." ); |
102 | /// let value: Value = "hybrid" .parse().expect("Failed to parse a Value." ); |
103 | /// let mut fields = [(key, value)].into_iter().collect::<Fields>(); |
104 | /// |
105 | /// let key: Key = "h0" .parse().expect("Failed to parse a Key." ); |
106 | /// assert!(&fields.contains_key(&key)); |
107 | /// ``` |
108 | pub fn contains_key<Q>(&self, key: &Q) -> bool |
109 | where |
110 | Key: Borrow<Q>, |
111 | Q: Ord, |
112 | { |
113 | self.0.contains_key(key) |
114 | } |
115 | |
116 | /// Returns a reference to the [`Value`] corresponding to the [`Key`]. |
117 | /// |
118 | /// |
119 | /// # Examples |
120 | /// |
121 | /// ``` |
122 | /// use icu::locid::extensions::transform::{key, Fields, Key, Value}; |
123 | /// |
124 | /// let value = "hybrid" .parse::<Value>().unwrap(); |
125 | /// let fields = [(key!("h0" ), value.clone())] |
126 | /// .into_iter() |
127 | /// .collect::<Fields>(); |
128 | /// |
129 | /// assert_eq!(fields.get(&key!("h0" )), Some(&value)); |
130 | /// ``` |
131 | pub fn get<Q>(&self, key: &Q) -> Option<&Value> |
132 | where |
133 | Key: Borrow<Q>, |
134 | Q: Ord, |
135 | { |
136 | self.0.get(key) |
137 | } |
138 | |
139 | /// Sets the specified keyword, returning the old value if it already existed. |
140 | /// |
141 | /// # Examples |
142 | /// |
143 | /// ``` |
144 | /// use icu::locid::extensions::transform::{key, Key, Value}; |
145 | /// use icu::locid::Locale; |
146 | /// |
147 | /// let lower = "lower" .parse::<Value>().expect("valid extension subtag" ); |
148 | /// let casefold = "casefold" .parse::<Value>().expect("valid extension subtag" ); |
149 | /// |
150 | /// let mut loc: Locale = "en-t-hi-d0-casefold" |
151 | /// .parse() |
152 | /// .expect("valid BCP-47 identifier" ); |
153 | /// let old_value = loc.extensions.transform.fields.set(key!("d0" ), lower); |
154 | /// |
155 | /// assert_eq!(old_value, Some(casefold)); |
156 | /// assert_eq!(loc, "en-t-hi-d0-lower" .parse().unwrap()); |
157 | /// ``` |
158 | pub fn set(&mut self, key: Key, value: Value) -> Option<Value> { |
159 | self.0.insert(key, value) |
160 | } |
161 | |
162 | /// Retains a subset of fields as specified by the predicate function. |
163 | /// |
164 | /// # Examples |
165 | /// |
166 | /// ``` |
167 | /// use icu::locid::extensions::transform::key; |
168 | /// use icu::locid::Locale; |
169 | /// |
170 | /// let mut loc: Locale = "und-t-h0-hybrid-d0-hex-m0-xml" .parse().unwrap(); |
171 | /// |
172 | /// loc.extensions |
173 | /// .transform |
174 | /// .fields |
175 | /// .retain_by_key(|&k| k == key!("h0" )); |
176 | /// assert_eq!(loc, "und-t-h0-hybrid" .parse().unwrap()); |
177 | /// |
178 | /// loc.extensions |
179 | /// .transform |
180 | /// .fields |
181 | /// .retain_by_key(|&k| k == key!("d0" )); |
182 | /// assert_eq!(loc, Locale::UND); |
183 | /// ``` |
184 | pub fn retain_by_key<F>(&mut self, mut predicate: F) |
185 | where |
186 | F: FnMut(&Key) -> bool, |
187 | { |
188 | self.0.retain(|k, _| predicate(k)) |
189 | } |
190 | |
191 | pub(crate) fn for_each_subtag_str<E, F>(&self, f: &mut F) -> Result<(), E> |
192 | where |
193 | F: FnMut(&str) -> Result<(), E>, |
194 | { |
195 | for (k, v) in self.0.iter() { |
196 | f(k.as_str())?; |
197 | v.for_each_subtag_str(f)?; |
198 | } |
199 | Ok(()) |
200 | } |
201 | |
202 | /// This needs to be its own method to help with type inference in helpers.rs |
203 | #[cfg (test)] |
204 | pub(crate) fn from_tuple_vec(v: Vec<(Key, Value)>) -> Self { |
205 | v.into_iter().collect() |
206 | } |
207 | } |
208 | |
209 | impl From<LiteMap<Key, Value>> for Fields { |
210 | fn from(map: LiteMap<Key, Value>) -> Self { |
211 | Self(map) |
212 | } |
213 | } |
214 | |
215 | impl FromIterator<(Key, Value)> for Fields { |
216 | fn from_iter<I: IntoIterator<Item = (Key, Value)>>(iter: I) -> Self { |
217 | LiteMap::from_iter(iter).into() |
218 | } |
219 | } |
220 | |
221 | impl_writeable_for_key_value!(Fields, "h0" , "hybrid" , "m0" , "m0-true" ); |
222 | |