universal_tool_core/
error.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use thiserror::Error;
5
6/// The primary error type that all tool functions must return in their Result.
7#[derive(Debug, Error, Serialize, Deserialize, JsonSchema)]
8pub struct ToolError {
9    /// A machine-readable code for the error category.
10    pub code: ErrorCode,
11    /// A human-readable, context-specific error message.
12    pub message: String,
13    /// Additional error context as key-value pairs.
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub details: Option<HashMap<String, serde_json::Value>>,
16}
17
18/// A controlled vocabulary for error types.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
20pub enum ErrorCode {
21    BadRequest,
22    InvalidArgument,
23    NotFound,
24    PermissionDenied,
25    Internal,
26    Timeout,
27    Conflict,
28    NetworkError,
29    ExternalServiceError,
30    ExecutionFailed,
31    SerializationError,
32    IoError,
33}
34
35// Implementation of Display for ToolError to satisfy the Error trait
36impl std::fmt::Display for ToolError {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        write!(f, "[{:?}] {}", self.code, self.message)
39    }
40}
41
42// Convenience constructors
43impl ToolError {
44    /// Create a new ToolError with the given code and message.
45    pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
46        Self {
47            code,
48            message: message.into(),
49            details: None,
50        }
51    }
52
53    /// Add details to this error.
54    pub fn with_details(mut self, details: HashMap<String, serde_json::Value>) -> Self {
55        self.details = Some(details);
56        self
57    }
58
59    /// Add a single detail string to this error.
60    pub fn with_detail(mut self, key: &str, value: impl Into<serde_json::Value>) -> Self {
61        let mut details = self.details.unwrap_or_default();
62        details.insert(key.to_string(), value.into());
63        self.details = Some(details);
64        self
65    }
66
67    // Convenience constructors for common error types
68    pub fn internal(message: impl Into<String>) -> Self {
69        Self::new(ErrorCode::Internal, message)
70    }
71
72    pub fn not_found(message: impl Into<String>) -> Self {
73        Self::new(ErrorCode::NotFound, message)
74    }
75
76    pub fn invalid_input(message: impl Into<String>) -> Self {
77        Self::new(ErrorCode::InvalidArgument, message)
78    }
79
80    pub fn conflict(message: impl Into<String>) -> Self {
81        Self::new(ErrorCode::Conflict, message)
82    }
83}
84
85// From implementations for common error types
86impl From<std::io::Error> for ToolError {
87    fn from(err: std::io::Error) -> Self {
88        Self::new(ErrorCode::ExecutionFailed, err.to_string())
89    }
90}
91
92impl From<serde_json::Error> for ToolError {
93    fn from(err: serde_json::Error) -> Self {
94        Self::new(ErrorCode::InvalidArgument, err.to_string())
95    }
96}
97
98impl From<String> for ToolError {
99    fn from(err: String) -> Self {
100        Self::new(ErrorCode::ExecutionFailed, err)
101    }
102}
103
104impl From<&str> for ToolError {
105    fn from(err: &str) -> Self {
106        Self::new(ErrorCode::ExecutionFailed, err)
107    }
108}