/rust/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-2.0.0/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 | | #[cfg(feature = "alloc")] |
99 | | pub fn into_owned(self) -> DataIdentifierCow<'static> { |
100 | | DataIdentifierCow { |
101 | | marker_attributes: Cow::Owned(self.marker_attributes.to_owned()), |
102 | | locale: *self.locale, |
103 | | } |
104 | | } |
105 | | |
106 | | /// Borrows this [`DataIdentifierBorrowed`] as a [`DataIdentifierCow<'a>`]. |
107 | | #[cfg(feature = "alloc")] |
108 | | pub fn as_cow(self) -> DataIdentifierCow<'a> { |
109 | | DataIdentifierCow { |
110 | | marker_attributes: Cow::Borrowed(self.marker_attributes), |
111 | | locale: *self.locale, |
112 | | } |
113 | | } |
114 | | } |
115 | | |
116 | | /// A data identifier identifies a particular version of data, such as "English". |
117 | | /// |
118 | | /// It is a wrapper around a [`DataLocale`] and a [`DataMarkerAttributes`]. |
119 | | #[derive(Debug, PartialEq, Eq, Hash, Clone)] |
120 | | #[non_exhaustive] |
121 | | #[cfg(feature = "alloc")] |
122 | | pub struct DataIdentifierCow<'a> { |
123 | | /// Marker-specific request attributes |
124 | | pub marker_attributes: Cow<'a, DataMarkerAttributes>, |
125 | | /// The CLDR locale |
126 | | pub locale: DataLocale, |
127 | | } |
128 | | |
129 | | #[cfg(feature = "alloc")] |
130 | | impl PartialOrd for DataIdentifierCow<'_> { |
131 | | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
132 | | Some(self.cmp(other)) |
133 | | } |
134 | | } |
135 | | |
136 | | #[cfg(feature = "alloc")] |
137 | | impl Ord for DataIdentifierCow<'_> { |
138 | | fn cmp(&self, other: &Self) -> Ordering { |
139 | | self.marker_attributes |
140 | | .cmp(&other.marker_attributes) |
141 | | .then_with(|| self.locale.total_cmp(&other.locale)) |
142 | | } |
143 | | } |
144 | | |
145 | | #[cfg(feature = "alloc")] |
146 | | impl fmt::Display for DataIdentifierCow<'_> { |
147 | | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
148 | | fmt::Display::fmt(&self.locale, f)?; |
149 | | if !self.marker_attributes.is_empty() { |
150 | | write!(f, "/{}", self.marker_attributes.as_str())?; |
151 | | } |
152 | | Ok(()) |
153 | | } |
154 | | } |
155 | | |
156 | | #[cfg(feature = "alloc")] |
157 | | impl<'a> DataIdentifierCow<'a> { |
158 | | /// Borrows this [`DataIdentifierCow`] as a [`DataIdentifierBorrowed<'a>`]. |
159 | | pub fn as_borrowed(&'a self) -> DataIdentifierBorrowed<'a> { |
160 | | DataIdentifierBorrowed { |
161 | | marker_attributes: &self.marker_attributes, |
162 | | locale: &self.locale, |
163 | | } |
164 | | } |
165 | | |
166 | | /// Creates a [`DataIdentifierCow`] from an owned [`DataLocale`]. |
167 | | pub fn from_locale(locale: DataLocale) -> Self { |
168 | | Self { |
169 | | marker_attributes: Cow::Borrowed(DataMarkerAttributes::empty()), |
170 | | locale, |
171 | | } |
172 | | } |
173 | | |
174 | | /// Creates a [`DataIdentifierCow`] from a borrowed [`DataMarkerAttributes`]. |
175 | | pub fn from_marker_attributes(marker_attributes: &'a DataMarkerAttributes) -> Self { |
176 | | Self { |
177 | | marker_attributes: Cow::Borrowed(marker_attributes), |
178 | | locale: Default::default(), |
179 | | } |
180 | | } |
181 | | |
182 | | /// Creates a [`DataIdentifierCow`] from an owned [`DataMarkerAttributes`]. |
183 | | pub fn from_marker_attributes_owned(marker_attributes: Box<DataMarkerAttributes>) -> Self { |
184 | | Self { |
185 | | marker_attributes: Cow::Owned(marker_attributes), |
186 | | locale: Default::default(), |
187 | | } |
188 | | } |
189 | | |
190 | | /// Creates a [`DataIdentifierCow`] from an owned [`DataMarkerAttributes`] and an owned [`DataLocale`]. |
191 | | #[cfg(feature = "alloc")] |
192 | | pub fn from_owned(marker_attributes: Box<DataMarkerAttributes>, locale: DataLocale) -> Self { |
193 | | Self { |
194 | | marker_attributes: Cow::Owned(marker_attributes), |
195 | | locale, |
196 | | } |
197 | | } |
198 | | |
199 | | /// Creates a [`DataIdentifierCow`] from a borrowed [`DataMarkerAttributes`] and an owned [`DataLocale`]. |
200 | | pub fn from_borrowed_and_owned( |
201 | | marker_attributes: &'a DataMarkerAttributes, |
202 | | locale: DataLocale, |
203 | | ) -> Self { |
204 | | Self { |
205 | | marker_attributes: Cow::Borrowed(marker_attributes), |
206 | | locale, |
207 | | } |
208 | | } |
209 | | |
210 | | /// Returns whether this id is equal to the default. |
211 | | pub fn is_unknown(&self) -> bool { |
212 | | self.marker_attributes.is_empty() && self.locale.is_unknown() |
213 | | } |
214 | | } |
215 | | |
216 | | #[cfg(feature = "alloc")] |
217 | | impl Default for DataIdentifierCow<'_> { |
218 | | fn default() -> Self { |
219 | | Self { |
220 | | marker_attributes: Cow::Borrowed(Default::default()), |
221 | | locale: Default::default(), |
222 | | } |
223 | | } |
224 | | } |
225 | | |
226 | | /// An additional key to identify data beyond a [`DataLocale`]. |
227 | | /// |
228 | | /// The is a loose wrapper around a string, with semantics defined by each [`DataMarker`](crate::DataMarker). |
229 | | #[derive(PartialEq, Eq, Ord, PartialOrd, Hash)] |
230 | | #[repr(transparent)] |
231 | | pub struct DataMarkerAttributes { |
232 | | // Validated to be non-empty ASCII alphanumeric + hyphen + underscore |
233 | | value: str, |
234 | | } |
235 | | |
236 | | impl Default for &DataMarkerAttributes { |
237 | 0 | fn default() -> Self { |
238 | 0 | DataMarkerAttributes::empty() |
239 | 0 | } |
240 | | } |
241 | | |
242 | | impl Deref for DataMarkerAttributes { |
243 | | type Target = str; |
244 | | #[inline] |
245 | 0 | fn deref(&self) -> &Self::Target { |
246 | 0 | &self.value |
247 | 0 | } |
248 | | } |
249 | | |
250 | | impl Debug for DataMarkerAttributes { |
251 | | #[inline] |
252 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
253 | 0 | self.value.fmt(f) |
254 | 0 | } |
255 | | } |
256 | | |
257 | | /// Invalid character |
258 | | #[derive(Debug)] |
259 | | #[non_exhaustive] |
260 | | pub struct AttributeParseError; |
261 | | |
262 | | impl DataMarkerAttributes { |
263 | | /// Safety-usable invariant: validated bytes are ASCII only |
264 | 0 | const fn validate(s: &[u8]) -> Result<(), AttributeParseError> { |
265 | 0 | let mut i = 0; |
266 | 0 | while i < s.len() { |
267 | | #[allow(clippy::indexing_slicing)] // duh |
268 | 0 | if !matches!(s[i], b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_') { |
269 | 0 | return Err(AttributeParseError); |
270 | 0 | } |
271 | 0 | i += 1; |
272 | | } |
273 | 0 | Ok(()) |
274 | 0 | } |
275 | | |
276 | | /// Creates a borrowed [`DataMarkerAttributes`] from a borrowed string. |
277 | | /// |
278 | | /// Returns an error if the string contains characters other than `[a-zA-Z0-9_\-]`. |
279 | 0 | pub const fn try_from_str(s: &str) -> Result<&Self, AttributeParseError> { |
280 | 0 | Self::try_from_utf8(s.as_bytes()) |
281 | 0 | } |
282 | | |
283 | | /// Attempts to create a borrowed [`DataMarkerAttributes`] from a borrowed UTF-8 encoded byte slice. |
284 | | /// |
285 | | /// # Examples |
286 | | /// |
287 | | /// ``` |
288 | | /// use icu_provider::prelude::*; |
289 | | /// |
290 | | /// let bytes = b"long-meter"; |
291 | | /// let marker = DataMarkerAttributes::try_from_utf8(bytes).unwrap(); |
292 | | /// assert_eq!(marker.to_string(), "long-meter"); |
293 | | /// ``` |
294 | | /// |
295 | | /// # Errors |
296 | | /// |
297 | | /// Returns an error if the byte slice contains code units other than `[a-zA-Z0-9_\-]`. |
298 | 0 | pub const fn try_from_utf8(code_units: &[u8]) -> Result<&Self, AttributeParseError> { |
299 | 0 | let Ok(()) = Self::validate(code_units) else { |
300 | 0 | return Err(AttributeParseError); |
301 | | }; |
302 | | |
303 | | // SAFETY: `validate` requires a UTF-8 subset |
304 | 0 | let s = unsafe { core::str::from_utf8_unchecked(code_units) }; |
305 | | |
306 | | // SAFETY: `Self` has the same layout as `str` |
307 | 0 | Ok(unsafe { &*(s as *const str as *const Self) }) |
308 | 0 | } |
309 | | |
310 | | /// Creates an owned [`DataMarkerAttributes`] from an owned string. |
311 | | /// |
312 | | /// Returns an error if the string contains characters other than `[a-zA-Z0-9_\-]`. |
313 | | #[cfg(feature = "alloc")] |
314 | | pub fn try_from_string(s: String) -> Result<Box<Self>, AttributeParseError> { |
315 | | let Ok(()) = Self::validate(s.as_bytes()) else { |
316 | | return Err(AttributeParseError); |
317 | | }; |
318 | | |
319 | | // SAFETY: `Self` has the same layout as `str` |
320 | | Ok(unsafe { core::mem::transmute::<Box<str>, Box<Self>>(s.into_boxed_str()) }) |
321 | | } |
322 | | |
323 | | /// Creates a borrowed [`DataMarkerAttributes`] from a borrowed string. |
324 | | /// |
325 | | /// Panics if the string contains characters other than `[a-zA-Z0-9_\-]`. |
326 | 0 | pub const fn from_str_or_panic(s: &str) -> &Self { |
327 | 0 | let Ok(r) = Self::try_from_str(s) else { |
328 | 0 | panic!("Invalid marker attribute syntax") |
329 | | }; |
330 | 0 | r |
331 | 0 | } |
332 | | |
333 | | /// Creates an empty [`DataMarkerAttributes`]. |
334 | 0 | pub const fn empty() -> &'static Self { |
335 | | // SAFETY: `Self` has the same layout as `str` |
336 | 0 | unsafe { &*("" as *const str as *const Self) } |
337 | 0 | } |
338 | | |
339 | | /// Returns this [`DataMarkerAttributes`] as a `&str`. |
340 | 0 | pub const fn as_str(&self) -> &str { |
341 | 0 | &self.value |
342 | 0 | } |
343 | | } |
344 | | |
345 | | #[cfg(feature = "alloc")] |
346 | | impl ToOwned for DataMarkerAttributes { |
347 | | type Owned = Box<Self>; |
348 | | fn to_owned(&self) -> Self::Owned { |
349 | | // SAFETY: `Self` has the same layout as `str` |
350 | | unsafe { core::mem::transmute::<Box<str>, Box<Self>>(self.as_str().to_boxed()) } |
351 | | } |
352 | | } |
353 | | |
354 | | #[test] |
355 | | fn test_data_marker_attributes_from_utf8() { |
356 | | let bytes_vec: Vec<&[u8]> = vec![ |
357 | | b"long-meter", |
358 | | b"long", |
359 | | b"meter", |
360 | | b"short-meter-second", |
361 | | b"usd", |
362 | | ]; |
363 | | |
364 | | for bytes in bytes_vec { |
365 | | let marker = DataMarkerAttributes::try_from_utf8(bytes).unwrap(); |
366 | | assert_eq!(marker.to_string().as_bytes(), bytes); |
367 | | } |
368 | | } |