#[cfg(not(feature = "std"))]
use alloc::{
borrow::Cow,
string::{String, ToString},
vec::Vec,
};
use core::{error, fmt};
#[cfg(feature = "std")]
use std::borrow::Cow;
pub type Result<T> = core::result::Result<T, Error>;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ErrorKind {
InvalidPath,
InvalidPayload,
InvalidVersion,
InvalidKeyData,
InvalidDerivation,
}
pub struct ErrorSource(anyhow::Error);
impl ErrorSource {
pub(crate) fn as_error(&self) -> &(dyn error::Error + 'static) {
self.0.as_ref()
}
}
impl fmt::Debug for ErrorSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for ErrorSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl From<String> for ErrorSource {
fn from(message: String) -> Self {
ErrorSource(anyhow::Error::msg(message))
}
}
impl From<&'static str> for ErrorSource {
fn from(message: &'static str) -> Self {
ErrorSource(anyhow::Error::msg(message))
}
}
pub trait IntoErrorSource {
fn into_error_source(self) -> ErrorSource;
}
impl IntoErrorSource for ErrorSource {
fn into_error_source(self) -> ErrorSource {
self
}
}
impl<E> IntoErrorSource for E
where
E: Into<anyhow::Error>,
{
fn into_error_source(self) -> ErrorSource {
ErrorSource(self.into())
}
}
pub struct Error {
kind: ErrorKind,
message: Cow<'static, str>,
context: Vec<(&'static str, String)>,
source: Option<ErrorSource>,
}
impl Error {
pub fn new(kind: ErrorKind, message: impl Into<Cow<'static, str>>) -> Self {
Self { kind, message: message.into(), context: Vec::new(), source: None }
}
pub const fn kind(&self) -> ErrorKind {
self.kind
}
pub fn message(&self) -> &str {
&self.message
}
pub fn context(&self) -> &[(&'static str, String)] {
&self.context
}
pub fn with_context(mut self, key: &'static str, value: impl ToString) -> Self {
self.context.push((key, value.to_string()));
self
}
pub fn set_source(mut self, source: impl IntoErrorSource) -> Self {
debug_assert!(self.source.is_none(), "source already set");
self.source = Some(source.into_error_source());
self
}
pub fn source(&self) -> Option<&ErrorSource> {
self.source.as_ref()
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorKind::InvalidPath => f.write_str("InvalidPath"),
ErrorKind::InvalidPayload => f.write_str("InvalidPayload"),
ErrorKind::InvalidVersion => f.write_str("InvalidVersion"),
ErrorKind::InvalidKeyData => f.write_str("InvalidKeyData"),
ErrorKind::InvalidDerivation => f.write_str("InvalidDerivation"),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.kind, self.message)?;
if !self.context.is_empty() {
f.write_str(" { ")?;
for (index, (key, value)) in self.context.iter().enumerate() {
if index > 0 {
f.write_str(", ")?;
}
write!(f, "{key}={value}")?;
}
f.write_str(" }")?;
}
Ok(())
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}: {}", self.kind, self.message)?;
if !self.context.is_empty() {
writeln!(f, "Context:")?;
for (key, value) in &self.context {
writeln!(f, " {key}: {value}")?;
}
}
if let Some(source) = &self.source {
writeln!(f, "Source: {source}")?;
}
Ok(())
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.source.as_ref().map(|err| err.0.as_ref())
}
}