icu_locale_core/extensions/transform/
fields.rs

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