1use thiserror::Error;
7
8pub type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Error, Debug)]
15pub enum Error {
16 #[error("HTTP request failed: {0}")]
18 Http(#[from] reqwest::Error),
19
20 #[error("JSON error: {0}")]
22 Json(#[from] serde_json::Error),
23
24 #[error("Invalid configuration: {0}")]
26 Config(String),
27
28 #[error("API error{}: {message}", .status.map(|code| format!(" {code}")).unwrap_or_default())]
33 Api {
34 status: Option<u16>,
36
37 message: String,
39 },
40
41 #[error("Streaming error: {0}")]
43 Stream(String),
44
45 #[error("Tool execution error: {0}")]
47 Tool(String),
48
49 #[error("Invalid input: {0}")]
51 InvalidInput(String),
52
53 #[error("Request timeout")]
55 Timeout,
56
57 #[error("Error: {0}")]
59 Other(String),
60}
61
62impl Error {
63 pub fn config(msg: impl Into<String>) -> Self {
65 Error::Config(msg.into())
66 }
67
68 pub fn api(msg: impl Into<String>) -> Self {
73 Error::Api {
74 status: None,
75 message: msg.into(),
76 }
77 }
78
79 pub fn api_status(status: u16, msg: impl Into<String>) -> Self {
100 Error::Api {
101 status: Some(status),
102 message: msg.into(),
103 }
104 }
105
106 pub fn status_code(&self) -> Option<u16> {
110 match self {
111 Error::Api { status, .. } => *status,
112 _ => None,
113 }
114 }
115
116 pub fn stream(msg: impl Into<String>) -> Self {
118 Error::Stream(msg.into())
119 }
120
121 pub fn tool(msg: impl Into<String>) -> Self {
123 Error::Tool(msg.into())
124 }
125
126 pub fn invalid_input(msg: impl Into<String>) -> Self {
128 Error::InvalidInput(msg.into())
129 }
130
131 pub fn other(msg: impl Into<String>) -> Self {
133 Error::Other(msg.into())
134 }
135
136 pub fn timeout() -> Self {
138 Error::Timeout
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_error_config() {
148 let err = Error::config("Invalid model");
149 assert!(matches!(err, Error::Config(_)));
150 assert_eq!(err.to_string(), "Invalid configuration: Invalid model");
151 }
152
153 #[test]
154 fn test_error_api() {
155 let err = Error::api("Internal Server Error");
156 assert!(matches!(err, Error::Api { status: None, .. }));
157 assert_eq!(err.to_string(), "API error: Internal Server Error");
158
159 let err = Error::api_status(500, "Internal Server Error");
161 assert!(matches!(
162 err,
163 Error::Api {
164 status: Some(500),
165 ..
166 }
167 ));
168 assert_eq!(err.to_string(), "API error 500: Internal Server Error");
169 assert_eq!(err.status_code(), Some(500));
170 }
171
172 #[test]
173 fn test_error_stream() {
174 let err = Error::stream("Connection lost");
175 assert!(matches!(err, Error::Stream(_)));
176 assert_eq!(err.to_string(), "Streaming error: Connection lost");
177 }
178
179 #[test]
180 fn test_error_tool() {
181 let err = Error::tool("Tool not found");
182 assert!(matches!(err, Error::Tool(_)));
183 assert_eq!(err.to_string(), "Tool execution error: Tool not found");
184 }
185
186 #[test]
187 fn test_error_invalid_input() {
188 let err = Error::invalid_input("Missing parameter");
189 assert!(matches!(err, Error::InvalidInput(_)));
190 assert_eq!(err.to_string(), "Invalid input: Missing parameter");
191 }
192
193 #[test]
194 fn test_error_timeout() {
195 let err = Error::timeout();
196 assert!(matches!(err, Error::Timeout));
197 assert_eq!(err.to_string(), "Request timeout");
198 }
199
200 #[test]
201 fn test_error_other() {
202 let err = Error::other("Something went wrong");
203 assert!(matches!(err, Error::Other(_)));
204 assert_eq!(err.to_string(), "Error: Something went wrong");
205 }
206
207 #[test]
208 fn test_error_from_serde_json() {
209 let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
211 let err: Error = json_err.into();
212 assert!(matches!(err, Error::Json(_)));
213 }
214}