]>
Commit | Line | Data |
---|---|---|
487cf647 FG |
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 | use crate::buf::BufferFormat; | |
6 | use crate::prelude::*; | |
7 | use core::fmt; | |
8 | use displaydoc::Display; | |
9 | ||
10 | /// A list specifying general categories of data provider error. | |
11 | /// | |
12 | /// Errors may be caused either by a malformed request or by the data provider | |
13 | /// not being able to fulfill a well-formed request. | |
14 | #[derive(Clone, Copy, Eq, PartialEq, Display, Debug)] | |
15 | #[non_exhaustive] | |
16 | pub enum DataErrorKind { | |
17 | /// No data for the provided resource key. | |
18 | #[displaydoc("Missing data for key")] | |
19 | MissingDataKey, | |
20 | ||
21 | /// There is data for the key, but not for this particular locale. | |
22 | #[displaydoc("Missing data for locale")] | |
23 | MissingLocale, | |
24 | ||
25 | /// The request should include a locale. | |
26 | #[displaydoc("Request needs a locale")] | |
27 | NeedsLocale, | |
28 | ||
29 | /// The request should not contain a locale. | |
30 | #[displaydoc("Request has an extraneous locale")] | |
31 | ExtraneousLocale, | |
32 | ||
33 | /// The resource was blocked by a filter. The resource may or may not be available. | |
34 | #[displaydoc("Resource blocked by filter")] | |
35 | FilteredResource, | |
36 | ||
37 | /// The generic type parameter does not match the TypeId. The expected type name is stored | |
38 | /// as context when this error is returned. | |
39 | #[displaydoc("Mismatched types: tried to downcast with {0}, but actual type is different")] | |
40 | MismatchedType(&'static str), | |
41 | ||
42 | /// The payload is missing. This is usually caused by a previous error. | |
43 | #[displaydoc("Missing payload")] | |
44 | MissingPayload, | |
45 | ||
46 | /// A data provider object was given to an operation in an invalid state. | |
47 | #[displaydoc("Invalid state")] | |
48 | InvalidState, | |
49 | ||
50 | /// An unspecified error occurred, such as a Serde error. | |
51 | /// | |
52 | /// Check debug logs for potentially more information. | |
53 | #[displaydoc("Custom")] | |
54 | Custom, | |
55 | ||
56 | /// An error occurred while accessing a system resource. | |
57 | #[displaydoc("I/O error: {0:?}")] | |
58 | #[cfg(feature = "std")] | |
59 | Io(std::io::ErrorKind), | |
60 | ||
61 | /// An unspecified data source containing the required data is unavailable. | |
62 | #[displaydoc("Missing source data")] | |
63 | #[cfg(feature = "datagen")] | |
64 | MissingSourceData, | |
65 | ||
66 | /// An error indicating that the desired buffer format is not available. This usually | |
9ffffee4 FG |
67 | /// means that a required Cargo feature was not enabled |
68 | #[displaydoc("Unavailable buffer format: {0:?} (does icu_provider need to be compiled with an additional Cargo feature?)")] | |
487cf647 FG |
69 | UnavailableBufferFormat(BufferFormat), |
70 | } | |
71 | ||
72 | /// The error type for ICU4X data provider operations. | |
73 | /// | |
74 | /// To create one of these, either start with a [`DataErrorKind`] or use [`DataError::custom()`]. | |
75 | /// | |
76 | /// # Example | |
77 | /// | |
78 | /// Create a NeedsLocale error and attach a data request for context: | |
79 | /// | |
80 | /// ```no_run | |
81 | /// # use icu_provider::prelude::*; | |
82 | /// let key: DataKey = unimplemented!(); | |
83 | /// let req: DataRequest = unimplemented!(); | |
84 | /// DataErrorKind::NeedsLocale.with_req(key, req); | |
85 | /// ``` | |
86 | /// | |
87 | /// Create a named custom error: | |
88 | /// | |
89 | /// ``` | |
90 | /// # use icu_provider::prelude::*; | |
91 | /// DataError::custom("This is an example error"); | |
92 | /// ``` | |
93 | #[derive(Clone, Copy, Eq, PartialEq, Debug)] | |
94 | #[non_exhaustive] | |
95 | pub struct DataError { | |
96 | /// Broad category of the error. | |
97 | pub kind: DataErrorKind, | |
98 | ||
99 | /// The data key of the request, if available. | |
100 | pub key: Option<DataKey>, | |
101 | ||
102 | /// Additional context, if available. | |
103 | pub str_context: Option<&'static str>, | |
104 | } | |
105 | ||
106 | impl fmt::Display for DataError { | |
107 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
108 | write!(f, "ICU4X data error")?; | |
109 | if self.kind != DataErrorKind::Custom { | |
110 | write!(f, ": {}", self.kind)?; | |
111 | } | |
112 | if let Some(key) = self.key { | |
113 | write!(f, " (key: {})", key)?; | |
114 | } | |
115 | if let Some(str_context) = self.str_context { | |
116 | write!(f, ": {}", str_context)?; | |
117 | } | |
118 | Ok(()) | |
119 | } | |
120 | } | |
121 | ||
122 | impl DataErrorKind { | |
123 | /// Converts this DataErrorKind into a DataError. | |
124 | /// | |
125 | /// If possible, you should attach context using a `with_` function. | |
126 | #[inline] | |
127 | pub const fn into_error(self) -> DataError { | |
128 | DataError { | |
129 | kind: self, | |
130 | key: None, | |
131 | str_context: None, | |
132 | } | |
133 | } | |
134 | ||
135 | /// Creates a DataError with a resource key context. | |
136 | #[inline] | |
137 | pub const fn with_key(self, key: DataKey) -> DataError { | |
138 | self.into_error().with_key(key) | |
139 | } | |
140 | ||
141 | /// Creates a DataError with a string context. | |
142 | #[inline] | |
143 | pub const fn with_str_context(self, context: &'static str) -> DataError { | |
144 | self.into_error().with_str_context(context) | |
145 | } | |
146 | ||
147 | /// Creates a DataError with a type name context. | |
148 | #[inline] | |
149 | pub fn with_type_context<T>(self) -> DataError { | |
150 | self.into_error().with_type_context::<T>() | |
151 | } | |
152 | ||
153 | /// Creates a DataError with a request context. | |
154 | #[inline] | |
155 | pub fn with_req(self, key: DataKey, req: DataRequest) -> DataError { | |
156 | self.into_error().with_req(key, req) | |
157 | } | |
158 | } | |
159 | ||
160 | impl DataError { | |
161 | /// Returns a new, empty DataError with kind Custom and a string error message. | |
162 | #[inline] | |
163 | pub const fn custom(str_context: &'static str) -> Self { | |
164 | Self { | |
165 | kind: DataErrorKind::Custom, | |
166 | key: None, | |
167 | str_context: Some(str_context), | |
168 | } | |
169 | } | |
170 | ||
171 | /// Sets the resource key of a DataError, returning a modified error. | |
172 | #[inline] | |
173 | pub const fn with_key(self, key: DataKey) -> Self { | |
174 | Self { | |
175 | kind: self.kind, | |
176 | key: Some(key), | |
177 | str_context: self.str_context, | |
178 | } | |
179 | } | |
180 | ||
181 | /// Sets the string context of a DataError, returning a modified error. | |
182 | #[inline] | |
183 | pub const fn with_str_context(self, context: &'static str) -> Self { | |
184 | Self { | |
185 | kind: self.kind, | |
186 | key: self.key, | |
187 | str_context: Some(context), | |
188 | } | |
189 | } | |
190 | ||
191 | /// Sets the string context of a DataError to the given type name, returning a modified error. | |
192 | #[inline] | |
193 | pub fn with_type_context<T>(self) -> Self { | |
194 | self.with_str_context(core::any::type_name::<T>()) | |
195 | } | |
196 | ||
197 | /// Logs the data error with the given request, returning an error containing the resource key. | |
198 | /// | |
9ffffee4 | 199 | /// If the "log_error_context" Cargo feature is enabled, this logs the whole request. Either way, |
487cf647 FG |
200 | /// it returns an error with the resource key portion of the request as context. |
201 | #[cfg_attr(not(feature = "log_error_context"), allow(unused_variables))] | |
202 | pub fn with_req(self, key: DataKey, req: DataRequest) -> Self { | |
203 | // Don't write out a log for MissingDataKey since there is no context to add | |
204 | #[cfg(feature = "log_error_context")] | |
205 | if self.kind != DataErrorKind::MissingDataKey { | |
206 | log::warn!("{} (key: {}, request: {})", self, key, req); | |
207 | } | |
208 | self.with_key(key) | |
209 | } | |
210 | ||
211 | /// Logs the data error with the given context, then return self. | |
212 | /// | |
9ffffee4 | 213 | /// This does not modify the error, but if the "log_error_context" Cargo feature is enabled, |
487cf647 FG |
214 | /// it will print out the context. |
215 | #[cfg(feature = "std")] | |
216 | #[cfg_attr(not(feature = "log_error_context"), allow(unused_variables))] | |
217 | pub fn with_path_context<P: AsRef<std::path::Path> + ?Sized>(self, path: &P) -> Self { | |
218 | #[cfg(feature = "log_error_context")] | |
219 | log::warn!("{} (path: {:?})", self, path.as_ref()); | |
220 | self | |
221 | } | |
222 | ||
223 | /// Logs the data error with the given context, then return self. | |
224 | /// | |
9ffffee4 | 225 | /// This does not modify the error, but if the "log_error_context" Cargo feature is enabled, |
487cf647 FG |
226 | /// it will print out the context. |
227 | #[cfg_attr(not(feature = "log_error_context"), allow(unused_variables))] | |
228 | #[inline] | |
229 | pub fn with_display_context<D: fmt::Display + ?Sized>(self, context: &D) -> Self { | |
230 | #[cfg(feature = "log_error_context")] | |
231 | log::warn!("{}: {}", self, context); | |
232 | self | |
233 | } | |
234 | ||
235 | /// Logs the data error with the given context, then return self. | |
236 | /// | |
9ffffee4 | 237 | /// This does not modify the error, but if the "log_error_context" Cargo feature is enabled, |
487cf647 FG |
238 | /// it will print out the context. |
239 | #[cfg_attr(not(feature = "log_error_context"), allow(unused_variables))] | |
240 | #[inline] | |
241 | pub fn with_debug_context<D: fmt::Debug + ?Sized>(self, context: &D) -> Self { | |
242 | #[cfg(feature = "log_error_context")] | |
243 | log::warn!("{}: {:?}", self, context); | |
244 | self | |
245 | } | |
246 | ||
247 | #[inline] | |
248 | pub(crate) fn for_type<T>() -> DataError { | |
249 | DataError { | |
250 | kind: DataErrorKind::MismatchedType(core::any::type_name::<T>()), | |
251 | key: None, | |
252 | str_context: None, | |
253 | } | |
254 | } | |
255 | } | |
256 | ||
257 | #[cfg(feature = "std")] | |
258 | impl std::error::Error for DataError {} | |
259 | ||
260 | #[cfg(feature = "std")] | |
261 | impl From<std::io::Error> for DataError { | |
262 | fn from(e: std::io::Error) -> Self { | |
263 | #[cfg(feature = "log_error_context")] | |
264 | log::warn!("I/O error: {}", e); | |
265 | DataErrorKind::Io(e.kind()).into_error() | |
266 | } | |
267 | } |