#![deny(missing_docs)]
#![no_std]
#[cfg(any(test, feature = "alloc"))]
extern crate alloc;
pub const MESSAGE_LIMIT: u16 = 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DisplayFullError<'e, E>(pub &'e E)
where
E: ::core::error::Error + ?Sized;
impl<E> ::core::fmt::Display for DisplayFullError<'_, E>
where
E: ::core::error::Error + ?Sized,
{
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
core::fmt::Display::fmt(&self.0, f)?;
let mut printed: u16 = 1;
for e in ::core::iter::successors(self.0.source(), |e| e.source()) {
if printed >= MESSAGE_LIMIT {
f.write_str(": ...")?;
return Ok(());
}
f.write_str(": ")?;
::core::fmt::Display::fmt(e, f)?;
printed = printed.saturating_add(1);
}
Ok(())
}
}
mod private {
pub trait Sealed {}
}
pub trait DisplayFullErrorExt: ::core::error::Error + private::Sealed {
fn display_full(&self) -> DisplayFullError<'_, Self> {
DisplayFullError(self)
}
#[cfg(feature = "alloc")]
fn to_string_full(&self) -> alloc::string::String {
use crate::alloc::string::ToString;
self.display_full().to_string()
}
}
impl<E> private::Sealed for E where E: ::core::error::Error + ?Sized {}
impl<E> DisplayFullErrorExt for E where E: ::core::error::Error + ?Sized {}
#[cfg(test)]
mod tests {
use super::*;
use ::alloc::format;
use ::alloc::string::{String, ToString};
use ::core::{error, fmt};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum UploadError {
Permission(PermissionError),
#[allow(dead_code)]
Limit(LimitError),
}
impl fmt::Display for UploadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("upload failed")
}
}
impl error::Error for UploadError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(match self {
UploadError::Permission(e) => e,
UploadError::Limit(e) => e,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct PermissionError;
impl fmt::Display for PermissionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("permission denied")
}
}
impl error::Error for PermissionError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct LimitError;
impl fmt::Display for LimitError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("upload exceeds max limit")
}
}
impl error::Error for LimitError {}
#[test]
fn error_without_source() {
let input = PermissionError;
let actual: String = input.display_full().to_string();
let expected = String::from("permission denied");
assert_eq!(actual, expected);
}
#[test]
fn error_with_source() {
let input = UploadError::Permission(PermissionError);
let actual: String = input.display_full().to_string();
let expected = String::from("upload failed: permission denied");
assert_eq!(actual, expected);
}
#[test]
fn error_with_cyclic_source_chain() {
#[derive(Debug)]
struct CyclicError;
impl fmt::Display for CyclicError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("cycle detected")
}
}
impl error::Error for CyclicError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(self as &dyn error::Error)
}
}
let input = CyclicError;
let actual: String = input.display_full().to_string();
let expected = format!("{}...", ["cycle detected: "; 1024].join(""));
assert_eq!(actual, expected);
}
}