use core::{
fmt,
fmt::{Debug, Display, Formatter},
};
use pastey::paste;
#[cfg(feature = "alloc")]
use alloc::{string::String, string::ToString};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("infallible error triggered!")]
Infallible(#[from] core::convert::Infallible),
#[error("vec is out of space at {0} entries")]
VecOutOfSpace(usize),
#[error("buffer too small at {got} bytes, need {want} bytes")]
BufferTooSmall {
got: usize,
want: usize,
},
#[error("missing field: {0}")]
MissingField(&'static str),
#[error("bad field: {0}")]
BadField(&'static str),
#[error("unsupported operation: {0:?}")]
Unsupported(Option<&'static str>),
#[error("implementation error: {0}")]
ImplementationError(ErrorMessage),
#[error("CC is truncated: {0}")]
CapabilityContainerTruncated(Truncated),
#[error("TLV block is truncated: {0}")]
BlockTruncated(Truncated),
#[error("CC version {field} field too large, got {got}")]
VersionTooLarge {
field: &'static str,
got: u8,
},
#[error("NDEF record error: {0}")]
RecordError(ErrorMessage),
#[error("empty NDEF record")]
EmptyRecord,
#[error("NDEF record is truncated: {0}")]
RecordTruncated(Truncated),
#[error("NDEF record ID length of {0} bytes is too long")]
RecordIdTooLong(usize),
#[error("NDEF record type length of {0} bytes is too long")]
RecordTyTooLong(usize),
#[error("UTF-8 error in NDEF type field: {0}")]
RecordTyUtf8Error(#[source] core::str::Utf8Error),
#[error("wrong NDEF record iterator called: called {got}, want {want}")]
WrongRecordIter {
got: &'static str,
want: &'static str,
},
#[error("NDEF payload error: {0}")]
PayloadError(ErrorMessage),
#[error("NDEF payload is truncated: {0}")]
PayloadTruncated(Truncated),
#[error("invalid payload for record: {0}")]
PayloadInvalid(&'static str),
#[error("invalid NDEF record TNF for payload: have {got}, want {want}")]
RecordTnfInvalid {
got: crate::tag::message::record::Tnf,
want: crate::tag::message::record::Tnf,
},
#[error("invalid NDEF type for {0}")]
RecordTyInvalidForPayload(&'static str),
#[error("UTF-8 error in URI: {0}")]
UriPayloadUtf8Error(#[source] core::str::Utf8Error),
#[error("invalid URI prefix code: {0}")]
UriPayloadPrefixInvalid(usize),
#[error("the URI prefix was not found in the prefix map: {0}")]
UriPayloadPrefixNotMatched(&'static str),
#[error("unexpected TLV terminator before TLV block")]
TerminatorBeforeBlock,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Truncated {
pub got: usize,
pub want: usize,
}
impl Display for Truncated {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "have {} bytes, want {} bytes", self.got, self.want)
}
}
macro_rules! truncated_error {
($error:ident, $function:ident) => {
impl Error {
#[inline]
pub fn $function(len: usize, want: usize) -> Self {
Self::$error(Truncated { got: len, want })
}
}
};
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ErrorMessage {
Static(&'static str, Option<usize>),
#[cfg(any(feature = "std", feature = "alloc"))]
Owned(String, Option<usize>),
}
impl Display for ErrorMessage {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Static(s, pos) => match pos {
Some(pos) => write!(f, "{} at {} bytes", s, pos),
None => write!(f, "{}", s),
},
#[cfg(any(feature = "std", feature = "alloc"))]
Self::Owned(s, pos) => match pos {
Some(pos) => write!(f, "{} at {} bytes", s, pos),
None => write!(f, "{}", s),
},
}
}
}
macro_rules! declare_error_trait {
($error:ident: $( $super:tt )+ ) => {
pub trait $error: $( $super )+ {
#[inline(always)]
fn custom<E: core::fmt::Display>(e: E) -> Self {
Self::custom_at(e, None)
}
#[inline(always)]
fn custom_at<E: core::fmt::Display>(_e: E, _pos: Option<usize>) -> Self {
unimplemented!()
}
}
impl $error for crate::Error {
fn custom_at<E: core::fmt::Display>(e: E, pos: Option<usize>) -> Self {
#[cfg(not(any(feature = "std", feature = "alloc")))]
return Self::ImplementationError(ErrorMessage::Static(core::any::type_name::<E>(), pos));
#[cfg(any(feature = "std", feature = "alloc"))]
return Self::ImplementationError(ErrorMessage::Owned(e.to_string(), pos));
}
}
paste! {
impl crate::Error {
#[inline]
pub fn [<$error:snake>]<E>(e: E) -> Self
where
E: $error + core::fmt::Display,
{
#[cfg(not(any(feature = "std", feature = "alloc")))]
return Self::$error(ErrorMessage::Static(core::any::type_name::<E>(), None));
#[cfg(any(feature = "std", feature = "alloc"))]
return Self::$error(ErrorMessage::Owned(e.to_string(), None));
}
#[inline]
pub fn [<$error:snake _at>]<E>(e: E, pos: usize) -> Self
where
E: $error + core::fmt::Display,
{
#[cfg(not(any(feature = "std", feature = "alloc")))]
return Self::$error(ErrorMessage::Static(core::any::type_name::<E>(), Some(pos)));
#[cfg(any(feature = "std", feature = "alloc"))]
return Self::$error(ErrorMessage::Owned(e.to_string(), Some(pos)));
}
}
}
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
declare_error_trait!(ImplementationError: core::error::Error + Sized);
#[cfg(not(any(feature = "std", feature = "alloc")))]
declare_error_trait!(ImplementationError: Debug + Display + Sized);
truncated_error!(BlockTruncated, block_truncated);
truncated_error!(RecordTruncated, record_truncated);
truncated_error!(PayloadTruncated, payload_truncated);