Skip to main content

tokenfold_core/
errors.rs

1#[derive(Debug, thiserror::Error)]
2pub enum TokenFoldError {
3    #[error("invalid input: {0}")]
4    InvalidInput(String),
5    #[error("safety violation: {0}")]
6    SafetyViolation(String),
7    #[error("redaction failed: {0}")]
8    RedactionFailed(String),
9    #[error("estimator error: {0}")]
10    EstimatorError(String),
11    #[error("config error: {0}")]
12    ConfigError(String),
13    #[error("internal error: {0}")]
14    InternalError(String),
15    #[error("io error: {0}")]
16    Io(#[from] std::io::Error),
17}
18
19impl TokenFoldError {
20    /// Stable CLI exit code for this error: 2 invalid input, 3 safety/redaction violation,
21    /// 4 estimator failure, 5 bad configuration, 6 internal or I/O failure. Every successful
22    /// [`crate::Status`] outcome — including `UnreachableTarget`, which is a typed result and
23    /// not an error — exits 0. The proxy's HTTP status mapping and the Python binding's
24    /// exception hierarchy are derived from these same buckets, so the numbering is a public
25    /// contract; `exit_codes_match_error_taxonomy_table` below pins it.
26    pub fn exit_code(&self) -> i32 {
27        match self {
28            TokenFoldError::InvalidInput(_) => 2,
29            TokenFoldError::SafetyViolation(_) | TokenFoldError::RedactionFailed(_) => 3,
30            TokenFoldError::EstimatorError(_) => 4,
31            TokenFoldError::ConfigError(_) => 5,
32            TokenFoldError::InternalError(_) | TokenFoldError::Io(_) => 6,
33        }
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn exit_codes_match_error_taxonomy_table() {
43        assert_eq!(TokenFoldError::InvalidInput("x".into()).exit_code(), 2);
44        assert_eq!(TokenFoldError::SafetyViolation("x".into()).exit_code(), 3);
45        assert_eq!(TokenFoldError::RedactionFailed("x".into()).exit_code(), 3);
46        assert_eq!(TokenFoldError::EstimatorError("x".into()).exit_code(), 4);
47        assert_eq!(TokenFoldError::ConfigError("x".into()).exit_code(), 5);
48        assert_eq!(TokenFoldError::InternalError("x".into()).exit_code(), 6);
49        let io_err = TokenFoldError::from(std::io::Error::other("x"));
50        assert_eq!(io_err.exit_code(), 6);
51    }
52}