opencode_sdk/types/
error.rs1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
12#[serde(rename_all = "camelCase")]
13pub struct APIError {
14 pub message: String,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
18 pub status_code: Option<u16>,
19 pub is_retryable: bool,
21 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub response_headers: Option<HashMap<String, String>>,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub response_body: Option<String>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
29 pub metadata: Option<HashMap<String, String>>,
30}
31
32impl std::fmt::Display for APIError {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 write!(f, "{}", self.message)?;
35 if let Some(code) = self.status_code {
36 write!(f, " (status: {})", code)?;
37 }
38 Ok(())
39 }
40}
41
42impl std::error::Error for APIError {}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn test_api_error_minimal() {
50 let json = r#"{"message":"Something went wrong","isRetryable":false}"#;
51 let error: APIError = serde_json::from_str(json).unwrap();
52 assert_eq!(error.message, "Something went wrong");
53 assert!(!error.is_retryable);
54 assert!(error.status_code.is_none());
55 }
56
57 #[test]
58 fn test_api_error_full() {
59 let json = r#"{
60 "message": "Rate limited",
61 "statusCode": 429,
62 "isRetryable": true,
63 "responseHeaders": {"retry-after": "60"},
64 "responseBody": "Too many requests",
65 "metadata": {"region": "us-east-1"}
66 }"#;
67 let error: APIError = serde_json::from_str(json).unwrap();
68 assert_eq!(error.message, "Rate limited");
69 assert_eq!(error.status_code, Some(429));
70 assert!(error.is_retryable);
71 assert_eq!(
72 error.response_headers.as_ref().unwrap().get("retry-after"),
73 Some(&"60".to_string())
74 );
75 assert_eq!(error.response_body, Some("Too many requests".to_string()));
76 assert_eq!(
77 error.metadata.as_ref().unwrap().get("region"),
78 Some(&"us-east-1".to_string())
79 );
80 }
81
82 #[test]
83 fn test_api_error_display() {
84 let error = APIError {
85 message: "Not found".to_string(),
86 status_code: Some(404),
87 is_retryable: false,
88 response_headers: None,
89 response_body: None,
90 metadata: None,
91 };
92 assert_eq!(error.to_string(), "Not found (status: 404)");
93 }
94
95 #[test]
96 fn test_api_error_roundtrip() {
97 let error = APIError {
98 message: "Test error".to_string(),
99 status_code: Some(500),
100 is_retryable: true,
101 response_headers: Some(HashMap::from([(
102 "x-request-id".to_string(),
103 "123".to_string(),
104 )])),
105 response_body: Some("Internal error".to_string()),
106 metadata: None,
107 };
108 let json = serde_json::to_string(&error).unwrap();
109 let parsed: APIError = serde_json::from_str(&json).unwrap();
110 assert_eq!(error, parsed);
111 }
112}