icu_provider/
request.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
5#[cfg(feature = "alloc")]
6use alloc::borrow::Cow;
7#[cfg(feature = "alloc")]
8use alloc::borrow::ToOwned;
9#[cfg(feature = "alloc")]
10use alloc::boxed::Box;
11#[cfg(feature = "alloc")]
12use alloc::string::String;
13#[cfg(feature = "alloc")]
14use core::cmp::Ordering;
15use core::default::Default;
16use core::fmt;
17use core::fmt::Debug;
18use core::hash::Hash;
19use core::ops::Deref;
20#[cfg(feature = "alloc")]
21use zerovec::ule::VarULE;
22
23pub use icu_locale_core::DataLocale;
24
25/// The request type passed into all data provider implementations.
26#[derive(#[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<'a> ::core::default::Default for DataRequest<'a> {
    #[inline]
    fn default() -> DataRequest<'a> {
        DataRequest {
            id: ::core::default::Default::default(),
            metadata: ::core::default::Default::default(),
        }
    }
}Default, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<'a> ::core::fmt::Debug for DataRequest<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "DataRequest",
            "id", &self.id, "metadata", &&self.metadata)
    }
}Debug, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<'a> ::core::clone::Clone for DataRequest<'a> {
    #[inline]
    fn clone(&self) -> DataRequest<'a> {
        let _: ::core::clone::AssertParamIsClone<DataIdentifierBorrowed<'a>>;
        let _: ::core::clone::AssertParamIsClone<DataRequestMetadata>;
        *self
    }
}Clone, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<'a> ::core::marker::Copy for DataRequest<'a> { }Copy, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<'a> ::core::cmp::PartialEq for DataRequest<'a> {
    #[inline]
    fn eq(&self, other: &DataRequest<'a>) -> bool {
        self.id == other.id && self.metadata == other.metadata
    }
}PartialEq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl<'a> ::core::cmp::Eq for DataRequest<'a> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {
        let _: ::core::cmp::AssertParamIsEq<DataIdentifierBorrowed<'a>>;
        let _: ::core::cmp::AssertParamIsEq<DataRequestMetadata>;
    }
}Eq)]
27#[allow(clippy::exhaustive_structs)] // this type is stable
28pub struct DataRequest<'a> {
29    /// The data identifier for which to load data.
30    ///
31    /// If locale fallback is enabled, the resulting data may be from a different identifier
32    /// than the one requested here.
33    pub id: DataIdentifierBorrowed<'a>,
34    /// Metadata that may affect the behavior of the data provider.
35    pub metadata: DataRequestMetadata,
36}
37
38/// Metadata for data requests. This is currently empty, but it may be extended with options
39/// for tuning locale fallback, buffer layout, and so forth.
40#[derive(#[automatically_derived]
impl ::core::default::Default for DataRequestMetadata {
    #[inline]
    fn default() -> DataRequestMetadata {
        DataRequestMetadata {
            silent: ::core::default::Default::default(),
            attributes_prefix_match: ::core::default::Default::default(),
        }
    }
}Default, #[automatically_derived]
impl ::core::fmt::Debug for DataRequestMetadata {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "DataRequestMetadata", "silent", &self.silent,
            "attributes_prefix_match", &&self.attributes_prefix_match)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for DataRequestMetadata {
    #[inline]
    fn clone(&self) -> DataRequestMetadata {
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for DataRequestMetadata { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for DataRequestMetadata {
    #[inline]
    fn eq(&self, other: &DataRequestMetadata) -> bool {
        self.silent == other.silent &&
            self.attributes_prefix_match == other.attributes_prefix_match
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for DataRequestMetadata {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {
        let _: ::core::cmp::AssertParamIsEq<bool>;
    }
}Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for DataRequestMetadata {
    #[inline]
    fn partial_cmp(&self, other: &DataRequestMetadata)
        -> ::core::option::Option<::core::cmp::Ordering> {
        match ::core::cmp::PartialOrd::partial_cmp(&self.silent,
                &other.silent) {
            ::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
                ::core::cmp::PartialOrd::partial_cmp(&self.attributes_prefix_match,
                    &other.attributes_prefix_match),
            cmp => cmp,
        }
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for DataRequestMetadata {
    #[inline]
    fn cmp(&self, other: &DataRequestMetadata) -> ::core::cmp::Ordering {
        match ::core::cmp::Ord::cmp(&self.silent, &other.silent) {
            ::core::cmp::Ordering::Equal =>
                ::core::cmp::Ord::cmp(&self.attributes_prefix_match,
                    &other.attributes_prefix_match),
            cmp => cmp,
        }
    }
}Ord)]
41#[non_exhaustive]
42pub struct DataRequestMetadata {
43    /// Silent requests do not log errors. This can be used for exploratory querying, such as fallbacks.
44    pub silent: bool,
45    /// Whether to allow prefix matches for the data marker attributes.
46    pub attributes_prefix_match: bool,
47}
48
49/// The borrowed version of a [`DataIdentifierCow`].
50#[derive(#[automatically_derived]
impl<'a> ::core::default::Default for DataIdentifierBorrowed<'a> {
    #[inline]
    fn default() -> DataIdentifierBorrowed<'a> {
        DataIdentifierBorrowed {
            marker_attributes: ::core::default::Default::default(),
            locale: ::core::default::Default::default(),
        }
    }
}Default, #[automatically_derived]
impl<'a> ::core::fmt::Debug for DataIdentifierBorrowed<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "DataIdentifierBorrowed", "marker_attributes",
            &self.marker_attributes, "locale", &&self.locale)
    }
}Debug, #[automatically_derived]
impl<'a> ::core::clone::Clone for DataIdentifierBorrowed<'a> {
    #[inline]
    fn clone(&self) -> DataIdentifierBorrowed<'a> {
        let _: ::core::clone::AssertParamIsClone<&'a DataMarkerAttributes>;
        let _: ::core::clone::AssertParamIsClone<&'a DataLocale>;
        *self
    }
}Clone, #[automatically_derived]
impl<'a> ::core::marker::Copy for DataIdentifierBorrowed<'a> { }Copy, #[automatically_derived]
impl<'a> ::core::cmp::PartialEq for DataIdentifierBorrowed<'a> {
    #[inline]
    fn eq(&self, other: &DataIdentifierBorrowed<'a>) -> bool {
        self.marker_attributes == other.marker_attributes &&
            self.locale == other.locale
    }
}PartialEq, #[automatically_derived]
impl<'a> ::core::cmp::Eq for DataIdentifierBorrowed<'a> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {
        let _: ::core::cmp::AssertParamIsEq<&'a DataMarkerAttributes>;
        let _: ::core::cmp::AssertParamIsEq<&'a DataLocale>;
    }
}Eq)]
51#[non_exhaustive]
52pub struct DataIdentifierBorrowed<'a> {
53    /// Marker-specific request attributes
54    pub marker_attributes: &'a DataMarkerAttributes,
55    /// The CLDR locale
56    pub locale: &'a DataLocale,
57}
58
59impl fmt::Display for DataIdentifierBorrowed<'_> {
60    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61        fmt::Display::fmt(self.locale, f)?;
62        if !self.marker_attributes.is_empty() {
63            f.write_fmt(format_args!("/{0}", self.marker_attributes.as_str()))write!(f, "/{}", self.marker_attributes.as_str())?;
64        }
65        Ok(())
66    }
67}
68
69impl<'a> DataIdentifierBorrowed<'a> {
70    /// Creates a [`DataIdentifierBorrowed`] for a borrowed [`DataLocale`].
71    pub fn for_locale(locale: &'a DataLocale) -> Self {
72        Self {
73            locale,
74            ..Default::default()
75        }
76    }
77
78    /// Creates a [`DataIdentifierBorrowed`] for a borrowed [`DataMarkerAttributes`].
79    pub fn for_marker_attributes(marker_attributes: &'a DataMarkerAttributes) -> Self {
80        Self {
81            marker_attributes,
82            ..Default::default()
83        }
84    }
85
86    /// Creates a [`DataIdentifierBorrowed`] for a borrowed [`DataMarkerAttributes`] and [`DataLocale`].
87    pub fn for_marker_attributes_and_locale(
88        marker_attributes: &'a DataMarkerAttributes,
89        locale: &'a DataLocale,
90    ) -> Self {
91        Self {
92            marker_attributes,
93            locale,
94        }
95    }
96
97    /// Converts this [`DataIdentifierBorrowed`] into a [`DataIdentifierCow<'static>`].
98    ///
99    /// ✨ *Enabled with the `alloc` Cargo feature.*
100    #[cfg(feature = "alloc")]
101    pub fn into_owned(self) -> DataIdentifierCow<'static> {
102        DataIdentifierCow {
103            marker_attributes: Cow::Owned(self.marker_attributes.to_owned()),
104            locale: *self.locale,
105        }
106    }
107
108    /// Borrows this [`DataIdentifierBorrowed`] as a [`DataIdentifierCow<'a>`].
109    ///
110    /// ✨ *Enabled with the `alloc` Cargo feature.*
111    #[cfg(feature = "alloc")]
112    pub fn as_cow(self) -> DataIdentifierCow<'a> {
113        DataIdentifierCow {
114            marker_attributes: Cow::Borrowed(self.marker_attributes),
115            locale: *self.locale,
116        }
117    }
118}
119
120/// A data identifier identifies a particular version of data, such as "English".
121///
122/// It is a wrapper around a [`DataLocale`] and a [`DataMarkerAttributes`].
123///
124/// ✨ *Enabled with the `alloc` Cargo feature.*
125#[derive(Debug, PartialEq, Eq, Hash, Clone)]
126#[non_exhaustive]
127#[cfg(feature = "alloc")]
128pub struct DataIdentifierCow<'a> {
129    /// Marker-specific request attributes
130    pub marker_attributes: Cow<'a, DataMarkerAttributes>,
131    /// The CLDR locale
132    pub locale: DataLocale,
133}
134
135#[cfg(feature = "alloc")]
136impl PartialOrd for DataIdentifierCow<'_> {
137    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
138        Some(self.cmp(other))
139    }
140}
141
142#[cfg(feature = "alloc")]
143impl Ord for DataIdentifierCow<'_> {
144    fn cmp(&self, other: &Self) -> Ordering {
145        self.marker_attributes
146            .cmp(&other.marker_attributes)
147            .then_with(|| self.locale.total_cmp(&other.locale))
148    }
149}
150
151#[cfg(feature = "alloc")]
152impl fmt::Display for DataIdentifierCow<'_> {
153    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154        fmt::Display::fmt(&self.locale, f)?;
155        if !self.marker_attributes.is_empty() {
156            write!(f, "/{}", self.marker_attributes.as_str())?;
157        }
158        Ok(())
159    }
160}
161
162#[cfg(feature = "alloc")]
163impl<'a> DataIdentifierCow<'a> {
164    /// Borrows this [`DataIdentifierCow`] as a [`DataIdentifierBorrowed<'a>`].
165    pub fn as_borrowed(&'a self) -> DataIdentifierBorrowed<'a> {
166        DataIdentifierBorrowed {
167            marker_attributes: &self.marker_attributes,
168            locale: &self.locale,
169        }
170    }
171
172    /// Creates a [`DataIdentifierCow`] from an owned [`DataLocale`].
173    pub fn from_locale(locale: DataLocale) -> Self {
174        Self {
175            marker_attributes: Cow::Borrowed(DataMarkerAttributes::empty()),
176            locale,
177        }
178    }
179
180    /// Creates a [`DataIdentifierCow`] from a borrowed [`DataMarkerAttributes`].
181    pub fn from_marker_attributes(marker_attributes: &'a DataMarkerAttributes) -> Self {
182        Self {
183            marker_attributes: Cow::Borrowed(marker_attributes),
184            locale: Default::default(),
185        }
186    }
187
188    /// Creates a [`DataIdentifierCow`] from an owned [`DataMarkerAttributes`].
189    pub fn from_marker_attributes_owned(marker_attributes: Box<DataMarkerAttributes>) -> Self {
190        Self {
191            marker_attributes: Cow::Owned(marker_attributes),
192            locale: Default::default(),
193        }
194    }
195
196    /// Creates a [`DataIdentifierCow`] from an owned [`DataMarkerAttributes`] and an owned [`DataLocale`].
197    pub fn from_owned(marker_attributes: Box<DataMarkerAttributes>, locale: DataLocale) -> Self {
198        Self {
199            marker_attributes: Cow::Owned(marker_attributes),
200            locale,
201        }
202    }
203
204    /// Creates a [`DataIdentifierCow`] from a borrowed [`DataMarkerAttributes`] and an owned [`DataLocale`].
205    pub fn from_borrowed_and_owned(
206        marker_attributes: &'a DataMarkerAttributes,
207        locale: DataLocale,
208    ) -> Self {
209        Self {
210            marker_attributes: Cow::Borrowed(marker_attributes),
211            locale,
212        }
213    }
214
215    /// Returns whether this id is equal to the default.
216    pub fn is_unknown(&self) -> bool {
217        self.marker_attributes.is_empty() && self.locale.is_unknown()
218    }
219}
220
221#[cfg(feature = "alloc")]
222impl Default for DataIdentifierCow<'_> {
223    fn default() -> Self {
224        Self {
225            marker_attributes: Cow::Borrowed(Default::default()),
226            locale: Default::default(),
227        }
228    }
229}
230
231/// An additional key to identify data beyond a [`DataLocale`].
232///
233/// The is a loose wrapper around a string, with semantics defined by each [`DataMarker`](crate::DataMarker).
234#[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for DataMarkerAttributes {
    #[inline]
    fn eq(&self, other: &DataMarkerAttributes) -> bool {
        self.value == other.value
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for DataMarkerAttributes {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {
        let _: ::core::cmp::AssertParamIsEq<str>;
    }
}Eq, #[automatically_derived]
impl ::core::cmp::Ord for DataMarkerAttributes {
    #[inline]
    fn cmp(&self, other: &DataMarkerAttributes) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&self.value, &other.value)
    }
}Ord, #[automatically_derived]
impl ::core::cmp::PartialOrd for DataMarkerAttributes {
    #[inline]
    fn partial_cmp(&self, other: &DataMarkerAttributes)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::cmp::PartialOrd::partial_cmp(&self.value, &other.value)
    }
}PartialOrd, #[automatically_derived]
impl ::core::hash::Hash for DataMarkerAttributes {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        ::core::hash::Hash::hash(&self.value, state)
    }
}Hash)]
235#[repr(transparent)]
236pub struct DataMarkerAttributes {
237    // Validated to be non-empty ASCII alphanumeric + hyphen + underscore
238    value: str,
239}
240
241impl Default for &DataMarkerAttributes {
242    fn default() -> Self {
243        DataMarkerAttributes::empty()
244    }
245}
246
247impl Deref for DataMarkerAttributes {
248    type Target = str;
249    #[inline]
250    fn deref(&self) -> &Self::Target {
251        &self.value
252    }
253}
254
255impl Debug for DataMarkerAttributes {
256    #[inline]
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        self.value.fmt(f)
259    }
260}
261
262/// Invalid character
263#[derive(#[automatically_derived]
impl ::core::fmt::Debug for AttributeParseError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "AttributeParseError")
    }
}Debug)]
264#[non_exhaustive]
265pub struct AttributeParseError;
266
267impl DataMarkerAttributes {
268    /// Safety-usable invariant: validated bytes are ASCII only
269    const fn validate(s: &[u8]) -> Result<(), AttributeParseError> {
270        let mut i = 0;
271        while i < s.len() {
272            #[expect(clippy::indexing_slicing)] // duh
273            if !#[allow(non_exhaustive_omitted_patterns)] match s[i] {
    b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_' => true,
    _ => false,
}matches!(s[i], b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_') {
274                return Err(AttributeParseError);
275            }
276            i += 1;
277        }
278        Ok(())
279    }
280
281    /// Creates a borrowed [`DataMarkerAttributes`] from a borrowed string.
282    ///
283    /// Returns an error if the string contains characters other than `[a-zA-Z0-9_\-]`.
284    pub const fn try_from_str(s: &str) -> Result<&Self, AttributeParseError> {
285        Self::try_from_utf8(s.as_bytes())
286    }
287
288    /// Attempts to create a borrowed [`DataMarkerAttributes`] from a borrowed UTF-8 encoded byte slice.
289    ///
290    /// # Examples
291    ///
292    /// ```
293    /// use icu_provider::prelude::*;
294    ///
295    /// let bytes = b"long-meter";
296    /// let marker = DataMarkerAttributes::try_from_utf8(bytes).unwrap();
297    /// assert_eq!(marker.to_string(), "long-meter");
298    /// ```
299    ///
300    /// # Errors
301    ///
302    /// Returns an error if the byte slice contains code units other than `[a-zA-Z0-9_\-]`.
303    pub const fn try_from_utf8(code_units: &[u8]) -> Result<&Self, AttributeParseError> {
304        let Ok(()) = Self::validate(code_units) else {
305            return Err(AttributeParseError);
306        };
307
308        // SAFETY: `validate` requires a UTF-8 subset
309        let s = unsafe { core::str::from_utf8_unchecked(code_units) };
310
311        // SAFETY: `Self` has the same layout as `str`
312        Ok(unsafe { &*(s as *const str as *const Self) })
313    }
314
315    /// Creates an owned [`DataMarkerAttributes`] from an owned string.
316    ///
317    /// Returns an error if the string contains characters other than `[a-zA-Z0-9_\-]`.
318    ///
319    /// ✨ *Enabled with the `alloc` Cargo feature.*
320    #[cfg(feature = "alloc")]
321    pub fn try_from_string(s: String) -> Result<Box<Self>, AttributeParseError> {
322        let Ok(()) = Self::validate(s.as_bytes()) else {
323            return Err(AttributeParseError);
324        };
325
326        // SAFETY: `Self` has the same layout as `str`
327        Ok(unsafe { core::mem::transmute::<Box<str>, Box<Self>>(s.into_boxed_str()) })
328    }
329
330    /// Creates a borrowed [`DataMarkerAttributes`] from a borrowed string.
331    ///
332    /// Panics if the string contains characters other than `[a-zA-Z0-9_\-]`.
333    pub const fn from_str_or_panic(s: &str) -> &Self {
334        let Ok(r) = Self::try_from_str(s) else {
335            {
    ::core::panicking::panic_fmt(format_args!("Invalid marker attribute syntax"));
}panic!("Invalid marker attribute syntax")
336        };
337        r
338    }
339
340    /// Creates an empty [`DataMarkerAttributes`].
341    pub const fn empty() -> &'static Self {
342        // SAFETY: `Self` has the same layout as `str`
343        unsafe { &*("" as *const str as *const Self) }
344    }
345
346    /// Returns this [`DataMarkerAttributes`] as a `&str`.
347    pub const fn as_str(&self) -> &str {
348        &self.value
349    }
350}
351
352/// ✨ *Enabled with the `alloc` Cargo feature.*
353#[cfg(feature = "alloc")]
354impl ToOwned for DataMarkerAttributes {
355    type Owned = Box<Self>;
356    fn to_owned(&self) -> Self::Owned {
357        // SAFETY: `Self` has the same layout as `str`
358        unsafe { core::mem::transmute::<Box<str>, Box<Self>>(self.as_str().to_boxed()) }
359    }
360}
361
362#[test]
363fn test_data_marker_attributes_from_utf8() {
364    let bytes_vec: Vec<&[u8]> = vec![
365        b"long-meter",
366        b"long",
367        b"meter",
368        b"short-meter-second",
369        b"usd",
370    ];
371
372    for bytes in bytes_vec {
373        let marker = DataMarkerAttributes::try_from_utf8(bytes).unwrap();
374        assert_eq!(marker.to_string().as_bytes(), bytes);
375    }
376}