# resext
**Main crate providing error handling with context chains**
This is the primary interface for ResExt. It re-exports the proc-macro as well as other helpers provided by ResExt.
---
## Installation
```toml
[dependencies]
resext = "1.0.0"
```
---
## Quick Example
```rust
use resext::resext;
use resext::ctx;
#[resext]
enum FileError {
Io(std::io::Error),
Parse(serde_json::Error),
}
fn load_data(path: &str) -> Res<Data> {
let content = std::fs::read_to_string(path)
.context(ctx!("Failed to read file: {}", path))?;
let data = serde_json::from_str(&content)
.context(ctx!("Failed to parse file: {}", path))?;
Ok(data)
}
```
---
## Proc Macro
The proc macro provides clean syntax with full customization:
```rust
#[resext(
prefix = "ERROR: ",
suffix = "\n",
msg_prefix = " at: ",
msg_suffix = "",
delimiter = "\n",
source_prefix = "Caused by: ",
include_variant = true,
alias = MyResult,
alloc = true
)]
enum MyError {
Network(reqwest::Error),
Database { error: sqlx::Error },
}
```
### Attribute Options
- `prefix` - String prepended to entire error message
- `suffix` - String appended to entire error message
- `msg_prefix` - String prepended to each context message
- `msg_suffix` - String appended to each context message
- `delimiter` - Separator between context messages (default: "\n - ")
- `source_prefix` - String prepended to source error (default: "Error: ")
- `include_variant` - Include variant name in Display output (default: false)
- `alias` - Custom type alias name which is used for getting the names for other items generated by the proc-macro (default: `Res`)
- `buf_size` - Size for the context message byte buffer (default: 64)
- `alloc` Enable heap-spilling if context exceeds `buf_size`
### `.context()` Method
Add static context to an error.
Accepts `&str` or `ctx!()` macro which outputs a lazily evaluated closure with usage similar to old `format_args!()` API
#### Example
```rust
std::fs::read("file.txt")
.context("Failed to read file")?;
```
### `Res!` macro
Generated macro for returning `Err()` results with context easily
#### Example
```rust
use resext::resext;
#[resext]
enum MyError {
Io(std::io::Error),
Utf8(core::str::Utf8Error),
}
fn return_error(file_name: &str) -> Res<()> {
Res!(std::io::Error::other("I/O Error"), "Failed to read file: {}", file_name);
}
```
### `ctx!()` macro
Macro defined in `resext` crate that returns a lazily evaluated closure with similar usage to old `format_args!()` context API but with better performance
- This macro shoudln't be used alone and should be used with `.context()` method instead
#### Example
```rust
let path: &str = "file.txt";
std::fs::read(path)
.context(ctx!("Failed to read file: {}", path))?;
```
---
## Error Display Format
Errors are displayed with context chains:
```
Failed to load application
- Failed to read config file
- Failed to open file
Error: No such file or directory
```
With `include_variant = true`:
```
Failed to load application
- Failed to read config file
Error: Io: No such file or directory
```
---
## Examples
### Basic Error Handling
```rust
use resext::resext;
use resext::ctx;
#[resext]
enum ConfigError {
Io(std::io::Error),
Parse(toml::de::Error),
}
fn load_config(path: &str) -> Res<Config> {
let content = std::fs::read_to_string(path)
.context("Failed to read config")?;
toml::from_str(&content)
.context(ctx!("Failed to parse {}", path))
}
```
### Multiple Error Types
```rust
use resext::resext;
#[resext(alias = ApiResult)]
enum ApiError {
Network(reqwest::Error),
Database(sqlx::Error),
Json(serde_json::Error),
}
async fn fetch_user(id: u64) -> ApiResult<User> {
let response = reqwest::get(format!("/users/{}", id))
.await
.context("Failed to fetch user")?;
let user = response.json()
.await
.context("Failed to parse user data")?;
Ok(user)
}
```
### Named Fields
```rust
use resext::resext;
#[resext]
enum DatabaseError {
Connection { error: sqlx::Error },
Query { error: sqlx::Error },
}
```
---
## Contributing
See [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines.
---
## License
MIT - See [LICENSE](../LICENSE) for details.