mirror of
https://github.com/eyre-rs/eyre.git
synced 2025-10-02 23:36:11 +00:00
make an effort at no_std support
This commit is contained in:
parent
6c1a9af5b8
commit
11757c1da5
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
- 1.31.0
|
- 1.34.0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
@ -29,7 +29,7 @@ jobs:
|
|||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
- nightly
|
- nightly
|
||||||
- 1.31.0
|
- 1.34.0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
@ -39,10 +39,10 @@ jobs:
|
|||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
- uses: actions-rs/cargo@v1
|
# - uses: actions-rs/cargo@v1
|
||||||
with:
|
# with:
|
||||||
command: test
|
# command: test
|
||||||
args: --no-default-features
|
# args: --no-default-features
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: Rustfmt
|
name: Rustfmt
|
||||||
@ -51,7 +51,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
- 1.31.0
|
- 1.34.0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
@ -71,7 +71,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
- 1.31.0
|
- 1.34.0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
|
@ -4,20 +4,6 @@ pub(crate) use std::backtrace::Backtrace;
|
|||||||
#[cfg(not(backtrace))]
|
#[cfg(not(backtrace))]
|
||||||
pub(crate) enum Backtrace {}
|
pub(crate) enum Backtrace {}
|
||||||
|
|
||||||
// #[cfg(backtrace)]
|
|
||||||
// macro_rules! backtrace {
|
|
||||||
// () => {
|
|
||||||
// Some(Backtrace::capture())
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[cfg(not(backtrace))]
|
|
||||||
macro_rules! backtrace {
|
|
||||||
() => {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(backtrace)]
|
#[cfg(backtrace)]
|
||||||
macro_rules! backtrace_if_absent {
|
macro_rules! backtrace_if_absent {
|
||||||
($err:expr) => {
|
($err:expr) => {
|
||||||
@ -28,7 +14,7 @@ macro_rules! backtrace_if_absent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(backtrace)))]
|
#[cfg(not(backtrace))]
|
||||||
macro_rules! backtrace_if_absent {
|
macro_rules! backtrace_if_absent {
|
||||||
($err:expr) => {
|
($err:expr) => {
|
||||||
None
|
None
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::error::ContextError;
|
use crate::error::ContextError;
|
||||||
use crate::{WrapErr, ErrReport, StdError, EyreContext};
|
use crate::{ErrReport, EyreContext, StdError, WrapErr};
|
||||||
use core::fmt::{self, Debug, Display, Write};
|
use core::fmt::{self, Debug, Display, Write};
|
||||||
|
|
||||||
#[cfg(backtrace)]
|
#[cfg(backtrace)]
|
||||||
|
37
src/error.rs
37
src/error.rs
@ -1,13 +1,15 @@
|
|||||||
use crate::alloc::Box;
|
use crate::alloc::Box;
|
||||||
use std::any::Any;
|
|
||||||
use crate::backtrace::Backtrace;
|
|
||||||
use crate::chain::Chain;
|
use crate::chain::Chain;
|
||||||
|
use crate::EyreContext;
|
||||||
use crate::{ErrReport, StdError};
|
use crate::{ErrReport, StdError};
|
||||||
|
use core::any::Any;
|
||||||
use core::any::TypeId;
|
use core::any::TypeId;
|
||||||
use core::fmt::{self, Debug, Display};
|
use core::fmt::{self, Debug, Display};
|
||||||
use core::mem::{self, ManuallyDrop};
|
use core::mem::{self, ManuallyDrop};
|
||||||
use core::ptr::{self, NonNull};
|
use core::ptr::{self, NonNull};
|
||||||
use crate::EyreContext;
|
|
||||||
|
#[cfg(backtrace)]
|
||||||
|
use crate::backtrace::Backtrace;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
@ -138,9 +140,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub(crate) fn from_boxed(
|
pub(crate) fn from_boxed(error: Box<dyn StdError + Send + Sync>) -> Self {
|
||||||
error: Box<dyn StdError + Send + Sync>,
|
|
||||||
) -> Self {
|
|
||||||
use crate::wrapper::BoxedError;
|
use crate::wrapper::BoxedError;
|
||||||
let error = BoxedError(error);
|
let error = BoxedError(error);
|
||||||
let vtable = &ErrorVTable {
|
let vtable = &ErrorVTable {
|
||||||
@ -163,10 +163,7 @@ where
|
|||||||
//
|
//
|
||||||
// Unsafe because the given vtable must have sensible behavior on the error
|
// Unsafe because the given vtable must have sensible behavior on the error
|
||||||
// value of type E.
|
// value of type E.
|
||||||
unsafe fn construct<E>(
|
unsafe fn construct<E>(error: E, vtable: &'static ErrorVTable<C>) -> Self
|
||||||
error: E,
|
|
||||||
vtable: &'static ErrorVTable<C>,
|
|
||||||
) -> Self
|
|
||||||
where
|
where
|
||||||
E: StdError + Send + Sync + 'static,
|
E: StdError + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
@ -245,10 +242,7 @@ where
|
|||||||
where
|
where
|
||||||
D: Display + Send + Sync + 'static,
|
D: Display + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
let error: ContextError<D, ErrReport<C>> = ContextError {
|
let error: ContextError<D, ErrReport<C>> = ContextError { msg, error: self };
|
||||||
msg,
|
|
||||||
error: self,
|
|
||||||
};
|
|
||||||
|
|
||||||
let vtable = &ErrorVTable {
|
let vtable = &ErrorVTable {
|
||||||
object_drop: object_drop::<ContextError<D, ErrReport<C>>, C>,
|
object_drop: object_drop::<ContextError<D, ErrReport<C>>, C>,
|
||||||
@ -431,6 +425,10 @@ where
|
|||||||
Some(&mut *addr.cast::<E>().as_ptr())
|
Some(&mut *addr.cast::<E>().as_ptr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn context<T: Any>(&self) -> Option<&T> {
|
||||||
|
self.inner.context()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -627,12 +625,14 @@ where
|
|||||||
D: 'static,
|
D: 'static,
|
||||||
{
|
{
|
||||||
if TypeId::of::<D>() == target {
|
if TypeId::of::<D>() == target {
|
||||||
let unerased = e as *const ErrorImpl<(), C> as *const ErrorImpl<ContextError<D, ErrReport<C>>, C>;
|
let unerased =
|
||||||
|
e as *const ErrorImpl<(), C> as *const ErrorImpl<ContextError<D, ErrReport<C>>, C>;
|
||||||
let addr = &(*unerased)._object.msg as *const D as *mut ();
|
let addr = &(*unerased)._object.msg as *const D as *mut ();
|
||||||
Some(NonNull::new_unchecked(addr))
|
Some(NonNull::new_unchecked(addr))
|
||||||
} else {
|
} else {
|
||||||
// Recurse down the context chain per the inner error's vtable.
|
// Recurse down the context chain per the inner error's vtable.
|
||||||
let unerased = e as *const ErrorImpl<(), C> as *const ErrorImpl<ContextError<D, ErrReport<C>>, C>;
|
let unerased =
|
||||||
|
e as *const ErrorImpl<(), C> as *const ErrorImpl<ContextError<D, ErrReport<C>>, C>;
|
||||||
let source = &(*unerased)._object.error;
|
let source = &(*unerased)._object.error;
|
||||||
(source.inner.vtable.object_downcast)(&source.inner, target)
|
(source.inner.vtable.object_downcast)(&source.inner, target)
|
||||||
}
|
}
|
||||||
@ -717,9 +717,10 @@ where
|
|||||||
unsafe { &mut *(self.vtable.object_mut)(self) }
|
unsafe { &mut *(self.vtable.object_mut)(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn context<T: Any>(&self) -> Option<&T> {
|
pub fn context<T: Any>(&self) -> Option<&T> {
|
||||||
self.context.context_raw(TypeId::of::<T>())?.downcast_ref::<T>()
|
self.context
|
||||||
|
.context_raw(TypeId::of::<T>())?
|
||||||
|
.downcast_ref::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(backtrace)]
|
#[cfg(backtrace)]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::error::ErrorImpl;
|
use crate::error::ErrorImpl;
|
||||||
use core::fmt::{self, Write};
|
|
||||||
use crate::EyreContext;
|
use crate::EyreContext;
|
||||||
|
use core::fmt::{self, Write};
|
||||||
|
|
||||||
impl<C> ErrorImpl<(), C>
|
impl<C> ErrorImpl<(), C>
|
||||||
where
|
where
|
||||||
@ -52,6 +52,7 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::alloc::String;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_digit() {
|
fn one_digit() {
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
// let error = $msg;
|
// let error = $msg;
|
||||||
// (&error).eyre_kind().new(error)
|
// (&error).eyre_kind().new(error)
|
||||||
|
|
||||||
use crate::{EyreContext, ErrReport};
|
use crate::{ErrReport, EyreContext};
|
||||||
use core::fmt::{Debug, Display};
|
use core::fmt::{Debug, Display};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
59
src/lib.rs
59
src/lib.rs
@ -195,6 +195,12 @@ mod alloc {
|
|||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use std::boxed::Box;
|
pub use std::boxed::Box;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
pub use alloc::string::String;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use std::string::String;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -208,12 +214,11 @@ mod macros;
|
|||||||
mod wrapper;
|
mod wrapper;
|
||||||
|
|
||||||
use crate::alloc::Box;
|
use crate::alloc::Box;
|
||||||
|
use crate::backtrace::Backtrace;
|
||||||
use crate::error::ErrorImpl;
|
use crate::error::ErrorImpl;
|
||||||
|
use core::any::{Any, TypeId};
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
use core::mem::ManuallyDrop;
|
use core::mem::ManuallyDrop;
|
||||||
use std::backtrace::Backtrace;
|
|
||||||
use std::any::{Any, TypeId};
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
@ -222,7 +227,7 @@ use core::fmt::Debug;
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
trait StdError: Debug + Display {
|
pub trait StdError: Debug + Display {
|
||||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -330,26 +335,32 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait EyreContext: Sized + Send + Sync + 'static {
|
pub trait EyreContext: Sized + Send + Sync + 'static {
|
||||||
fn default(err: &(dyn std::error::Error + 'static)) -> Self;
|
fn default(err: &(dyn StdError + 'static)) -> Self;
|
||||||
|
|
||||||
fn context_raw(&self, typeid: TypeId) -> Option<&dyn Any>;
|
fn context_raw(&self, typeid: TypeId) -> Option<&dyn Any>;
|
||||||
|
|
||||||
fn display(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result;
|
fn display(
|
||||||
|
&self,
|
||||||
|
error: &(dyn StdError + 'static),
|
||||||
|
f: &mut core::fmt::Formatter<'_>,
|
||||||
|
) -> core::fmt::Result;
|
||||||
|
|
||||||
fn debug(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result;
|
fn debug(
|
||||||
|
&self,
|
||||||
|
error: &(dyn StdError + 'static),
|
||||||
|
f: &mut core::fmt::Formatter<'_>,
|
||||||
|
) -> core::fmt::Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DefaultContext {
|
pub struct DefaultContext {
|
||||||
pub backtrace: Option<Backtrace>,
|
backtrace: Option<Backtrace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EyreContext for DefaultContext {
|
impl EyreContext for DefaultContext {
|
||||||
fn default(error: &(dyn std::error::Error + 'static)) -> Self {
|
fn default(_error: &(dyn StdError + 'static)) -> Self {
|
||||||
let backtrace = backtrace_if_absent!(error);
|
let backtrace = backtrace_if_absent!(error);
|
||||||
|
|
||||||
Self {
|
Self { backtrace }
|
||||||
backtrace
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn context_raw(&self, typid: TypeId) -> Option<&dyn Any> {
|
fn context_raw(&self, typid: TypeId) -> Option<&dyn Any> {
|
||||||
@ -359,11 +370,15 @@ impl EyreContext for DefaultContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn display(
|
||||||
|
&self,
|
||||||
|
error: &(dyn StdError + 'static),
|
||||||
|
f: &mut core::fmt::Formatter<'_>,
|
||||||
|
) -> core::fmt::Result {
|
||||||
write!(f, "{}", error)?;
|
write!(f, "{}", error)?;
|
||||||
|
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
for cause in Chain::new(error).skip(1) {
|
for cause in crate::chain::Chain::new(error).skip(1) {
|
||||||
write!(f, ": {}", cause)?;
|
write!(f, ": {}", cause)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -371,7 +386,11 @@ impl EyreContext for DefaultContext {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn debug(
|
||||||
|
&self,
|
||||||
|
error: &(dyn StdError + 'static),
|
||||||
|
f: &mut core::fmt::Formatter<'_>,
|
||||||
|
) -> core::fmt::Result {
|
||||||
use core::fmt::Write as _;
|
use core::fmt::Write as _;
|
||||||
|
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
@ -383,7 +402,7 @@ impl EyreContext for DefaultContext {
|
|||||||
if let Some(cause) = error.source() {
|
if let Some(cause) = error.source() {
|
||||||
write!(f, "\n\nCaused by:")?;
|
write!(f, "\n\nCaused by:")?;
|
||||||
let multiple = cause.source().is_some();
|
let multiple = cause.source().is_some();
|
||||||
for (n, error) in Chain::new(cause).enumerate() {
|
for (n, error) in crate::chain::Chain::new(cause).enumerate() {
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
let mut indented = fmt::Indented {
|
let mut indented = fmt::Indented {
|
||||||
inner: f,
|
inner: f,
|
||||||
@ -398,7 +417,11 @@ impl EyreContext for DefaultContext {
|
|||||||
{
|
{
|
||||||
use std::backtrace::BacktraceStatus;
|
use std::backtrace::BacktraceStatus;
|
||||||
|
|
||||||
let backtrace = self.backtrace.as_ref().or_else(|| error.backtrace()).expect("backtrace capture failed");
|
let backtrace = self
|
||||||
|
.backtrace
|
||||||
|
.as_ref()
|
||||||
|
.or_else(|| error.backtrace())
|
||||||
|
.expect("backtrace capture failed");
|
||||||
if let BacktraceStatus::Captured = backtrace.status() {
|
if let BacktraceStatus::Captured = backtrace.status() {
|
||||||
let mut backtrace = backtrace.to_string();
|
let mut backtrace = backtrace.to_string();
|
||||||
if backtrace.starts_with("stack backtrace:") {
|
if backtrace.starts_with("stack backtrace:") {
|
||||||
@ -650,7 +673,7 @@ where
|
|||||||
// Not public API. Referenced by macro-generated code.
|
// Not public API. Referenced by macro-generated code.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod private {
|
pub mod private {
|
||||||
use crate::{EyreContext, ErrReport};
|
use crate::{ErrReport, EyreContext};
|
||||||
use core::fmt::{Debug, Display};
|
use core::fmt::{Debug, Display};
|
||||||
|
|
||||||
// #[cfg(backtrace)]
|
// #[cfg(backtrace)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use eyre::{ErrReport, eyre};
|
use eyre::{eyre, ErrReport};
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::io;
|
use std::io;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
mod drop;
|
mod drop;
|
||||||
|
|
||||||
use crate::drop::{DetectDrop, Flag};
|
use crate::drop::{DetectDrop, Flag};
|
||||||
use eyre::{WrapErr, ErrReport, Result};
|
use eyre::{ErrReport, Result, WrapErr};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use eyre::{bail, WrapErr, Result};
|
use eyre::{bail, Result, WrapErr};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
fn f() -> Result<()> {
|
fn f() -> Result<()> {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use self::common::*;
|
use self::common::*;
|
||||||
use eyre::{Result, ensure};
|
use eyre::{ensure, Result};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_messages() {
|
fn test_messages() {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
mod drop;
|
mod drop;
|
||||||
|
|
||||||
use self::drop::{DetectDrop, Flag};
|
use self::drop::{DetectDrop, Flag};
|
||||||
use eyre::ErrReport;
|
|
||||||
use eyre::DefaultContext;
|
use eyre::DefaultContext;
|
||||||
|
use eyre::ErrReport;
|
||||||
use std::marker::Unpin;
|
use std::marker::Unpin;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
@ -13,7 +13,10 @@ fn test_error_size() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_null_pointer_optimization() {
|
fn test_null_pointer_optimization() {
|
||||||
assert_eq!(mem::size_of::<Result<(), ErrReport>>(), mem::size_of::<usize>());
|
assert_eq!(
|
||||||
|
mem::size_of::<Result<(), ErrReport>>(),
|
||||||
|
mem::size_of::<usize>()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -25,6 +28,8 @@ fn test_autotraits() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_drop() {
|
fn test_drop() {
|
||||||
let has_dropped = Flag::new();
|
let has_dropped = Flag::new();
|
||||||
drop(ErrReport::<DefaultContext>::new(DetectDrop::new(&has_dropped)));
|
drop(ErrReport::<DefaultContext>::new(DetectDrop::new(
|
||||||
|
&has_dropped,
|
||||||
|
)));
|
||||||
assert!(has_dropped.get());
|
assert!(has_dropped.get());
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use eyre::{ErrReport, eyre};
|
use eyre::{eyre, ErrReport};
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user