// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use core::borrow::Borrow; use core::iter::FromIterator; use litemap::LiteMap; use super::Key; use super::Value; /// A list of [`Key`]-[`Value`] pairs representing functional information /// about content transformations. /// /// Here are examples of fields used in Unicode: /// - `s0`, `d0` - Transform source/destination /// - `t0` - Machine Translation /// - `h0` - Hybrid Locale Identifiers /// /// You can find the full list in [`Unicode BCP 47 T Extension`] section of LDML. /// /// [`Unicode BCP 47 T Extension`]: https://unicode.org/reports/tr35/tr35.html#BCP47_T_Extension /// /// # Examples /// /// ``` /// use icu::locid::extensions::transform::{Fields, Key, Value}; /// /// let key: Key = "h0".parse().expect("Failed to parse a Key."); /// let value: Value = "hybrid".parse().expect("Failed to parse a Value."); /// let fields: Fields = vec![(key, value)].into_iter().collect(); /// /// assert_eq!(&fields.to_string(), "h0-hybrid"); /// ``` #[derive(Clone, PartialEq, Eq, Debug, Default, Hash, PartialOrd, Ord)] pub struct Fields(LiteMap); impl Fields { /// Returns a new empty list of key-value pairs. Same as [`default()`](Default::default()), but is `const`. /// /// # Examples /// /// ``` /// use icu::locid::extensions::transform::Fields; /// /// assert_eq!(Fields::new(), Fields::default()); /// ``` #[inline] pub const fn new() -> Self { Self(LiteMap::new()) } /// Returns `true` if there are no fields. /// /// # Examples /// /// ``` /// use icu::locid::extensions::transform::Fields; /// use icu::locid::locale; /// use icu::locid::Locale; /// /// let loc1 = Locale::try_from_bytes(b"und-t-h0-hybrid").unwrap(); /// let loc2 = locale!("und-u-ca-buddhist"); /// /// assert!(!loc1.extensions.transform.fields.is_empty()); /// assert!(loc2.extensions.transform.fields.is_empty()); /// ``` pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Empties the [`Fields`] list. /// /// Returns the old list. /// /// # Examples /// /// ``` /// use icu::locid::extensions::transform::{Fields, Key, Value}; /// /// let key: Key = "h0".parse().expect("Failed to parse a Key."); /// let value: Value = "hybrid".parse().expect("Failed to parse a Value."); /// let mut fields: Fields = vec![(key, value)].into_iter().collect(); /// /// assert_eq!(&fields.to_string(), "h0-hybrid"); /// /// fields.clear(); /// /// assert_eq!(&fields.to_string(), ""); /// ``` pub fn clear(&mut self) -> Self { core::mem::take(self) } /// Returns `true` if the list contains a [`Value`] for the specified [`Key`]. /// /// /// # Examples /// /// ``` /// use icu::locid::extensions::transform::{Fields, Key, Value}; /// /// let key: Key = "h0".parse().expect("Failed to parse a Key."); /// let value: Value = "hybrid".parse().expect("Failed to parse a Value."); /// let mut fields: Fields = vec![(key, value)].into_iter().collect(); /// /// let key: Key = "h0".parse().expect("Failed to parse a Key."); /// assert!(&fields.contains_key(&key)); /// ``` pub fn contains_key(&self, key: &Q) -> bool where Key: Borrow, Q: Ord, { self.0.contains_key(key) } /// Returns a reference to the [`Value`] corresponding to the [`Key`]. /// /// /// # Examples /// /// ``` /// use icu::locid::extensions::transform::{Fields, Key, Value}; /// /// let key: Key = "h0".parse().expect("Failed to parse a Key."); /// let value: Value = "hybrid".parse().expect("Failed to parse a Value."); /// let mut fields: Fields = vec![(key, value)].into_iter().collect(); /// /// let key: Key = "h0".parse().expect("Failed to parse a Key."); /// assert_eq!( /// fields.get(&key).map(|v| v.to_string()), /// Some("hybrid".to_string()) /// ); /// ``` pub fn get(&self, key: &Q) -> Option<&Value> where Key: Borrow, Q: Ord, { self.0.get(key) } /// Sets the specified keyword, returning the old value if it already existed. /// /// # Examples /// /// ``` /// use icu::locid::extensions::transform::Key; /// use icu::locid::extensions::transform::Value; /// use icu::locid::extensions_transform_key as key; /// use icu::locid::Locale; /// /// let lower = "lower".parse::().expect("valid extension subtag"); /// let casefold = "casefold".parse::().expect("valid extension subtag"); /// /// let mut loc: Locale = "en-t-hi-d0-casefold" /// .parse() /// .expect("valid BCP-47 identifier"); /// let old_value = loc.extensions.transform.fields.set(key!("d0"), lower); /// /// assert_eq!(old_value, Some(casefold)); /// assert_eq!(loc, "en-t-hi-d0-lower".parse().unwrap()); /// ``` pub fn set(&mut self, key: Key, value: Value) -> Option { self.0.insert(key, value) } /// Retains a subset of fields as specified by the predicate function. /// /// # Examples /// /// ``` /// use icu::locid::extensions_transform_key as key; /// use icu::locid::Locale; /// /// let mut loc: Locale = "und-t-h0-hybrid-d0-hex-m0-xml".parse().unwrap(); /// /// loc.extensions /// .transform /// .fields /// .retain_by_key(|&k| k == key!("h0")); /// assert_eq!(loc, "und-t-h0-hybrid".parse().unwrap()); /// /// loc.extensions /// .transform /// .fields /// .retain_by_key(|&k| k == key!("d0")); /// assert_eq!(loc, Locale::UND); /// ``` pub fn retain_by_key(&mut self, mut predicate: F) where F: FnMut(&Key) -> bool, { self.0.retain(|k, _| predicate(k)) } pub(crate) fn for_each_subtag_str(&self, f: &mut F) -> Result<(), E> where F: FnMut(&str) -> Result<(), E>, { for (k, v) in self.0.iter() { f(k.as_str())?; v.for_each_subtag_str(f)?; } Ok(()) } /// This needs to be its own method to help with type inference in helpers.rs #[cfg(test)] pub(crate) fn from_tuple_vec(v: Vec<(Key, Value)>) -> Self { v.into_iter().collect() } } impl From> for Fields { fn from(map: LiteMap) -> Self { Self(map) } } impl FromIterator<(Key, Value)> for Fields { fn from_iter>(iter: I) -> Self { LiteMap::from_iter(iter).into() } } impl_writeable_for_key_value!(Fields, "h0", "hybrid", "m0", "m0-true");