Skip to main content

icu_provider/
error.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
5use crate::log;
6use crate::{marker::DataMarkerId, prelude::*};
7use core::fmt;
8use 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(#[automatically_derived]
impl ::core::clone::Clone for DataErrorKind {
    #[inline]
    fn clone(&self) -> DataErrorKind {
        let _: ::core::clone::AssertParamIsClone<DataMarkerInfo>;
        let _: ::core::clone::AssertParamIsClone<&'static str>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for DataErrorKind { }Copy, #[automatically_derived]
impl ::core::cmp::Eq for DataErrorKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<DataMarkerInfo>;
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
    }
}Eq, #[automatically_derived]
impl ::core::cmp::PartialEq for DataErrorKind {
    #[inline]
    fn eq(&self, other: &DataErrorKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (DataErrorKind::InconsistentData(__self_0),
                    DataErrorKind::InconsistentData(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (DataErrorKind::Downcast(__self_0),
                    DataErrorKind::Downcast(__arg1_0)) => __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () =
    {
        impl ::core::fmt::Display for DataErrorKind {
            fn fmt(&self, formatter: &mut ::core::fmt::Formatter)
                -> ::core::fmt::Result {

                #[allow(unused_variables, unused_assignments)]
                match self {
                    Self::MarkerNotFound => {
                        formatter.write_fmt(format_args!("Missing data for marker"))
                    }
                    Self::IdentifierNotFound => {
                        formatter.write_fmt(format_args!("Missing data for identifier"))
                    }
                    Self::InvalidRequest => {
                        formatter.write_fmt(format_args!("Invalid request"))
                    }
                    Self::InconsistentData(_0) => {
                        formatter.write_fmt(format_args!("The data for two markers is not consistent: {0:?} (were they generated in different datagen invocations?)",
                                _0))
                    }
                    Self::Downcast(_0) => {
                        formatter.write_fmt(format_args!("Downcast: expected {0}, found",
                                _0))
                    }
                    Self::Deserialize => {
                        formatter.write_fmt(format_args!("Deserialize"))
                    }
                    Self::Custom => {
                        formatter.write_fmt(format_args!("Custom"))
                    }
                }
            }
        }
    };Display, #[automatically_derived]
impl ::core::fmt::Debug for DataErrorKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            DataErrorKind::MarkerNotFound =>
                ::core::fmt::Formatter::write_str(f, "MarkerNotFound"),
            DataErrorKind::IdentifierNotFound =>
                ::core::fmt::Formatter::write_str(f, "IdentifierNotFound"),
            DataErrorKind::InvalidRequest =>
                ::core::fmt::Formatter::write_str(f, "InvalidRequest"),
            DataErrorKind::InconsistentData(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "InconsistentData", &__self_0),
            DataErrorKind::Downcast(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Downcast", &__self_0),
            DataErrorKind::Deserialize =>
                ::core::fmt::Formatter::write_str(f, "Deserialize"),
            DataErrorKind::Custom =>
                ::core::fmt::Formatter::write_str(f, "Custom"),
        }
    }
}Debug)]
15#[non_exhaustive]
16pub enum DataErrorKind {
17    /// No data for the requested data marker. This is only returned by [`DynamicDataProvider`].
18    #[displaydoc("Missing data for marker")]
19    MarkerNotFound,
20
21    /// There is data for the data marker, but not for this particular data identifier.
22    #[displaydoc("Missing data for identifier")]
23    IdentifierNotFound,
24
25    /// The request is invalid, such as a request for a singleton marker containing a data identifier.
26    #[displaydoc("Invalid request")]
27    InvalidRequest,
28
29    /// The data for two [`DataMarker`]s is not consistent.
30    #[displaydoc(
31        "The data for two markers is not consistent: {0:?} (were they generated in different datagen invocations?)"
32    )]
33    InconsistentData(DataMarkerInfo),
34
35    /// An error occured during [`Any`](core::any::Any) downcasting.
36    #[displaydoc("Downcast: expected {0}, found")]
37    Downcast(&'static str),
38
39    /// An error occured during [`serde`] deserialization.
40    ///
41    /// Check debug logs for potentially more information.
42    #[displaydoc("Deserialize")]
43    Deserialize,
44
45    /// An unspecified error occurred.
46    ///
47    /// Check debug logs for potentially more information.
48    #[displaydoc("Custom")]
49    Custom,
50
51    /// An error occurred while accessing a system resource.
52    #[displaydoc("I/O: {0:?}")]
53    #[cfg(feature = "std")]
54    Io(std::io::ErrorKind),
55}
56
57/// The error type for ICU4X data provider operations.
58///
59/// To create one of these, either start with a [`DataErrorKind`] or use [`DataError::custom()`].
60///
61/// # Example
62///
63/// Create a [`DataErrorKind::IdentifierNotFound`] error and attach a data request for context:
64///
65/// ```no_run
66/// # use icu_provider::prelude::*;
67/// let marker: DataMarkerInfo = unimplemented!();
68/// let req: DataRequest = unimplemented!();
69/// DataErrorKind::IdentifierNotFound.with_req(marker, req);
70/// ```
71///
72/// Create a named custom error:
73///
74/// ```
75/// # use icu_provider::prelude::*;
76/// DataError::custom("This is an example error");
77/// ```
78#[derive(#[automatically_derived]
impl ::core::clone::Clone for DataError {
    #[inline]
    fn clone(&self) -> DataError {
        let _: ::core::clone::AssertParamIsClone<DataErrorKind>;
        let _: ::core::clone::AssertParamIsClone<Option<DataMarkerId>>;
        let _: ::core::clone::AssertParamIsClone<Option<&'static str>>;
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for DataError { }Copy, #[automatically_derived]
impl ::core::cmp::Eq for DataError {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<DataErrorKind>;
        let _: ::core::cmp::AssertParamIsEq<Option<DataMarkerId>>;
        let _: ::core::cmp::AssertParamIsEq<Option<&'static str>>;
        let _: ::core::cmp::AssertParamIsEq<bool>;
    }
}Eq, #[automatically_derived]
impl ::core::cmp::PartialEq for DataError {
    #[inline]
    fn eq(&self, other: &DataError) -> bool {
        self.silent == other.silent && self.kind == other.kind &&
                self.marker == other.marker &&
            self.str_context == other.str_context
    }
}PartialEq, #[automatically_derived]
impl ::core::fmt::Debug for DataError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f, "DataError",
            "kind", &self.kind, "marker", &self.marker, "str_context",
            &self.str_context, "silent", &&self.silent)
    }
}Debug)]
79#[non_exhaustive]
80pub struct DataError {
81    /// Broad category of the error.
82    pub kind: DataErrorKind,
83
84    /// The data marker of the request, if available.
85    pub marker: Option<DataMarkerId>,
86
87    /// Additional context, if available.
88    pub str_context: Option<&'static str>,
89
90    /// Whether this error was created in silent mode to not log.
91    pub silent: bool,
92}
93
94impl fmt::Display for DataError {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        f.write_fmt(format_args!("ICU4X data error"))write!(f, "ICU4X data error")?;
97        if self.kind != DataErrorKind::Custom {
98            f.write_fmt(format_args!(": {0}", self.kind))write!(f, ": {}", self.kind)?;
99        }
100        if let Some(marker) = self.marker {
101            f.write_fmt(format_args!(" (marker: {0:?})", marker))write!(f, " (marker: {marker:?})")?;
102        }
103        if let Some(str_context) = self.str_context {
104            f.write_fmt(format_args!(": {0}", str_context))write!(f, ": {str_context}")?;
105        }
106        Ok(())
107    }
108}
109
110impl DataErrorKind {
111    /// Converts this [`DataErrorKind`] into a [`DataError`].
112    ///
113    /// If possible, you should attach context using a `with_` function.
114    #[inline]
115    pub const fn into_error(self) -> DataError {
116        DataError {
117            kind: self,
118            marker: None,
119            str_context: None,
120            silent: false,
121        }
122    }
123
124    /// Creates a [`DataError`] with a data marker context.
125    #[inline]
126    pub const fn with_marker(self, marker: DataMarkerInfo) -> DataError {
127        self.into_error().with_marker(marker)
128    }
129
130    /// Creates a [`DataError`] with a string context.
131    #[inline]
132    pub const fn with_str_context(self, context: &'static str) -> DataError {
133        self.into_error().with_str_context(context)
134    }
135
136    /// Creates a [`DataError`] with a type name context.
137    #[inline]
138    pub fn with_type_context<T>(self) -> DataError {
139        self.into_error().with_type_context::<T>()
140    }
141
142    /// Creates a [`DataError`] with a request context.
143    #[inline]
144    pub fn with_req(self, marker: DataMarkerInfo, req: DataRequest) -> DataError {
145        self.into_error().with_req(marker, req)
146    }
147}
148
149impl DataError {
150    /// Returns a new, empty [`DataError`] with kind Custom and a string error message.
151    #[inline]
152    pub const fn custom(str_context: &'static str) -> Self {
153        Self {
154            kind: DataErrorKind::Custom,
155            marker: None,
156            str_context: Some(str_context),
157            silent: false,
158        }
159    }
160
161    /// Sets the data marker of a [`DataError`], returning a modified error.
162    #[inline]
163    pub const fn with_marker(self, marker: DataMarkerInfo) -> Self {
164        Self {
165            kind: self.kind,
166            marker: Some(marker.id),
167            str_context: self.str_context,
168            silent: self.silent,
169        }
170    }
171
172    /// Sets the string context of a [`DataError`], returning a modified error.
173    #[inline]
174    pub const fn with_str_context(self, context: &'static str) -> Self {
175        Self {
176            kind: self.kind,
177            marker: self.marker,
178            str_context: Some(context),
179            silent: self.silent,
180        }
181    }
182
183    /// Sets the string context of a [`DataError`] to the given type name, returning a modified error.
184    #[inline]
185    pub fn with_type_context<T>(self) -> Self {
186        if !self.silent {
187            log::warn!("{self}: Type context: {}", core::any::type_name::<T>());
188        }
189        self.with_str_context(core::any::type_name::<T>())
190    }
191
192    /// Logs the data error with the given request, returning an error containing the data marker.
193    ///
194    /// If the "logging" Cargo feature is enabled, this logs the whole request. Either way,
195    /// it returns an error with the data marker portion of the request as context.
196    pub fn with_req(mut self, marker: DataMarkerInfo, req: DataRequest) -> Self {
197        if req.metadata.silent {
198            self.silent = true;
199        }
200        // Don't write out a log for MissingDataMarker since there is no context to add
201        if !self.silent && self.kind != DataErrorKind::MarkerNotFound {
202            log::warn!("{self} (marker: {marker:?}, request: {})", req.id);
203        }
204        self.with_marker(marker)
205    }
206
207    /// Logs the data error with the given context, then return self.
208    ///
209    /// This does not modify the error, but if the "logging" Cargo feature is enabled,
210    /// it will print out the context.
211    #[cfg(feature = "std")]
212    pub fn with_path_context(self, _path: &std::path::Path) -> Self {
213        if !self.silent {
214            log::warn!("{self} (path: {_path:?})");
215        }
216        self
217    }
218
219    /// Logs the data error with the given context, then return self.
220    ///
221    /// This does not modify the error, but if the "logging" Cargo feature is enabled,
222    /// it will print out the context.
223    #[cfg_attr(not(feature = "logging"), allow(unused_variables))]
224    #[inline]
225    pub fn with_display_context<D: fmt::Display + ?Sized>(self, context: &D) -> Self {
226        if !self.silent {
227            log::warn!("{self}: {context}");
228        }
229        self
230    }
231
232    /// Logs the data error with the given context, then return self.
233    ///
234    /// This does not modify the error, but if the "logging" Cargo feature is enabled,
235    /// it will print out the context.
236    #[cfg_attr(not(feature = "logging"), allow(unused_variables))]
237    #[inline]
238    pub fn with_debug_context<D: fmt::Debug + ?Sized>(self, context: &D) -> Self {
239        if !self.silent {
240            log::warn!("{self}: {context:?}");
241        }
242        self
243    }
244
245    #[inline]
246    pub(crate) fn for_type<T>() -> DataError {
247        DataError {
248            kind: DataErrorKind::Downcast(core::any::type_name::<T>()),
249            marker: None,
250            str_context: None,
251            silent: false,
252        }
253    }
254}
255
256impl core::error::Error for DataError {}
257
258#[cfg(feature = "std")]
259impl From<std::io::Error> for DataError {
260    fn from(e: std::io::Error) -> Self {
261        log::warn!("I/O error: {e}");
262        DataErrorKind::Io(e.kind()).into_error()
263    }
264}
265
266/// Extension trait for `Result<T, DataError>`.
267pub trait ResultDataError<T>: Sized {
268    /// Propagates all errors other than [`DataErrorKind::IdentifierNotFound`], and returns `None` in that case.
269    fn allow_identifier_not_found(self) -> Result<Option<T>, DataError>;
270}
271
272impl<T> ResultDataError<T> for Result<T, DataError> {
273    fn allow_identifier_not_found(self) -> Result<Option<T>, DataError> {
274        match self {
275            Ok(t) => Ok(Some(t)),
276            Err(DataError {
277                kind: DataErrorKind::IdentifierNotFound,
278                ..
279            }) => Ok(None),
280            Err(e) => Err(e),
281        }
282    }
283}