#![deny(
future_incompatible,
missing_docs,
nonstandard_style,
unsafe_op_in_unsafe_fn,
unused,
warnings,
clippy::all,
clippy::missing_safety_doc,
clippy::undocumented_unsafe_blocks,
rustdoc::broken_intra_doc_links,
rustdoc::missing_crate_level_docs
)]
#![no_std]
#![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
mod boxed_error;
#[cfg(feature = "alloc")]
mod thin_box;
use core::{
error, fmt,
hint::unreachable_unchecked,
marker::PhantomData,
ops::{Deref, DerefMut},
};
pub trait Trace: Sized + Send + Sync + 'static {
fn trace<R>(self, trace: R) -> Self
where
R: fmt::Debug + fmt::Display + Send + Sync + 'static;
}
pub trait Source: Trace + error::Error {
fn new<T: error::Error + Send + Sync + 'static>(source: T) -> Self;
}
pub trait Fallible {
type Error;
}
#[repr(transparent)]
pub struct Strategy<T: ?Sized, E> {
_error: PhantomData<E>,
inner: T,
}
impl<T: ?Sized, E> Fallible for Strategy<T, E> {
type Error = E;
}
impl<T: ?Sized, E> Strategy<T, E> {
pub fn wrap(inner: &mut T) -> &mut Self {
unsafe { core::mem::transmute::<&mut T, &mut Self>(inner) }
}
}
impl<T: ?Sized, E> Deref for Strategy<T, E> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: ?Sized, E> DerefMut for Strategy<T, E> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[macro_export]
macro_rules! fail {
($($x:tt)*) => {
return ::core::result::Result::Err($crate::Source::new($($x)*));
};
}
pub trait ResultExt<T, E> {
fn into_error<U>(self) -> Result<T, U>
where
U: Source,
E: error::Error + Send + Sync + 'static;
fn into_trace<U, R>(self, trace: R) -> Result<T, U>
where
U: Source,
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
E: error::Error + Send + Sync + 'static;
fn into_with_trace<U, R, F>(self, f: F) -> Result<T, U>
where
U: Source,
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
F: FnOnce() -> R,
E: error::Error + Send + Sync + 'static;
fn trace<R>(self, trace: R) -> Result<T, E>
where
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
E: Trace;
fn with_trace<R, F>(self, f: F) -> Result<T, E>
where
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
F: FnOnce() -> R,
E: Trace;
fn always_ok(self) -> T
where
E: Never;
}
impl<T, E> ResultExt<T, E> for Result<T, E> {
fn into_error<U>(self) -> Result<T, U>
where
U: Source,
E: error::Error + Send + Sync + 'static,
{
match self {
Ok(x) => Ok(x),
Err(e) => Err(U::new(e)),
}
}
fn into_trace<U, R>(self, trace: R) -> Result<T, U>
where
U: Source,
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
E: error::Error + Send + Sync + 'static,
{
match self {
Ok(x) => Ok(x),
Err(e) => Err(U::new(e).trace(trace)),
}
}
fn into_with_trace<U, R, F>(self, f: F) -> Result<T, U>
where
U: Source,
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
F: FnOnce() -> R,
E: error::Error + Send + Sync + 'static,
{
match self {
Ok(x) => Ok(x),
Err(e) => Err(U::new(e).trace(f())),
}
}
fn trace<R>(self, trace: R) -> Result<T, E>
where
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
E: Trace,
{
match self {
Ok(x) => Ok(x),
Err(e) => Err(e.trace(trace)),
}
}
fn with_trace<R, F>(self, f: F) -> Result<T, E>
where
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
F: FnOnce() -> R,
E: Trace,
{
match self {
Ok(x) => Ok(x),
Err(e) => Err(e.trace(f())),
}
}
fn always_ok(self) -> T
where
E: Never,
{
match self {
Ok(x) => x,
Err(e) => unreachable_checked(e),
}
}
}
pub trait OptionExt<T> {
fn into_error<E>(self) -> Result<T, E>
where
E: Source;
fn into_trace<E, R>(self, trace: R) -> Result<T, E>
where
E: Source,
R: fmt::Debug + fmt::Display + Send + Sync + 'static;
fn into_with_trace<E, R, F>(self, f: F) -> Result<T, E>
where
E: Source,
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
F: FnOnce() -> R;
}
pub unsafe trait Never {}
#[inline(always)]
pub const fn unreachable_checked<T: Never>(_: T) -> ! {
unsafe { unreachable_unchecked() }
}
#[derive(Debug)]
struct NoneError;
impl fmt::Display for NoneError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "`Option` is `None`, expected `Some`")
}
}
impl error::Error for NoneError {}
impl<T> OptionExt<T> for Option<T> {
fn into_error<E>(self) -> Result<T, E>
where
E: Source,
{
match self {
Some(x) => Ok(x),
None => Err(E::new(NoneError)),
}
}
fn into_trace<E, R>(self, trace: R) -> Result<T, E>
where
E: Source,
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
{
match self {
Some(x) => Ok(x),
None => Err(E::new(NoneError).trace(trace)),
}
}
fn into_with_trace<E, R, F>(self, f: F) -> Result<T, E>
where
E: Source,
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
F: FnOnce() -> R,
{
match self {
Some(x) => Ok(x),
None => Err(E::new(NoneError).trace(f())),
}
}
}
pub use core::convert::Infallible;
unsafe impl Never for Infallible {}
impl Trace for Infallible {
fn trace<R>(self, _: R) -> Self
where
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
{
match self {}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Panic {}
unsafe impl Never for Panic {}
impl fmt::Display for Panic {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {}
}
}
impl error::Error for Panic {}
impl Trace for Panic {
fn trace<R>(self, _: R) -> Self
where
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
{
match self {}
}
}
impl Source for Panic {
fn new<T: fmt::Display>(error: T) -> Self {
panic!("created a new `Panic` from: {error}");
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Failure;
impl fmt::Display for Failure {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "failed without error information")
}
}
impl error::Error for Failure {}
impl Trace for Failure {
fn trace<R>(self, _: R) -> Self
where
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
{
self
}
}
impl Source for Failure {
fn new<T: error::Error + Send + Sync + 'static>(_: T) -> Self {
Self
}
}
#[cfg(feature = "alloc")]
pub use boxed_error::BoxedError;
#[cfg(all(debug_assertions, feature = "alloc"))]
type ErrorType = BoxedError;
#[cfg(not(all(debug_assertions, feature = "alloc")))]
type ErrorType = Failure;
#[derive(Debug)]
pub struct Error {
inner: ErrorType,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)?;
#[cfg(not(all(debug_assertions, feature = "alloc")))]
write!(
f,
"; enable debug assertions and the `alloc` feature in rancor for \
error information"
)?;
Ok(())
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.inner.source()
}
}
impl Trace for Error {
fn trace<R>(self, trace: R) -> Self
where
R: fmt::Debug + fmt::Display + Send + Sync + 'static,
{
Self {
inner: self.inner.trace(trace),
}
}
}
impl Source for Error {
fn new<T: error::Error + Send + Sync + 'static>(source: T) -> Self {
Self {
inner: ErrorType::new(source),
}
}
}
#[cfg(test)]
mod test {
use super::*;
struct Inner {
value: u64,
}
#[test]
fn test_strategy() {
let mut inner = Inner { value: 10 };
let address = &inner.value as *const u64;
let strategy: &mut Strategy<Inner, Failure> =
Strategy::wrap(&mut inner);
let s_address = (&strategy.inner.value) as *const u64;
assert_eq!(address, s_address);
assert_eq!(strategy.value, 10);
strategy.value = 20;
assert_eq!(strategy.value, 20);
assert_eq!(inner.value, 20);
}
}