1use crate::log;
6use crate::{marker::DataMarkerId, prelude::*};
7use core::fmt;
8use displaydoc::Display;
9
10#[derive(Clone, Copy, Eq, PartialEq, Display, Debug)]
15#[non_exhaustive]
16pub enum DataErrorKind {
17 #[displaydoc("Missing data for marker")]
19 MarkerNotFound,
20
21 #[displaydoc("Missing data for identifier")]
23 IdentifierNotFound,
24
25 #[displaydoc("Invalid request")]
27 InvalidRequest,
28
29 #[displaydoc("The data for two markers is not consistent: {0:?} (were they generated in different datagen invocations?)")]
31 InconsistentData(DataMarkerInfo),
32
33 #[displaydoc("Downcast: expected {0}, found")]
35 Downcast(&'static str),
36
37 #[displaydoc("Deserialize")]
41 Deserialize,
42
43 #[displaydoc("Custom")]
47 Custom,
48
49 #[displaydoc("I/O: {0:?}")]
51 #[cfg(feature = "std")]
52 Io(std::io::ErrorKind),
53}
54
55#[derive(Clone, Copy, Eq, PartialEq, Debug)]
77#[non_exhaustive]
78pub struct DataError {
79 pub kind: DataErrorKind,
81
82 pub marker: Option<DataMarkerId>,
84
85 pub str_context: Option<&'static str>,
87
88 pub silent: bool,
90}
91
92impl fmt::Display for DataError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "ICU4X data error")?;
95 if self.kind != DataErrorKind::Custom {
96 write!(f, ": {}", self.kind)?;
97 }
98 if let Some(marker) = self.marker {
99 write!(f, " (marker: {marker:?})")?;
100 }
101 if let Some(str_context) = self.str_context {
102 write!(f, ": {str_context}")?;
103 }
104 Ok(())
105 }
106}
107
108impl DataErrorKind {
109 #[inline]
113 pub const fn into_error(self) -> DataError {
114 DataError {
115 kind: self,
116 marker: None,
117 str_context: None,
118 silent: false,
119 }
120 }
121
122 #[inline]
124 pub const fn with_marker(self, marker: DataMarkerInfo) -> DataError {
125 self.into_error().with_marker(marker)
126 }
127
128 #[inline]
130 pub const fn with_str_context(self, context: &'static str) -> DataError {
131 self.into_error().with_str_context(context)
132 }
133
134 #[inline]
136 pub fn with_type_context<T>(self) -> DataError {
137 self.into_error().with_type_context::<T>()
138 }
139
140 #[inline]
142 pub fn with_req(self, marker: DataMarkerInfo, req: DataRequest) -> DataError {
143 self.into_error().with_req(marker, req)
144 }
145}
146
147impl DataError {
148 #[inline]
150 pub const fn custom(str_context: &'static str) -> Self {
151 Self {
152 kind: DataErrorKind::Custom,
153 marker: None,
154 str_context: Some(str_context),
155 silent: false,
156 }
157 }
158
159 #[inline]
161 pub const fn with_marker(self, marker: DataMarkerInfo) -> Self {
162 Self {
163 kind: self.kind,
164 marker: Some(marker.id),
165 str_context: self.str_context,
166 silent: self.silent,
167 }
168 }
169
170 #[inline]
172 pub const fn with_str_context(self, context: &'static str) -> Self {
173 Self {
174 kind: self.kind,
175 marker: self.marker,
176 str_context: Some(context),
177 silent: self.silent,
178 }
179 }
180
181 #[inline]
183 pub fn with_type_context<T>(self) -> Self {
184 if !self.silent {
185 log::warn!("{self}: Type context: {}", core::any::type_name::<T>());
186 }
187 self.with_str_context(core::any::type_name::<T>())
188 }
189
190 pub fn with_req(mut self, marker: DataMarkerInfo, req: DataRequest) -> Self {
195 if req.metadata.silent {
196 self.silent = true;
197 }
198 if !self.silent && self.kind != DataErrorKind::MarkerNotFound {
200 log::warn!("{self} (marker: {marker:?}, request: {})", req.id);
201 }
202 self.with_marker(marker)
203 }
204
205 #[cfg(feature = "std")]
210 pub fn with_path_context(self, _path: &std::path::Path) -> Self {
211 if !self.silent {
212 log::warn!("{self} (path: {_path:?})");
213 }
214 self
215 }
216
217 #[cfg_attr(not(feature = "logging"), allow(unused_variables))]
222 #[inline]
223 pub fn with_display_context<D: fmt::Display + ?Sized>(self, context: &D) -> Self {
224 if !self.silent {
225 log::warn!("{}: {}", self, context);
226 }
227 self
228 }
229
230 #[cfg_attr(not(feature = "logging"), allow(unused_variables))]
235 #[inline]
236 pub fn with_debug_context<D: fmt::Debug + ?Sized>(self, context: &D) -> Self {
237 if !self.silent {
238 log::warn!("{}: {:?}", self, context);
239 }
240 self
241 }
242
243 #[inline]
244 pub(crate) fn for_type<T>() -> DataError {
245 DataError {
246 kind: DataErrorKind::Downcast(core::any::type_name::<T>()),
247 marker: None,
248 str_context: None,
249 silent: false,
250 }
251 }
252}
253
254impl core::error::Error for DataError {}
255
256#[cfg(feature = "std")]
257impl From<std::io::Error> for DataError {
258 fn from(e: std::io::Error) -> Self {
259 log::warn!("I/O error: {}", e);
260 DataErrorKind::Io(e.kind()).into_error()
261 }
262}
263
264pub trait ResultDataError<T>: Sized {
266 fn allow_identifier_not_found(self) -> Result<Option<T>, DataError>;
268}
269
270impl<T> ResultDataError<T> for Result<T, DataError> {
271 fn allow_identifier_not_found(self) -> Result<Option<T>, DataError> {
272 match self {
273 Ok(t) => Ok(Some(t)),
274 Err(DataError {
275 kind: DataErrorKind::IdentifierNotFound,
276 ..
277 }) => Ok(None),
278 Err(e) => Err(e),
279 }
280 }
281}