1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*!
[![Crates.io](https://img.shields.io/crates/v/embedded-error-chain.svg)](https://crates.io/crates/embedded-error-chain)
[![API reference](https://docs.rs/embedded-error-chain/badge.svg)](https://docs.rs/embedded-error-chain/)

Easy error handling for embedded devices (`no_alloc` and `no_std`).

A rust library implementing easy error handling for embedded devices. An [`Error`] value is
only a single [`u32`] in size and supports up to 4 chained error codes. Each error code can
have a value from `0` to `15` (4 bits). All error codes come from an enum that implements
the [`ErrorCategory`] trait (a derive macro exists). This trait is also used to implement
debug printing and equality for each error code.

This library was inspired by libraries such as [error-chain](https://crates.io/crates/error-chain)
and [anyhow](https://crates.io/crates/anyhow), though its goal is to work in `no_std` and `no_alloc`
environments with very little memory overhead.

## Example
```rust
use embedded_error_chain::prelude::*;

#[derive(Clone, Copy, ErrorCategory)]
#[repr(u8)]
enum SpiError {
    BusError,
    // ...
}

static LAST_GYRO_ACC_READOUT: usize = 200;

#[derive(Clone, Copy, ErrorCategory)]
#[error_category(links(SpiError))]
#[repr(u8)]
enum GyroAccError {
    InitFailed,

    #[error("{variant} (readout={})", LAST_GYRO_ACC_READOUT)]
    ReadoutFailed,

    /// Value must be in range [0, 256)
    #[error("{variant}: {summary}")]
    InvalidValue,
}

#[derive(Clone, Copy, ErrorCategory)]
#[error_category(links(GyroAccError))]
#[repr(u8)]
enum CalibrationError {
    Inner,
}

fn main() {
    if let Err(err) = calibrate() {
        // log the error
        println!("{:?}", err);
        // ...
    }

    let readout = match gyro_acc_readout() {
        Ok(val) => val,
        Err(err) => {
            if let Some(spi_error) = err.code_of_category::<SpiError>() {
                // try to fix it
                0
            }
            else {
                panic!("unfixable spi error");
            }
        }
    };
}

fn spi_init() -> Result<(), SpiError> {
    Err(SpiError::BusError)
}

fn gyro_acc_init() -> Result<(), Error<GyroAccError>> {
    spi_init().chain_err(GyroAccError::InitFailed)?;
    Ok(())
}

fn gyro_acc_readout() -> Result<u32, Error<GyroAccError>> {
    Err(SpiError::BusError.chain(GyroAccError::InvalidValue))
}

fn calibrate() -> Result<(), Error<CalibrationError>> {
    gyro_acc_init().chain_err(CalibrationError::Inner)?;
    Ok(())
}
```
*/

#![no_std]
#![cfg_attr(feature = "nightly", feature(const_panic, const_fn))]

#[cfg(feature = "std")]
extern crate std;

mod error;
mod error_category;
mod error_data;

#[doc(hidden)]
pub mod utils;

pub use error::{ChainError, Error, ErrorIter, ResultChainError};
pub use error_category::{
    format_chained, ErrorCategory, ErrorCategoryHandle, ErrorCodeFormatter, ErrorCodeFormatterVal,
};
pub use error_data::{ErrorData, ERROR_CHAIN_LEN};

/// Everything for easy error handling.

pub mod prelude {
    #[doc(no_inline)]
    pub use crate::{ChainError, Error, ErrorCategory, ErrorCategoryHandle, ResultChainError};
}

/// Marker types.

pub mod marker {
    /// A tag type to disambiguate between trait implementations.

    pub struct L0;
    /// A tag type to disambiguate between trait implementations.

    pub struct L1;
    /// A tag type to disambiguate between trait implementations.

    pub struct L2;
    /// A tag type to disambiguate between trait implementations.

    pub struct L3;
    /// A tag type to disambiguate between trait implementations.

    pub struct L4;
    /// A tag type to disambiguate between trait implementations.

    pub struct L5;

    /// A tag type to disambiguate between `ChainError` trait implementations for

    /// `Error<T>` and just for `T`.

    #[allow(non_camel_case_types)]
    pub struct Error_t;

    /// A tag type to disambiguate between `ChainError` trait implementations for

    /// `Error<T>` and just for `T`.

    #[allow(non_camel_case_types)]
    pub struct Concrete_t;

    pub use super::error_category::Unused;
}

/// An error code.

///

/// Must only be 4 bits wide.

pub type ErrorCode = u8;

/// Derive [`ErrorCategory`](ErrorCategory) for an enum.

///

/// This will also try to derive the trait dependencies of

/// [`ErrorCategory`](ErrorCategory) with the exception of

/// [`Copy`](core::marker::Copy):

/// - [`core::fmt::Debug`](core::fmt::Debug)

/// - [`Into`](core::convert::Into)`<`[`ErrorCode`](ErrorCode)`>`

/// - [`From`](core::convert::From)`<`[`ErrorCode`](ErrorCode)`>`

///

/// The traits `Into<ErrorCode>` and `From<ErrorCode>` are only derived if the enum is

/// `repr(u8)` (see

/// [`Data Layout` in the

/// nomicon](https://doc.rust-lang.org/nomicon/other-reprs.html#repru-repri)) or does *not*

/// contain any variants.

///

/// ## Usage

///

/// ```rust

/// use embedded_error_chain::prelude::*;

///

/// #[derive(Clone, Copy, ErrorCategory)]

/// #[repr(u8)]

/// enum OtherError {

///     ExtremeFailure,

/// }

///

/// static SOME_GLOBAL_VARIABLE: usize = 200;

///

/// #[derive(Clone, Copy, ErrorCategory)]

/// #[error_category(name = "optional name", links(OtherError))]

/// #[repr(u8)]

/// enum TestError {

///     #[error("format string {summary}, {details}, {variant}, {category}")]

///     Foo = 0,

///

///     #[error("custom {}, {:?}", "some_expr", SOME_GLOBAL_VARIABLE)]

///     Other,

///

///     /// Summary

///     ///

///     /// Detailed description.

///     /// The summary and detailed description are available as placeholders in

///     /// the `#[error(...)]` attribute. If no such attribute is put on the variant

///     /// or the `...` part is empty, then the summary will be used. If the summary

///     /// does not exist (no doc comments on the variant), then the variant name is

///     /// used for debug printing.

///     Bar,

/// }

///

/// #[derive(Clone, Copy, ErrorCategory)]

/// #[error_category(links(OtherError, TestError))]

/// #[repr(u8)]

/// enum SeperateError {

///     SomethingHappened,

/// }

///

/// #[derive(Clone, Copy, ErrorCategory)]

/// enum YetEmptyError {}

/// ```

pub use macros::ErrorCategory;