use std::fmt;
#[derive(Debug, thiserror::Error)]
pub enum CoreError {
#[error("{}", .0.as_deref().unwrap_or("cancelled"))]
Cancelled(Option<String>),
#[error("canonical encoding failed: {0}")]
Encoding(String),
#[error("missing environment variable: {0}")]
MissingEnv(String),
#[error("filesystem: {0}")]
Io(#[from] std::io::Error),
}
impl CoreError {
pub fn cancelled() -> Self {
CoreError::Cancelled(None)
}
pub fn cancelled_with(reason: impl Into<String>) -> Self {
CoreError::Cancelled(Some(reason.into()))
}
pub fn is_cancelled(&self) -> bool {
matches!(self, CoreError::Cancelled(_))
}
}
pub type CoreResult<T> = Result<T, CoreError>;
impl CoreError {
pub fn detail(&self) -> impl fmt::Display + '_ {
struct D<'a>(&'a CoreError);
impl fmt::Display for D<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
D(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cancelled_default_message_is_byte_exact() {
assert_eq!(CoreError::cancelled().to_string(), "cancelled");
}
#[test]
fn cancelled_with_reason_uses_the_reason() {
assert_eq!(
CoreError::cancelled_with("user aborted").to_string(),
"user aborted"
);
assert!(CoreError::cancelled_with("x").is_cancelled());
}
#[test]
fn non_cancel_variants_are_not_cancelled() {
assert!(!CoreError::MissingEnv("X".into()).is_cancelled());
assert_eq!(
CoreError::MissingEnv("OPENAI_API_KEY".into()).to_string(),
"missing environment variable: OPENAI_API_KEY"
);
}
}