/rust/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-2.1.1/src/request.rs
Line | Count | Source |
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")] |
6 | | use alloc::borrow::Cow; |
7 | | #[cfg(feature = "alloc")] |
8 | | use alloc::borrow::ToOwned; |
9 | | #[cfg(feature = "alloc")] |
10 | | use alloc::boxed::Box; |
11 | | #[cfg(feature = "alloc")] |
12 | | use alloc::string::String; |
13 | | #[cfg(feature = "alloc")] |
14 | | use core::cmp::Ordering; |
15 | | use core::default::Default; |
16 | | use core::fmt; |
17 | | use core::fmt::Debug; |
18 | | use core::hash::Hash; |
19 | | use core::ops::Deref; |
20 | | #[cfg(feature = "alloc")] |
21 | | use zerovec::ule::VarULE; |
22 | | |
23 | | pub use icu_locale_core::DataLocale; |
24 | | |
25 | | /// The request type passed into all data provider implementations. |
26 | | #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] |
27 | | #[allow(clippy::exhaustive_structs)] // this type is stable |
28 | | pub 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(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
41 | | #[non_exhaustive] |
42 | | pub 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(Default, Debug, Clone, Copy, PartialEq, Eq)] |
51 | | #[non_exhaustive] |
52 | | pub 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 | | |
59 | | impl fmt::Display for DataIdentifierBorrowed<'_> { |
60 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
61 | 0 | fmt::Display::fmt(self.locale, f)?; |
62 | 0 | if !self.marker_attributes.is_empty() { |
63 | 0 | write!(f, "/{}", self.marker_attributes.as_str())?; |
64 | 0 | } |
65 | 0 | Ok(()) |
66 | 0 | } |
67 | | } |
68 | | |
69 | | impl<'a> DataIdentifierBorrowed<'a> { |
70 | | /// Creates a [`DataIdentifierBorrowed`] for a borrowed [`DataLocale`]. |
71 | 0 | pub fn for_locale(locale: &'a DataLocale) -> Self { |
72 | 0 | Self { |
73 | 0 | locale, |
74 | 0 | ..Default::default() |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | /// Creates a [`DataIdentifierBorrowed`] for a borrowed [`DataMarkerAttributes`]. |
79 | 0 | pub fn for_marker_attributes(marker_attributes: &'a DataMarkerAttributes) -> Self { |
80 | 0 | Self { |
81 | 0 | marker_attributes, |
82 | 0 | ..Default::default() |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | | /// Creates a [`DataIdentifierBorrowed`] for a borrowed [`DataMarkerAttributes`] and [`DataLocale`]. |
87 | 0 | pub fn for_marker_attributes_and_locale( |
88 | 0 | marker_attributes: &'a DataMarkerAttributes, |
89 | 0 | locale: &'a DataLocale, |
90 | 0 | ) -> Self { |
91 | 0 | Self { |
92 | 0 | marker_attributes, |
93 | 0 | locale, |
94 | 0 | } |
95 | 0 | } |
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")] |
128 | | pub 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")] |
136 | | impl PartialOrd for DataIdentifierCow<'_> { |
137 | | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
138 | | Some(self.cmp(other)) |
139 | | } |
140 | | } |
141 | | |
142 | | #[cfg(feature = "alloc")] |
143 | | impl 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")] |
152 | | impl 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")] |
163 | | impl<'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")] |
222 | | impl 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(PartialEq, Eq, Ord, PartialOrd, Hash)] |
235 | | #[repr(transparent)] |
236 | | pub struct DataMarkerAttributes { |
237 | | // Validated to be non-empty ASCII alphanumeric + hyphen + underscore |
238 | | value: str, |
239 | | } |
240 | | |
241 | | impl Default for &DataMarkerAttributes { |
242 | 0 | fn default() -> Self { |
243 | 0 | DataMarkerAttributes::empty() |
244 | 0 | } |
245 | | } |
246 | | |
247 | | impl Deref for DataMarkerAttributes { |
248 | | type Target = str; |
249 | | #[inline] |
250 | 0 | fn deref(&self) -> &Self::Target { |
251 | 0 | &self.value |
252 | 0 | } |
253 | | } |
254 | | |
255 | | impl Debug for DataMarkerAttributes { |
256 | | #[inline] |
257 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
258 | 0 | self.value.fmt(f) |
259 | 0 | } |
260 | | } |
261 | | |
262 | | /// Invalid character |
263 | | #[derive(Debug)] |
264 | | #[non_exhaustive] |
265 | | pub struct AttributeParseError; |
266 | | |
267 | | impl DataMarkerAttributes { |
268 | | /// Safety-usable invariant: validated bytes are ASCII only |
269 | 0 | const fn validate(s: &[u8]) -> Result<(), AttributeParseError> { |
270 | 0 | let mut i = 0; |
271 | 0 | while i < s.len() { |
272 | | #[expect(clippy::indexing_slicing)] // duh |
273 | 0 | if !matches!(s[i], b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_') { |
274 | 0 | return Err(AttributeParseError); |
275 | 0 | } |
276 | 0 | i += 1; |
277 | | } |
278 | 0 | Ok(()) |
279 | 0 | } |
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 | 0 | pub const fn try_from_str(s: &str) -> Result<&Self, AttributeParseError> { |
285 | 0 | Self::try_from_utf8(s.as_bytes()) |
286 | 0 | } |
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 | 0 | pub const fn try_from_utf8(code_units: &[u8]) -> Result<&Self, AttributeParseError> { |
304 | 0 | let Ok(()) = Self::validate(code_units) else { |
305 | 0 | return Err(AttributeParseError); |
306 | | }; |
307 | | |
308 | | // SAFETY: `validate` requires a UTF-8 subset |
309 | 0 | let s = unsafe { core::str::from_utf8_unchecked(code_units) }; |
310 | | |
311 | | // SAFETY: `Self` has the same layout as `str` |
312 | 0 | Ok(unsafe { &*(s as *const str as *const Self) }) |
313 | 0 | } |
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 | 0 | pub const fn from_str_or_panic(s: &str) -> &Self { |
334 | 0 | let Ok(r) = Self::try_from_str(s) else { |
335 | 0 | panic!("Invalid marker attribute syntax") |
336 | | }; |
337 | 0 | r |
338 | 0 | } |
339 | | |
340 | | /// Creates an empty [`DataMarkerAttributes`]. |
341 | 0 | pub const fn empty() -> &'static Self { |
342 | | // SAFETY: `Self` has the same layout as `str` |
343 | 0 | unsafe { &*("" as *const str as *const Self) } |
344 | 0 | } |
345 | | |
346 | | /// Returns this [`DataMarkerAttributes`] as a `&str`. |
347 | 0 | pub const fn as_str(&self) -> &str { |
348 | 0 | &self.value |
349 | 0 | } |
350 | | } |
351 | | |
352 | | /// ✨ *Enabled with the `alloc` Cargo feature.* |
353 | | #[cfg(feature = "alloc")] |
354 | | impl 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] |
363 | | fn 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 | | } |