Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# CHANGELOG

All significant changes to this project will be documented in this file.

## Unreleased

### Breaking Changes

* `Exn::from_iter` has been renamed to `Exn::raise_all`
* `exn::Error` trait bound has been removed in favor of inlined `StdError + Send + Sync + 'static` bounds.
* `err.raise()` has been moved to the `exn::ErrorExt` extension trait.

### New Features

* This crate is now `no_std` compatible, while the `alloc` crate is still required for heap allocations. It is worth noting that `no_std` support is a nice-to-have feature, and can be dropped if it blocks other important features in the future. Before 1.0, once `exn` APIs settle down, the decision on whether to keep `no_std` as a promise will be finalized.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# A context-aware concrete Error type built on `std::error::Error`
# A context-aware concrete Error type built on `core::error::Error`

[![Crates.io][crates-badge]][crates-url]
[![Documentation][docs-badge]][docs-url]
Expand All @@ -16,14 +16,20 @@

## Overview

`exn` provides the missing context APIs for `std::error::Error`.
`exn` provides the missing context APIs for `core::error::Error`.

It organizes errors as a tree structure, allowing you to easily access the root cause and all related errors with their context.

## Documentation

Read the online documents at https://docs.rs/exn.

## `no_std` crates

This crate is `no_std` compatible, while the `alloc` crate is still required for heap allocations.

It is worth noting that `no_std` support is a nice-to-have feature, and can be dropped if it blocks other important features in the future. Before 1.0, once `exn` APIs settle down, the decision on whether to keep `no_std` as a promise will be finalized.

## License

This project is licensed under [Apache License, Version 2.0](LICENSE).
4 changes: 2 additions & 2 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ publish = false
edition.workspace = true

[[example]]
name = "anti-pattern"
path = "src/anti-pattern.rs"
name = "antipattern"
path = "src/antipattern.rs"

[[example]]
name = "basic"
Expand Down
6 changes: 3 additions & 3 deletions examples/src/anti-pattern.rs → examples/src/antipattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ mod http {
// Output when running `cargo run --example anti_pattern`.
// Notice "failed to send request" appears twice with no new information!
//
// Error: fatal error occurred in application, at examples/src/anti-pattern.rs:35:16
// Error: fatal error occurred in application, at examples/src/antipattern.rs:35:16
// |
// |-> failed to send request, at examples/src/anti-pattern.rs:49:30
// |-> failed to send request, at examples/src/antipattern.rs:49:30
// |
// |-> failed to send request to server: https://anti-pattern.com, at examples/src/anti-pattern.rs:67:9
// |-> failed to send request to server: https://anti-pattern.com, at examples/src/antipattern.rs:67:9
2 changes: 1 addition & 1 deletion examples/src/make-error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
//! When a function has several fallible calls, it's common to want one *function-level* context
//! string for all of them.
//!
//! This reduces boilerplate and avoids the anti-pattern of writing per-callsite context that
//! This reduces boilerplate and avoids the antipattern of writing per-caller-site context that
//! simply repeats what the child error already says.

use std::error::Error;
Expand Down
2 changes: 1 addition & 1 deletion exn/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
name = "exn"
version = "0.3.0-rc.2"

description = "A context-aware concrete Error type built on `std::error::Error`."
description = "A context-aware concrete Error type built on `core::error::Error`."

edition.workspace = true
homepage.workspace = true
Expand Down
5 changes: 3 additions & 2 deletions exn/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use std::fmt;
use alloc::format;
use core::error::Error;
use core::fmt;

use crate::Exn;
use crate::Frame;
Expand Down
4 changes: 2 additions & 2 deletions exn/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use std::fmt;
use core::error::Error;
use core::fmt;

use crate::Exn;
use crate::Frame;
Expand Down
6 changes: 3 additions & 3 deletions exn/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use core::error::Error;

use crate::Exn;
use crate::Result;
Expand All @@ -25,13 +25,13 @@ use crate::Result;
/// One might think that `exn::Result::Ok(value)` would work in such cases, but it does not.
///
/// ```console
/// error[E0282]: type annotations needed for `std::result::Result<i32, E>`
/// error[E0282]: type annotations needed for `core::result::Result<i32, E>`
/// --> src/main.rs:11:13
/// |
/// 11 | let _ = exn::Result::Ok(1);
/// | - ^^^^^^^^^^^^^^^ cannot infer type for type parameter `E` declared on the enum `Result`
/// | |
/// | consider giving this pattern the explicit type `std::result::Result<i32, E>`, where the type parameter `E` is specified
/// | consider giving this pattern the explicit type `core::result::Result<i32, E>`, where the type parameter `E` is specified
/// ```
#[expect(non_snake_case)]
pub fn Ok<T, E: Error + Send + Sync + 'static>(value: T) -> Result<T, E> {
Expand Down
13 changes: 9 additions & 4 deletions exn/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use std::fmt;
use std::marker::PhantomData;
use std::panic::Location;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
use core::error::Error;
use core::fmt;
use core::marker::PhantomData;
use core::panic::Location;

/// An exception type that can hold an error tree and additional context.
pub struct Exn<E: Error + Send + Sync + 'static> {
Expand Down
17 changes: 10 additions & 7 deletions exn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//! A context-aware concrete Error type built on `std::error::Error`
//! A context-aware concrete Error type built on `core::error::Error`
//!
//! # Examples
//!
Expand All @@ -25,13 +25,13 @@
//! #[derive(Debug)]
//! struct LogicError(String);
//!
//! impl std::fmt::Display for LogicError {
//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//! impl core::fmt::Display for LogicError {
//! fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
//! write!(f, "logic error: {}", self.0)
//! }
//! }
//!
//! impl std::error::Error for LogicError {}
//! impl core::error::Error for LogicError {}
//!
//! fn do_logic() -> Result<(), LogicError> {
//! bail!(LogicError("0 == 1".to_string()));
Expand All @@ -44,16 +44,16 @@
//! Trivial,
//! }
//!
//! impl std::fmt::Display for AppError {
//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//! impl core::fmt::Display for AppError {
//! fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
//! match self {
//! AppError::Fatal { consequences } => write!(f, "fatal error: {consequences}"),
//! AppError::Trivial => write!(f, "trivial error"),
//! }
//! }
//! }
//!
//! impl std::error::Error for AppError {}
//! impl core::error::Error for AppError {}
//!
//! fn main() {
//! if let Err(err) = do_logic().or_raise(|| AppError::Fatal {
Expand All @@ -74,6 +74,9 @@

#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]
#![no_std]

extern crate alloc;

mod debug;
mod display;
Expand Down
11 changes: 6 additions & 5 deletions exn/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@
/// Create an [`Exn`] from [`Error`]:
///
/// [`Exn`]: crate::Exn
/// [`Error`]: std::error::Error
/// [`Error`]: core::error::Error
///
/// ```
/// use std::fs;
///
/// use exn::bail;
///
/// # fn wrapper() -> exn::Result<(), std::io::Error> {
/// match fs::read_to_string("/path/to/file") {
/// Ok(content) => println!("file contents: {content}"),
Expand All @@ -37,7 +38,7 @@
#[macro_export]
macro_rules! bail {
($err:expr) => {{
return ::std::result::Result::Err($crate::Exn::from($err));
return ::core::result::Result::Err($crate::Exn::from($err));
}};
}

Expand All @@ -50,16 +51,16 @@ macro_rules! bail {
/// Create an [`Exn`] from an [`Error`]:
///
/// [`Exn`]: crate::Exn
/// [`Error`]: std::error::Error
/// [`Error`]: core::error::Error
///
/// ```
/// # fn has_permission(_: &u32, _: &u32) -> bool { true }
/// # type User = u32;
/// # let user = 0;
/// # type Resource = u32;
/// # let resource = 0;
/// use std::error::Error;
/// use std::fmt;
/// use core::error::Error;
/// use core::fmt;
///
/// use exn::ensure;
///
Expand Down
2 changes: 1 addition & 1 deletion exn/src/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use core::error::Error;

use crate::Exn;
use crate::Result;
Expand Down
8 changes: 4 additions & 4 deletions exn/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::error::Error;
use core::error::Error;

use crate::Exn;

/// A reasonable return type to use throughout an application.
pub type Result<T, E> = std::result::Result<T, Exn<E>>;
pub type Result<T, E> = core::result::Result<T, Exn<E>>;

/// An extension trait for [`Result`] to provide context information on [`Exn`]s.
pub trait ResultExt {
Expand All @@ -36,7 +36,7 @@ pub trait ResultExt {
F: FnOnce() -> A;
}

impl<T, E> ResultExt for std::result::Result<T, E>
impl<T, E> ResultExt for core::result::Result<T, E>
where
E: Error + Send + Sync + 'static,
{
Expand All @@ -56,7 +56,7 @@ where
}
}

impl<T, E> ResultExt for std::result::Result<T, Exn<E>>
impl<T, E> ResultExt for core::result::Result<T, Exn<E>>
where
E: Error + Send + Sync + 'static,
{
Expand Down