# charon-error
Structured error handling for Rust with rich error reports, data sensitivity labels, panic capture, and GitLab issue integration.
Named after **Charon**, the Greek ferryman of the dead across the river Styx. This crate ferries your program from a running state to a graceful death, collecting every detail along the way.
## Features
- **Error chains** — Stack multiple errors with `ErrorReport` to preserve full context from root cause to top-level failure
- **Sensitivity labels** — Tag data as `Public`, `Private`, `Internal`, or `Confidential` to control what information gets shared in reports
- **Panic hooks** — Capture panics and display human-readable error messages with `setup_panic!` and `setup_panic_simple!`
- **Issue submission** — Generate pre-filled GitLab issue URLs directly from errors so users can report bugs with one click
- **Tracing integration** — Works with the `tracing` crate. Use `#[instrument]` and span traces are captured automatically in error frames
- **Flexible formatting** — Configure output detail level (Compact, Medium, Full, Debug) and indentation style
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
charon-error = "0.1"
```
## Quick Start
```rust
use charon_error::prelude::*;
#[derive(Debug, ThisError)]
enum AppError {
#[error("failed to load configuration")]
ConfigLoad,
#[error("failed to start server")]
ServerStart,
}
#[instrument]
fn load_config() -> ResultER<String> {
std::fs::read_to_string("config.toml")
.change_context(AppError::ConfigLoad)
}
#[instrument]
fn start_server() -> ResultER<()> {
let _config = load_config()
.change_context(AppError::ServerStart)?;
Ok(())
}
```
`ResultER<T>` is a type alias for `Result<T, ErrorReport>`. The `ResultExt` trait (imported via the prelude) adds `.change_context()`, `.error_attach()`, and `.unwrap_error()` to any `Result` or `Option`.
## Error Context and Attachments
### Adding Context
Use `.change_context()` to wrap errors with higher-level meaning:
```rust
use charon_error::prelude::*;
#[instrument]
fn read_user(id: u64) -> ResultER<String> {
std::fs::read_to_string(format!("users/{id}.json"))
.change_context(StringError::new("failed to read user"))
}
```
### Attaching Data
Attach diagnostic data to errors with sensitivity labels:
```rust
use charon_error::prelude::*;
#[instrument]
fn process_order(order_id: &str) -> ResultER<()> {
do_work()
.error_attach_public_string("order_id", order_id.to_owned())?;
Ok(())
}
fn do_work() -> ResultER<()> { Ok(()) }
```
## Sensitivity Labels
`ErrorSensitivityLabel` controls who can see attached data:
| `Public` | yes | yes | yes | no |
| `Private` | yes | no | yes | no |
| `PrivateConfidential`| yes | no | no | yes |
| `Internal` | no | no | yes | no |
| `Confidential` | no | no | yes | yes |
| `HighlyConfidential` | no | no | yes | yes |
Note: Encryption is not implemented yet.
## Panic Hooks
### Full Panic Hook with Issue Submission
```rust
use charon_error::prelude::*;
use charon_error::prelude::gitlab_er::*;
#[allow(dead_code)]
fn check_if_common_errors(error_message: &str) -> Option<String> {
// use colored::*;
match error_message {
"Address/Port binding error." => {
Some("We got a Address/Port binding error.\n\
You most likely already have the application already open somewhere. \
Because the port is already in use. If this is not the case an other app \
is using this port. You can change the port in the config file.\n\
Change the port `config.json` -> server -> port.\n\
Change the value to 20001 and try again.".to_owned())
},
_ => {
None
},
}
}
fn main() -> ResultER<()> {
// Configure GitLab integration
GitLabErrorReport::setup_global_config(GitLabERGlobalSettings {
domain: "gitlab.com".to_owned(),
project_path: "my-group/my-project".to_owned(),
labels: vec!["bug".to_owned(), "auto-generated".to_owned()],
url_char_limit: 6000,
..Default::default()
})
.unwrap_error();
// Install panic hook
setup_panic!(GitLabErrorReport, check_if_common_errors);
// Your application code here
Ok(())
}
```
When a panic occurs:
- **Known errors**: Displays a friendly message from `check_if_common_errors`
- **Unknown errors (debug build)**: Shows the full error report with a clickable link to create a GitLab issue
- **Unknown errors (release build)**: Shows a user-friendly message with report instructions
### Simple Panic Hook
For applications that do not need issue submission:
```rust
use charon_error::prelude::*;
fn main() {
setup_panic_simple!();
// Your application code here
}
```
## Tracing Integration
`charon-error` integrates with the `tracing` crate. Annotate functions with `#[instrument]` and span traces are automatically captured in each error frame:
```rust
use charon_error::prelude::*;
#[instrument]
fn process_request(user_id: u64) -> ResultER<()> {
validate_user(user_id)?;
Ok(())
}
#[instrument]
fn validate_user(id: u64) -> ResultER<()> {
Err(StringError::new("user not found"))?
}
```
The error report will include the span trace showing the call chain through `process_request` and `validate_user`.
## Error Formatting
Control output detail with `ErrorFmtSettings`:
```rust
use charon_error::{ErrorFmt, ErrorFmtSettings, ErrorFmtLoD, ErrorReport, StringError};
let report = ErrorReport::from_error(StringError::new("example"));
// Compact output
let compact = report.stringify(ErrorFmtSettings {
level_of_detail: ErrorFmtLoD::Compact,
..Default::default()
});
// Full output with no color (for logs)
let full = report.stringify(ErrorFmtSettings {
level_of_detail: ErrorFmtLoD::Full,
enable_color: false,
..Default::default()
});
```
Detail levels:
- **Compact** — Key fields only
- **Medium** — Balanced (default)
- **SubmitReport** — Tailored for issue submission (no color)
- **Full** — All available information
- **Debug** — Structural output for debugging the error system itself
## GitLab Configuration
`GitLabERGlobalSettings` fields:
| `domain` | `"gitlab.com"` | GitLab instance domain |
| `project_path` | `"<not set>"` | Group/project path (e.g. `"org/project"`) |
| `labels` | `["Auto Generated Issue"]` | Labels applied to created issues |
| `url_char_limit` | `2085` | Max URL length before trimming report |
| `title_char_limit` | `1024` | Max issue title length |
| `description_char_limit`| `1_048_576` | Max issue description length (1MB) |
## Roadmap
- [ ] GitHub issue submission support
- [ ] Encryption for `Confidential` and `HighlyConfidential` data
- [ ] Configurable global formatting settings
## Use of AI in this project
This project was hand written before AI. It will be using AI to assist in development.
However ALL code is manually reviewed and improve.
---
## License
The code in this project is licensed under the MIT or Apache 2.0 license.
All contributions, code and documentation, to this project will be similarly licensed.