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
5use core::borrow::Borrow;
6use core::iter::FromIterator;
7use litemap::LiteMap;
8
9use super::Key;
10use 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)]
35pub struct Fields(LiteMap<Key, Value>);
36
37impl 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
209impl From<LiteMap<Key, Value>> for Fields {
210 fn from(map: LiteMap<Key, Value>) -> Self {
211 Self(map)
212 }
213}
214
215impl 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
221impl_writeable_for_key_value!(Fields, "h0", "hybrid", "m0", "m0-true");
222