use crate::buf::BufferFormat;
use crate::prelude::*;
use core::fmt;
use displaydoc::Display;
#[derive(Clone, Copy, Eq, PartialEq, Display, Debug)]
#[non_exhaustive]
pub enum DataErrorKind {
#[displaydoc("Missing data for key")]
MissingDataKey,
#[displaydoc("Missing data for locale")]
MissingLocale,
#[displaydoc("Request needs a locale")]
NeedsLocale,
#[displaydoc("Request has an extraneous locale")]
ExtraneousLocale,
#[displaydoc("Resource blocked by filter")]
FilteredResource,
#[displaydoc("Mismatched types: tried to downcast with {0}, but actual type is different")]
MismatchedType(&'static str),
#[displaydoc("Missing payload")]
MissingPayload,
#[displaydoc("Invalid state")]
InvalidState,
#[displaydoc("Parse error for data key or data locale")]
KeyLocaleSyntax,
#[displaydoc("Custom")]
Custom,
#[displaydoc("I/O error: {0:?}")]
#[cfg(feature = "std")]
Io(std::io::ErrorKind),
#[displaydoc("Missing source data")]
#[cfg(feature = "datagen")]
MissingSourceData,
#[displaydoc("Unavailable buffer format: {0:?} (does icu_provider need to be compiled with an additional Cargo feature?)")]
UnavailableBufferFormat(BufferFormat),
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
#[non_exhaustive]
pub struct DataError {
pub kind: DataErrorKind,
pub key: Option<DataKey>,
pub str_context: Option<&'static str>,
pub silent: bool,
}
impl fmt::Display for DataError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ICU4X data error")?;
if self.kind != DataErrorKind::Custom {
write!(f, ": {}", self.kind)?;
}
if let Some(key) = self.key {
write!(f, " (key: {key})")?;
}
if let Some(str_context) = self.str_context {
write!(f, ": {str_context}")?;
}
Ok(())
}
}
impl DataErrorKind {
#[inline]
pub const fn into_error(self) -> DataError {
DataError {
kind: self,
key: None,
str_context: None,
silent: false,
}
}
#[inline]
pub const fn with_key(self, key: DataKey) -> DataError {
self.into_error().with_key(key)
}
#[inline]
pub const fn with_str_context(self, context: &'static str) -> DataError {
self.into_error().with_str_context(context)
}
#[inline]
pub fn with_type_context<T>(self) -> DataError {
self.into_error().with_type_context::<T>()
}
#[inline]
pub fn with_req(self, key: DataKey, req: DataRequest) -> DataError {
self.into_error().with_req(key, req)
}
}
impl DataError {
#[inline]
pub const fn custom(str_context: &'static str) -> Self {
Self {
kind: DataErrorKind::Custom,
key: None,
str_context: Some(str_context),
silent: false,
}
}
#[inline]
pub const fn with_key(self, key: DataKey) -> Self {
Self {
kind: self.kind,
key: Some(key),
str_context: self.str_context,
silent: self.silent,
}
}
#[inline]
pub const fn with_str_context(self, context: &'static str) -> Self {
Self {
kind: self.kind,
key: self.key,
str_context: Some(context),
silent: self.silent,
}
}
#[inline]
pub fn with_type_context<T>(self) -> Self {
#[cfg(feature = "logging")]
if !self.silent {
log::warn!("{self}: Type context: {}", core::any::type_name::<T>());
}
self.with_str_context(core::any::type_name::<T>())
}
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
pub fn with_req(mut self, key: DataKey, req: DataRequest) -> Self {
if req.metadata.silent {
self.silent = true;
}
#[cfg(feature = "logging")]
if !self.silent && self.kind != DataErrorKind::MissingDataKey {
log::warn!("{} (key: {}, request: {})", self, key, req);
}
self.with_key(key)
}
#[cfg(feature = "std")]
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
pub fn with_path_context<P: AsRef<std::path::Path> + ?Sized>(self, path: &P) -> Self {
#[cfg(feature = "logging")]
if !self.silent {
log::warn!("{} (path: {:?})", self, path.as_ref());
}
self
}
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
#[inline]
pub fn with_display_context<D: fmt::Display + ?Sized>(self, context: &D) -> Self {
#[cfg(feature = "logging")]
if !self.silent {
log::warn!("{}: {}", self, context);
}
self
}
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
#[inline]
pub fn with_debug_context<D: fmt::Debug + ?Sized>(self, context: &D) -> Self {
#[cfg(feature = "logging")]
if !self.silent {
log::warn!("{}: {:?}", self, context);
}
self
}
#[inline]
pub(crate) fn for_type<T>() -> DataError {
DataError {
kind: DataErrorKind::MismatchedType(core::any::type_name::<T>()),
key: None,
str_context: None,
silent: false,
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DataError {}
#[cfg(feature = "std")]
impl From<std::io::Error> for DataError {
fn from(e: std::io::Error) -> Self {
#[cfg(feature = "logging")]
log::warn!("I/O error: {}", e);
DataErrorKind::Io(e.kind()).into_error()
}
}