agent_tui/ipc/
error.rs

1use serde_json::Value;
2use thiserror::Error;
3
4use crate::ipc::error_codes::ErrorCategory;
5
6#[derive(Error, Debug)]
7pub enum ClientError {
8    #[error("Failed to connect to daemon: {0}")]
9    ConnectionFailed(#[from] std::io::Error),
10
11    #[error("Failed to serialize request: {0}")]
12    SerializationFailed(#[from] serde_json::Error),
13
14    #[error("RPC error ({code}): {message}")]
15    RpcError {
16        code: i32,
17        message: String,
18        category: Option<ErrorCategory>,
19        retryable: bool,
20        context: Option<Value>,
21        suggestion: Option<String>,
22    },
23
24    #[error("Daemon not running")]
25    DaemonNotRunning,
26
27    #[error("Invalid response from daemon")]
28    InvalidResponse,
29
30    #[error("Failed to send signal to process {pid}: {message}")]
31    SignalFailed { pid: u32, message: String },
32}
33
34impl ClientError {
35    pub fn is_retryable(&self) -> bool {
36        match self {
37            ClientError::RpcError { retryable, .. } => *retryable,
38            _ => false,
39        }
40    }
41
42    pub fn category(&self) -> Option<ErrorCategory> {
43        match self {
44            ClientError::RpcError { category, .. } => *category,
45            _ => None,
46        }
47    }
48
49    pub fn suggestion(&self) -> Option<&str> {
50        match self {
51            ClientError::RpcError { suggestion, .. } => suggestion.as_deref(),
52            _ => None,
53        }
54    }
55
56    pub fn context(&self) -> Option<&Value> {
57        match self {
58            ClientError::RpcError { context, .. } => context.as_ref(),
59            _ => None,
60        }
61    }
62
63    pub fn to_json(&self) -> Value {
64        match self {
65            ClientError::RpcError {
66                code,
67                message,
68                category,
69                retryable,
70                context,
71                suggestion,
72            } => {
73                let mut obj = serde_json::json!({
74                    "code": code,
75                    "message": message,
76                    "retryable": retryable,
77                });
78                if let Some(cat) = category {
79                    obj["category"] = serde_json::json!(cat.as_str());
80                }
81                if let Some(ctx) = context {
82                    obj["context"] = ctx.clone();
83                }
84                if let Some(sug) = suggestion {
85                    obj["suggestion"] = serde_json::json!(sug);
86                }
87                obj
88            }
89            ClientError::ConnectionFailed(e) => serde_json::json!({
90                "code": -32000,
91                "message": format!("Connection failed: {}", e),
92                "category": "external",
93                "retryable": true,
94            }),
95            ClientError::DaemonNotRunning => serde_json::json!({
96                "code": -32000,
97                "message": "Daemon not running",
98                "category": "external",
99                "retryable": false,
100                "suggestion": "Start daemon with: agent-tui daemon",
101            }),
102            ClientError::InvalidResponse => serde_json::json!({
103                "code": -32000,
104                "message": "Invalid response from daemon",
105                "category": "internal",
106                "retryable": false,
107            }),
108            ClientError::SerializationFailed(e) => serde_json::json!({
109                "code": -32000,
110                "message": format!("Serialization failed: {}", e),
111                "category": "internal",
112                "retryable": false,
113            }),
114            ClientError::SignalFailed { pid, message } => serde_json::json!({
115                "code": -32000,
116                "message": format!("Failed to send signal to process {}: {}", pid, message),
117                "category": "external",
118                "retryable": false,
119                "exit_code": 74,
120            }),
121        }
122    }
123}