<div align="center">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/materror.png" alt="masterror" width="600"/>
<p><strong>Framework-agnostic application error types</strong></p>
[](https://crates.io/crates/masterror)
[](https://docs.rs/masterror)
[](https://crates.io/crates/masterror)


[](https://api.reuse.software/info/github.com/RAprogramm/masterror)
[](https://codecov.io/gh/RAprogramm/masterror)
[](https://github.com/RAprogramm/masterror/actions/workflows/ci.yml?query=branch%3Amain)
[](https://hitsofcode.com/github/RAprogramm/masterror/view?branch=main&exclude=Cargo.lock,.gitignore,CHANGELOG.md)
[](https://github.com/RAprogramm/infra-metrics-insight-renderer)
> 🇷🇺 [Читайте README на русском языке](README.ru.md)
> 🇰🇷 [한국어 README](README.ko.md)
</div>
---
## Table of Contents
- [Overview](#overview)
- [Highlights](#highlights)
- [Workspace Crates](#workspace-crates)
- [Feature Flags](#feature-flags)
- [Installation](#installation)
- [Benchmarks](#benchmarks)
- [Code Coverage](#code-coverage)
- [Quick Start](#quick-start)
- [Advanced Usage](#advanced-usage)
- [Examples](#examples)
- [Resources](#resources)
- [Metrics](#metrics)
- [License](#license)
---
## Overview
`masterror` grew from a handful of helpers into a workspace of composable crates for
building consistent, observable error surfaces across Rust services. The core
crate stays framework-agnostic, while feature flags light up transport adapters,
integrations and telemetry without pulling in heavyweight defaults. No
`unsafe`, MSRV is pinned, and the derive macros keep your domain types in charge
of redaction and metadata.
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Highlights
- **Unified taxonomy.** `AppError`, `AppErrorKind` and `AppCode` model domain and
transport concerns with conservative HTTP/gRPC mappings, turnkey retry/auth
hints and RFC7807 output via `ProblemJson`.
- **Native derives.** `#[derive(Error)]` and `#[derive(Masterror)]` wire custom
types into the runtime types. `#[derive(Masterror)]` with `#[masterror(...)]`
forwards sources, backtraces, telemetry fields and redaction policy;
`#[app_error]` maps a derived error to an `AppErrorKind`/`AppCode` (optionally
exposing its `Display` message) and `#[provide]` registers typed telemetry
providers on the domain error itself.
- **Typed telemetry.** `Metadata` stores structured key/value context (strings,
integers, floats, durations, IP addresses and optional JSON) with per-field
redaction controls and builders in `field::*`, so logs stay structured without
manual `String` maps.
- **Transport adapters.** Optional features expose Actix/Axum responders,
`tonic::Status` conversions, WASM/browser logging and OpenAPI schema
generation without contaminating the lean default build.
- **Battle-tested integrations.** Enable focused mappings for `sqlx`,
`reqwest`, `redis`, `validator`, `config`, `tokio`, `teloxide`, `multipart`,
Telegram init-data validation and more — each translating library errors into
the taxonomy with telemetry attached.
- **Turnkey defaults.** The `turnkey` module ships a ready-to-use error catalog,
a heuristic classifier and conservative mappings into the canonical taxonomy
for teams that want a consistent baseline out of the box.
- **Boxed error interop.** `AppError::from_boxed` adopts any
`Box<dyn Error + Send + Sync>` without re-boxing and `into_boxed_dyn_error`
converts back, matching `anyhow`'s round-trip API while keeping the source
chain intact and downcastable.
- **Typed control-flow macros.** `ensure!` and `fail!` short-circuit functions
with your domain errors without allocating or formatting on the happy path,
while `app_error!` builds ad-hoc `AppError` values with `format!`-style
messages in expression position.
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Workspace Crates
| [`masterror`](https://crates.io/crates/masterror) | Core error types, metadata builders, transports, integrations and the prelude. | Application crates, services and libraries that want a stable error surface. |
| [`masterror-derive`](masterror-derive/README.md) | Proc-macros backing `#[derive(Error)]`, `#[derive(Masterror)]`, `#[app_error]` and `#[provide]`. | Brought in automatically via `masterror`; depend directly only for macro hacking. |
| [`masterror-template`](masterror-template/README.md) | Shared template parser used by the derive macros for formatter analysis. | Internal dependency; reuse when you need the template parser elsewhere. |
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Feature Flags
Pick only what you need; the default feature set is just `std`, everything
else is opt-in.
- **Web transports:** `axum`, `actix`, `multipart`, `openapi`, `serde_json`.
- **Telemetry & observability:** `tracing`, `metrics`, `backtrace`, `colored` for
colored terminal output.
- **Async & IO integrations:** `tokio`, `reqwest`, `sqlx`, `sqlx-migrate`,
`redis`, `validator`, `config`.
- **Messaging & bots:** `teloxide`, `init-data` for Telegram Mini App
init-data validation via `init-data-rs`.
- **Front-end tooling:** `frontend` for WASM/browser console logging.
- **gRPC:** `tonic` to emit `tonic::Status` responses.
- **Batteries included:** `turnkey` to adopt the pre-built taxonomy and helpers.
The build script keeps the full feature snippet below in sync with
`Cargo.toml`.
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Installation
~~~toml
[dependencies]
masterror = { version = "0.29.0", default-features = false }
# or with features:
# masterror = { version = "0.29.0", features = [
# "std", "axum", "actix", "openapi",
# "serde_json", "tracing", "metrics", "backtrace",
# "colored", "sqlx", "sqlx-migrate", "reqwest",
# "redis", "validator", "config", "tokio",
# "multipart", "teloxide", "init-data", "tonic",
# "frontend", "turnkey", "benchmarks"
# ] }
~~~
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Benchmarks
Criterion benchmarks cover the hottest conversion paths so regressions are
visible before shipping. Run them locally with:
~~~sh
cargo bench -F benchmarks --bench error_paths
~~~
The suite emits two groups:
- `context_into_error/*` promotes a dummy source error with representative
metadata (strings, counters, durations, IPs) through `ResultExt::ctx` in
both redacted and non-redacted modes.
- `problem_json_from_app_error/*` consumes the resulting `AppError` values to
build RFC 7807 payloads via `ProblemJson::from_app_error`, showing how message
redaction and field policies impact serialization.
Adjust Criterion CLI flags (for example `--sample-size 200` or `--save-baseline local`) after `--` to trade
throughput for tighter confidence intervals when investigating changes.
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Code Coverage
[](https://app.codecov.io/gh/RAprogramm/masterror)
Coverage reports are automatically generated on every CI run and uploaded to [Codecov](https://app.codecov.io/gh/RAprogramm/masterror). The project maintains high test coverage across all modules to ensure reliability and catch regressions early.
<details>
<summary><b>Coverage Visualizations</b></summary>
#### Sunburst Graph
The inner-most circle represents the entire project, moving outward through folders to individual files. Size and color indicate statement count and coverage percentage.
[](https://app.codecov.io/gh/RAprogramm/masterror)
#### Grid View
Each block represents a single file. Block size and color correspond to statement count and coverage percentage.
[](https://app.codecov.io/gh/RAprogramm/masterror)
#### Icicle Chart
Hierarchical view starting with the entire project at the top, drilling down through folders to individual files. Size and color reflect statement count and coverage.
[](https://app.codecov.io/gh/RAprogramm/masterror)
</details>
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Quick Start
<details>
<summary><b>Create an error</b></summary>
Create an error:
~~~rust
use masterror::{AppError, AppErrorKind, field};
let err = AppError::new(AppErrorKind::BadRequest, "Flag must be set");
assert!(matches!(err.kind, AppErrorKind::BadRequest));
let err_with_meta = AppError::service("downstream")
.with_field(field::str("request_id", "abc123"));
assert_eq!(err_with_meta.metadata().len(), 1);
let err_with_context = AppError::internal("db down")
.with_context(std::io::Error::new(std::io::ErrorKind::Other, "boom"));
assert!(err_with_context.source_ref().is_some());
~~~
With prelude:
~~~rust
use masterror::prelude::*;
fn do_work(flag: bool) -> AppResult<()> {
if !flag {
return Err(AppError::bad_request("Flag must be set"));
}
Ok(())
}
~~~
</details>
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Advanced Usage
<details>
<summary><b>Fail fast without sacrificing typing</b></summary>
`ensure!` and `fail!` provide typed alternatives to the formatting-heavy
`anyhow::ensure!`/`anyhow::bail!` helpers. They evaluate the error expression
only when the guard trips, so success paths stay allocation-free.
~~~rust
use masterror::{AppError, AppErrorKind, AppResult};
fn guard(flag: bool) -> AppResult<()> {
masterror::ensure!(flag, AppError::bad_request("flag must be set"));
Ok(())
}
fn bail() -> AppResult<()> {
masterror::fail!(AppError::unauthorized("token expired"));
}
assert!(guard(true).is_ok());
assert!(matches!(guard(false).unwrap_err().kind, AppErrorKind::BadRequest));
assert!(matches!(bail().unwrap_err().kind, AppErrorKind::Unauthorized));
~~~
`app_error!` is the `anyhow::anyhow!` counterpart: an expression macro that
builds an `AppError` from a kind and an optional `format!`-style message with
implicit capture. The kind-only form performs no allocation; the message form
allocates exactly once.
~~~rust
use masterror::{AppErrorKind, AppResult, app_error};
fn find(id: u64) -> AppResult<u64> {
None::<u64>.ok_or_else(|| app_error!(AppErrorKind::NotFound, "no entity {id}"))
}
let bare = app_error!(AppErrorKind::Timeout);
assert!(bare.message.is_none());
assert!(matches!(find(7).unwrap_err().kind, AppErrorKind::NotFound));
~~~
</details>
<details>
<summary><b>Derive domain errors and map them to transports</b></summary>
`masterror` ships native derives so your domain types stay expressive while the
crate handles conversions, telemetry and redaction for you.
~~~rust
use std::io;
use masterror::Error;
#[derive(Debug, Error)]
#[error("I/O failed: {source}")]
pub struct DomainError {
#[from]
#[source]
source: io::Error,
}
#[derive(Debug, Error)]
#[error(transparent)]
pub struct WrappedDomainError(
#[from]
#[source]
DomainError
);
fn load() -> Result<(), DomainError> {
Err(io::Error::other("disk offline").into())
}
let err = load().unwrap_err();
assert_eq!(err.to_string(), "I/O failed: disk offline");
let wrapped = WrappedDomainError::from(err);
assert_eq!(wrapped.to_string(), "I/O failed: disk offline");
~~~
- `use masterror::Error;` brings the derive macro into scope.
- `#[from]` automatically implements `From<...>` while ensuring wrapper shapes are
valid.
- `#[error(transparent)]` enforces single-field wrappers that forward
`Display`/`source` to the inner error.
- `#[app_error(kind = AppErrorKind::..., code = AppCode::..., message)]` maps the
derived error into `AppError`/`AppCode`. The optional `code = ...` arm emits an
`AppCode` conversion, while the `message` flag forwards the derived
`Display` output as the public message instead of producing a bare error.
- `masterror::error::template::ErrorTemplate` parses `#[error("...")]`
strings, exposing literal and placeholder segments so custom derives can be
implemented without relying on `thiserror`.
- `TemplateFormatter` mirrors `thiserror`'s formatter detection so existing
derives that relied on hexadecimal, pointer or exponential renderers keep
compiling.
- Display placeholders preserve their raw format specs via
`TemplateFormatter::display_spec()` and `TemplateFormatter::format_fragment()`,
so derived code can forward `:>8`, `:.3` and other display-only options
without reconstructing the original string.
- `TemplateFormatterKind` exposes the formatter trait requested by a
placeholder, making it easy to branch on the requested rendering behaviour
without manually matching every enum variant.
</details>
<details>
<summary><b>Attach telemetry, redaction policy and conversions</b></summary>
`#[derive(Masterror)]` wires a domain error into [`masterror::Error`], adds
metadata, redaction policy and optional transport mappings. The accompanying
`#[masterror(...)]` attribute mirrors the `#[app_error]` syntax while staying
explicit about telemetry and redaction.
~~~rust
use masterror::{
mapping::HttpMapping, AppCode, AppErrorKind, Error, Masterror, MessageEditPolicy
};
#[derive(Debug, Masterror)]
#[error("user {user_id} missing flag {flag}")]
#[masterror(
code = AppCode::NotFound,
category = AppErrorKind::NotFound,
message,
redact(message, fields("user_id" = hash)),
telemetry(
Some(masterror::field::str("user_id", user_id.clone())),
attempt.map(|value| masterror::field::u64("attempt", value))
),
map.grpc = 5,
map.problem = "https://errors.example.com/not-found"
)]
struct MissingFlag {
user_id: String,
flag: &'static str,
attempt: Option<u64>,
#[source]
source: Option<std::io::Error>
}
let err = MissingFlag {
user_id: "alice".into(),
flag: "beta",
attempt: Some(2),
source: None
};
let converted: Error = err.into();
assert_eq!(converted.code, AppCode::NotFound);
assert_eq!(converted.kind, AppErrorKind::NotFound);
assert_eq!(converted.edit_policy, MessageEditPolicy::Redact);
assert!(converted.metadata().get("user_id").is_some());
assert_eq!(
MissingFlag::HTTP_MAPPING,
HttpMapping::new(AppCode::NotFound, AppErrorKind::NotFound)
);
~~~
- `code` / `category` pick the public [`AppCode`] and internal
[`AppErrorKind`].
- `message` forwards the formatted [`Display`] output as the safe public
message. Omit it to keep the message private.
- `redact(message)` flips [`MessageEditPolicy`] to redactable at the transport
boundary, `fields("name" = hash, "card" = last4)` overrides metadata
policies (`hash`, `last4`, `redact`, `none`).
- `telemetry(...)` accepts expressions that evaluate to
`Option<masterror::Field>`. Each populated field is inserted into the
resulting [`Metadata`]; use `telemetry()` when no fields are attached.
- `map.grpc` / `map.problem` capture optional gRPC status codes (as `i32`) and
RFC 7807 `type` URIs. The derive emits tables such as
`MyError::HTTP_MAPPING`, `MyError::GRPC_MAPPING` and
`MyError::PROBLEM_MAPPING` (or slice variants for enums) for downstream
integrations.
All familiar field-level attributes (`#[from]`, `#[source]`, `#[backtrace]`)
are still honoured. Sources and backtraces are automatically attached to the
generated [`masterror::Error`].
</details>
<details>
<summary><b>Structured telemetry providers and AppError mappings</b></summary>
`#[provide(...)]` exposes typed context through `std::error::Request`, while
`#[app_error(...)]` records how your domain error translates into `AppError`
and `AppCode`. The derive mirrors `thiserror`'s syntax. The generated `From`
conversions produce an `AppError` carrying the mapped kind and code (plus the
`Display` output as public message when the `message` flag is set) and attach
the original domain error as the source: it stays downcastable via
`downcast_ref`, shows up in `chain()`/`root_cause()`, and its `#[provide]`
data is forwarded through the `AppError` on toolchains with
`error_generic_member_access`. Source attachment requires the domain error to
be `Send + Sync + 'static`; add the `no_source` flag to `#[app_error(...)]`
to opt out and drop the domain error during conversion instead.
`request_ref`/`request_value` and the `std::error::Request` machinery require
a nightly toolchain (`error_generic_member_access`); the crate detects
compiler support at build time and only enables provider integration when
available.
~~~rust
use std::error::request_ref;
use masterror::{AppCode, AppError, AppErrorKind, Error};
#[derive(Clone, Debug, PartialEq, Eq)]
struct TelemetrySnapshot {
name: &'static str,
value: u64,
}
#[derive(Debug, Error)]
#[error("structured telemetry {snapshot:?}")]
#[app_error(kind = AppErrorKind::Service, code = AppCode::Service)]
struct StructuredTelemetryError {
#[provide(ref = TelemetrySnapshot, value = TelemetrySnapshot)]
snapshot: TelemetrySnapshot,
}
let err = StructuredTelemetryError {
snapshot: TelemetrySnapshot {
name: "db.query",
value: 42,
},
};
let snapshot = request_ref::<TelemetrySnapshot>(&err).expect("telemetry");
assert_eq!(snapshot.value, 42);
let app: AppError = err.into();
assert!(matches!(app.kind, AppErrorKind::Service));
~~~
Optional telemetry only surfaces when present, so `None` does not register a
provider. Owned snapshots can still be provided as values when the caller
requests ownership:
~~~rust
use masterror::{AppCode, AppErrorKind, Error};
#[derive(Debug, Error)]
#[error("optional telemetry {telemetry:?}")]
#[app_error(kind = AppErrorKind::Internal, code = AppCode::Internal)]
struct OptionalTelemetryError {
#[provide(ref = TelemetrySnapshot, value = TelemetrySnapshot)]
telemetry: Option<TelemetrySnapshot>,
}
let noisy = OptionalTelemetryError {
telemetry: Some(TelemetrySnapshot {
name: "queue.depth",
value: 17,
}),
};
let silent = OptionalTelemetryError { telemetry: None };
assert!(request_ref::<TelemetrySnapshot>(&noisy).is_some());
assert!(request_ref::<TelemetrySnapshot>(&silent).is_none());
~~~
Enums support per-variant telemetry and conversion metadata. Each variant chooses
its own `AppErrorKind`/`AppCode` mapping while the derive generates a single
`From<Enum>` implementation:
~~~rust
#[derive(Debug, Error)]
enum EnumTelemetryError {
#[error("named {label}")]
#[app_error(kind = AppErrorKind::NotFound, code = AppCode::NotFound)]
Named {
label: &'static str,
#[provide(ref = TelemetrySnapshot)]
snapshot: TelemetrySnapshot,
},
#[error("optional tuple")]
#[app_error(kind = AppErrorKind::Timeout, code = AppCode::Timeout)]
Optional(#[provide(ref = TelemetrySnapshot)] Option<TelemetrySnapshot>),
#[error("owned tuple")]
#[app_error(kind = AppErrorKind::Service, code = AppCode::Service)]
Owned(#[provide(value = TelemetrySnapshot)] TelemetrySnapshot),
}
let owned = EnumTelemetryError::Owned(TelemetrySnapshot {
name: "redis.latency",
value: 3,
});
let app: AppError = owned.into();
assert!(matches!(app.kind, AppErrorKind::Service));
~~~
Compared to `thiserror`, you retain the familiar deriving surface while gaining
structured telemetry (`#[provide]`) and first-class conversions into
`AppError`/`AppCode` without manual glue.
</details>
<details>
<summary><b>Problem JSON payloads and retry/authentication hints</b></summary>
~~~rust
use masterror::{AppError, AppErrorKind, ProblemJson};
let problem = ProblemJson::from_app_error(
AppError::new(AppErrorKind::Unauthorized, "Token expired")
.with_retry_after_secs(30)
.with_www_authenticate(r#"Bearer realm="api", error="invalid_token""#)
);
assert_eq!(problem.status, 401);
assert_eq!(problem.retry_after, Some(30));
assert_eq!(problem.grpc.expect("grpc").name, "UNAUTHENTICATED");
~~~
</details>
<details>
<summary><b>Environment detection with DisplayMode</b></summary>
`DisplayMode` detects the deployment environment (`Prod`, `Local` or
`Staging`) and drives the `Display` output of `AppError`.
`DisplayMode::current()` resolves the mode in this order and caches the
result on first access (the environment is read once per process):
1. `MASTERROR_ENV` environment variable (`prod`/`production`,
`local`/`dev`/`development`, or `staging`/`stage`)
2. `KUBERNETES_SERVICE_HOST` presence (selects `Prod`)
3. Build configuration (`debug_assertions` → `Local`, release → `Prod`;
the only detection step available without the `std` feature)
~~~rust
use masterror::DisplayMode;
let mode = DisplayMode::current();
match mode {
DisplayMode::Prod => println!("Running in production mode"),
DisplayMode::Local => println!("Running in local development mode"),
DisplayMode::Staging => println!("Running in staging mode"),
}
~~~
`Display` for `AppError` dispatches on the detected mode:
| `Local` | Multi-line human-readable report: kind, code, message, source chain, metadata |
| `Prod` | Compact single-line JSON: `kind`, `code`, optional `message`, metadata |
| `Staging` | Same JSON as `Prod` plus a `source_chain` array |
All layouts apply per-field redaction policies: `Redact` renders the
`[REDACTED]` placeholder, `Hash` renders a SHA-256 hex digest, `Last4`
keeps only the trailing characters (fields that cannot be masked are
omitted). Set `MASTERROR_ENV=local` on any host to force the
human-readable layout, e.g. when debugging inside Kubernetes.
In `Local` mode the output looks like:
~~~
Error: Not found
Code: NOT_FOUND
Message: User not found
Context:
user_id: 12345
request_id: abc-def
~~~
In `Prod` mode the same error renders as:
~~~
{"kind":"NotFound","code":"NOT_FOUND","message":"User not found","metadata":{"request_id":"abc-def","user_id":12345}}
~~~
**Colored Terminal Output:**
Enable the `colored` feature for ANSI styling with automatic TTY
detection. It affects only the `Local` layout; `Prod` and `Staging` JSON
never contains escape sequences:
~~~toml
[dependencies]
masterror = { version = "0.29.0", features = ["colored"] }
~~~
</details>
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Examples
Comprehensive real-world examples demonstrating masterror integration with popular frameworks:
| [**axum-rest-api**](examples/axum-rest-api/) | REST API with RFC 7807 Problem Details | HTTP endpoints, domain errors, integration tests |
| [**sqlx-database**](examples/sqlx-database/) | Database error handling with SQLx | Connection errors, constraint violations, transactions |
| [**custom-domain-errors**](examples/custom-domain-errors/) | Payment processing domain errors | Derive macro, error conversion, structured errors |
| [**basic-async**](examples/basic-async/) | Async error handling with tokio | Error propagation, timeout handling, Result types |
All examples are runnable; the axum-rest-api example additionally ships integration tests. See the [`examples/`](examples/) directory for complete source code and documentation.
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Resources
- Explore the [error-handling wiki](https://github.com/RAprogramm/masterror/wiki) for step-by-step guides,
comparisons with `thiserror`/`anyhow`, and troubleshooting recipes.
- Browse the [crate documentation on docs.rs](https://docs.rs/masterror) for API
details, feature-specific guides and transport tables.
- Check [`CHANGELOG.md`](CHANGELOG.md) for release highlights and migration notes.
- Review [RustManifest](https://github.com/RAprogramm/RustManifest) for the development standards and best practices this project follows.
<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## Metrics

<div align="right">
<div align="right">
<a href="#table-of-contents">
<img src="https://raw.githubusercontent.com/RAprogramm/masterror/main/images/masterror_go_to_top.png" alt="Go to top" width="50"/>
</a>
</div>
</div>
---
## License
MSRV: **1.96** · License: **MIT** · No `unsafe`