1use thiserror::Error;
4
5use crate::types::RequestId;
6
7pub type Result<T> = std::result::Result<T, FusilladeError>;
9
10#[derive(Error, Debug)]
12pub enum FusilladeError {
13 #[error("Request not found: {0}")]
15 RequestNotFound(RequestId),
16
17 #[error("Request {id} is in state '{current_state}', expected one of: {expected}")]
25 RequestStateConflict {
26 id: RequestId,
27 current_state: String,
28 expected: &'static str,
29 },
30
31 #[error("Request cancelled: {0}")]
33 RequestCancelled(RequestId),
34
35 #[error("Daemon is shutting down")]
37 Shutdown,
38
39 #[error("Invalid state transition: request {0} is in state '{1}', expected '{2}'")]
41 InvalidState(RequestId, String, String),
42
43 #[error("Validation error: {0}")]
45 ValidationError(String),
46
47 #[error("HTTP request failed: {0}")]
49 HttpClient(String),
50
51 #[error("HTTP request builder failed: {0}")]
53 HttpRequestBuilder(String),
54
55 #[error("HTTP request timed out: {0}")]
57 HttpClientTimeout(String),
58
59 #[error("First chunk timeout: {0}")]
63 FirstChunkTimeout(String),
64
65 #[error("Tokens timeout: {0}")]
67 TokensTimeout(String),
68
69 #[error("Body timeout: {0}")]
72 BodyTimeout(String),
73
74 #[error("Serialization error: {0}")]
76 Serialization(#[from] serde_json::Error),
77
78 #[error(transparent)]
80 Other(#[from] anyhow::Error),
81}
82
83pub mod error_serialization {
89 use anyhow::Error;
90 use serde::{Deserialize, Serialize};
91
92 #[derive(Debug, Clone, Serialize, Deserialize)]
94 pub struct SerializedError {
95 pub message: String,
97 pub sources: Vec<String>,
99 }
100
101 pub fn serialize_error(error: &Error) -> String {
105 let serialized = SerializedError {
106 message: error.to_string(),
107 sources: error.chain().skip(1).map(|e| e.to_string()).collect(),
108 };
109 serde_json::to_string(&serialized).unwrap_or_else(|_| {
110 format!(
111 r#"{{"message":"{}","sources":[]}}"#,
112 error.to_string().replace('"', "\\\"")
113 )
114 })
115 }
116
117 pub fn deserialize_error(json: &str) -> Error {
121 match serde_json::from_str::<SerializedError>(json) {
122 Ok(serialized) => {
123 let mut error_msg = serialized.message;
124 if !serialized.sources.is_empty() {
125 error_msg.push_str("\nCaused by:\n");
126 for (i, source) in serialized.sources.iter().enumerate() {
127 error_msg.push_str(&format!(" {}: {}\n", i + 1, source));
128 }
129 }
130 anyhow::anyhow!(error_msg)
131 }
132 Err(_) => {
133 anyhow::anyhow!("Deserialization failed: {}", json)
135 }
136 }
137 }
138
139 #[cfg(test)]
140 mod tests {
141 use super::*;
142
143 #[test]
144 fn test_serialize_deserialize_simple_error() {
145 let error = anyhow::anyhow!("Test error");
146 let serialized = serialize_error(&error);
147 let deserialized = deserialize_error(&serialized);
148 assert_eq!(error.to_string(), deserialized.to_string());
149 }
150
151 #[test]
152 fn test_serialize_deserialize_with_context() {
153 let error = anyhow::anyhow!("Root cause")
154 .context("Middle context")
155 .context("Top context");
156 let serialized = serialize_error(&error);
157 let deserialized = deserialize_error(&serialized);
158 assert!(deserialized.to_string().contains("Top context"));
160 assert!(deserialized.to_string().contains("Middle context"));
161 assert!(deserialized.to_string().contains("Root cause"));
162 }
163 }
164}