use thiserror::Error;
#[derive(Debug, Error)]
pub enum EditError {
#[error("policy block: {0}")]
PolicyBlock(#[from] PolicyBlockError),
#[error("runtime error: {0}")]
Runtime(#[from] anyhow::Error),
}
#[derive(Debug, Error)]
pub enum PolicyBlockError {
#[error("precondition mismatch: {message}")]
PreconditionMismatch {
message: String,
},
#[error("safety gate denial: {message}")]
SafetyGateDenial {
message: String,
},
#[error("policy denial: {message}")]
PolicyDenial {
message: String,
},
#[error("caps exceeded: {message}")]
CapsExceeded {
message: String,
},
}
impl EditError {
pub fn is_policy_block(&self) -> bool {
matches!(self, EditError::PolicyBlock(_))
}
pub fn exit_code(&self) -> u8 {
match self {
EditError::PolicyBlock(_) => 2,
EditError::Runtime(_) => 1,
}
}
}
pub type EditResult<T> = Result<T, EditError>;
#[cfg(test)]
mod tests {
use super::{EditError, PolicyBlockError};
#[test]
fn policy_block_reports_exit_code_2() {
let err = EditError::from(PolicyBlockError::PreconditionMismatch {
message: "oops".to_string(),
});
assert!(err.is_policy_block());
assert_eq!(err.exit_code(), 2);
assert!(err.to_string().contains("policy block"));
}
#[test]
fn runtime_error_reports_exit_code_1() {
let err = EditError::from(anyhow::anyhow!("boom"));
assert!(!err.is_policy_block());
assert_eq!(err.exit_code(), 1);
assert!(err.to_string().contains("runtime error"));
}
#[test]
fn policy_block_display_includes_variant() {
let err = PolicyBlockError::SafetyGateDenial {
message: "guarded".to_string(),
};
assert!(err.to_string().contains("safety gate denial"));
assert!(err.to_string().contains("guarded"));
}
}