feat(status): add error context

This commit was merged in pull request #70.
This commit is contained in:
2026-09-17 15:07:14 -04:00
parent 49cf043035
commit 6b29f2c2b9
3 changed files with 58 additions and 5 deletions

View File

@@ -1,5 +1,12 @@
pub type Result<T> = std::result::Result<T, Status>;
pub trait ResultExt<T> {
fn with_context<C, F>(self, context: F) -> Result<T>
where
C: Into<String>,
F: FnOnce() -> C;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Code {
InvalidArgument,
@@ -48,6 +55,10 @@ impl Status {
self
}
pub fn with_context(self, context: impl Into<String>) -> Self {
Self::new(self.code, context).with_source(self)
}
pub fn code(&self) -> Code {
self.code
}
@@ -82,3 +93,16 @@ impl From<std::io::Error> for Status {
Self::new(code, error.to_string()).with_source(error)
}
}
impl<T, E> ResultExt<T> for std::result::Result<T, E>
where
E: Into<Status>,
{
fn with_context<C, F>(self, context: F) -> Result<T>
where
C: Into<String>,
F: FnOnce() -> C,
{
self.map_err(|error| error.into().with_context(context()))
}
}

View File

@@ -1,6 +1,6 @@
use std::error::Error;
use status::{Code, Status};
use status::{Code, ResultExt, Status};
#[test]
fn constructors_set_code_and_message() {
@@ -34,6 +34,29 @@ fn with_source_preserves_source_error() {
assert_eq!("denied", status.source().unwrap().to_string());
}
#[test]
fn with_context_preserves_code_and_wraps_status() {
let status = Status::not_found("missing input").with_context("failed to load config");
assert_eq!(Code::NotFound, status.code());
assert_eq!("failed to load config", status.message());
assert_eq!("NotFound: missing input", status.source().unwrap().to_string());
}
#[test]
fn result_ext_adds_lazy_context() {
let result: status::Result<()> = Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"denied",
))
.with_context(|| "failed to read input");
let status = result.unwrap_err();
assert_eq!(Code::PermissionDenied, status.code());
assert_eq!("failed to read input", status.message());
assert_eq!("PermissionDenied: denied", status.source().unwrap().to_string());
}
#[test]
fn io_error_maps_not_found() {
let status = Status::from(std::io::Error::new(