| 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 | |