use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ToolCallError {
#[error("invalid arguments: {0}")]
InvalidArgs(String),
#[error("execution failed ({code}): {message}")]
ExecutionFailed {
code: String,
message: String,
retryable: bool,
},
#[error("internal error: {0}")]
InternalError(String),
#[error("rate limited ({tool_id}): max_concurrent={limit} in-flight")]
RateLimited {
tool_id: String,
limit: u32,
retry_after_ms: Option<u64>,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn invalid_args_display_format() {
let e = ToolCallError::InvalidArgs("missing field `path`".into());
assert_eq!(format!("{e}"), "invalid arguments: missing field `path`");
}
#[test]
fn execution_failed_display_includes_code_and_message() {
let e = ToolCallError::ExecutionFailed {
code: "EPERM".into(),
message: "denied".into(),
retryable: false,
};
let s = format!("{e}");
assert!(s.contains("EPERM"));
assert!(s.contains("denied"));
}
#[test]
fn internal_error_display_format() {
let e = ToolCallError::InternalError("logic bug".into());
assert_eq!(format!("{e}"), "internal error: logic bug");
}
#[test]
fn enum_is_non_exhaustive_at_api_boundary() {
let e = ToolCallError::InvalidArgs("x".into());
match e {
ToolCallError::InvalidArgs(_) => {}
ToolCallError::ExecutionFailed { .. } => {}
ToolCallError::InternalError(_) => {}
ToolCallError::RateLimited { .. } => {}
}
}
}