Skip to main content

deckyfx_dioxus_ipc_bridge/
response.rs

1//! IPC Response Types
2//!
3//! Defines response and error types for IPC communication.
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::collections::HashMap;
8use std::fmt;
9
10/// HTTP-like IPC response from Rust to JavaScript
11///
12/// # Example
13/// ```rust
14/// use dioxus_ipc_bridge::response::IpcResponse;
15/// use serde_json::json;
16/// use std::collections::HashMap;
17///
18/// let response = IpcResponse {
19///     status: 200,
20///     headers: HashMap::from([("Content-Type".to_string(), "application/json".to_string())]),
21///     body: json!({ "success": true, "message": "Form submitted!" }),
22/// };
23/// ```
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct IpcResponse {
26    /// HTTP-like status code
27    /// - 200: Success
28    /// - 400: Bad Request (invalid input)
29    /// - 404: Not Found (no route matches)
30    /// - 500: Internal Server Error (handler error)
31    pub status: u16,
32
33    /// Response headers
34    pub headers: HashMap<String, String>,
35
36    /// Response body (always JSON)
37    pub body: Value,
38}
39
40impl IpcResponse {
41    /// Create a successful (200 OK) response
42    ///
43    /// # Arguments
44    /// * `body` - JSON response body
45    ///
46    /// # Example
47    /// ```rust
48    /// use dioxus_ipc_bridge::response::IpcResponse;
49    /// use serde_json::json;
50    ///
51    /// let response = IpcResponse::ok(json!({ "message": "Success!" }));
52    /// ```
53    pub fn ok(body: Value) -> Self {
54        Self {
55            status: 200,
56            headers: HashMap::from([(
57                "Content-Type".to_string(),
58                "application/json".to_string(),
59            )]),
60            body,
61        }
62    }
63
64    /// Create a bad request (400) error response
65    ///
66    /// # Arguments
67    /// * `message` - Error message
68    ///
69    /// # Example
70    /// ```rust
71    /// use dioxus_ipc_bridge::response::IpcResponse;
72    ///
73    /// let response = IpcResponse::bad_request("Missing 'name' field");
74    /// ```
75    pub fn bad_request(message: &str) -> Self {
76        Self {
77            status: 400,
78            headers: HashMap::from([(
79                "Content-Type".to_string(),
80                "application/json".to_string(),
81            )]),
82            body: serde_json::json!({
83                "error": "Bad Request",
84                "message": message
85            }),
86        }
87    }
88
89    /// Create a not found (404) error response
90    ///
91    /// # Arguments
92    /// * `path` - The path that was not found
93    ///
94    /// # Example
95    /// ```rust
96    /// use dioxus_ipc_bridge::response::IpcResponse;
97    ///
98    /// let response = IpcResponse::not_found("/unknown/route");
99    /// ```
100    pub fn not_found(path: &str) -> Self {
101        Self {
102            status: 404,
103            headers: HashMap::from([(
104                "Content-Type".to_string(),
105                "application/json".to_string(),
106            )]),
107            body: serde_json::json!({
108                "error": "Not Found",
109                "message": format!("No route matches '{}'", path)
110            }),
111        }
112    }
113
114    /// Create an internal server error (500) response
115    ///
116    /// # Arguments
117    /// * `error` - Error details
118    ///
119    /// # Example
120    /// ```rust
121    /// use dioxus_ipc_bridge::response::IpcResponse;
122    ///
123    /// let response = IpcResponse::internal_error("Database connection failed");
124    /// ```
125    pub fn internal_error(error: &str) -> Self {
126        Self {
127            status: 500,
128            headers: HashMap::from([(
129                "Content-Type".to_string(),
130                "application/json".to_string(),
131            )]),
132            body: serde_json::json!({
133                "error": "Internal Server Error",
134                "message": error
135            }),
136        }
137    }
138
139    /// Create a custom response with specific status code
140    pub fn with_status(status: u16, body: Value) -> Self {
141        Self {
142            status,
143            headers: HashMap::from([(
144                "Content-Type".to_string(),
145                "application/json".to_string(),
146            )]),
147            body,
148        }
149    }
150
151    /// Add a header to the response
152    pub fn with_header(mut self, key: String, value: String) -> Self {
153        self.headers.insert(key, value);
154        self
155    }
156}
157
158/// IPC error types
159///
160/// Represents various error conditions that can occur during IPC routing and handling.
161#[derive(Debug, Clone)]
162pub enum IpcError {
163    /// Invalid request format
164    BadRequest(String),
165
166    /// Route not found
167    NotFound(String),
168
169    /// Handler execution error
170    InternalError(String),
171
172    /// URL parsing error
173    ParseError(String),
174
175    /// Unauthorized access
176    Unauthorized,
177
178    /// Forbidden access
179    Forbidden(String),
180}
181
182impl fmt::Display for IpcError {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        match self {
185            IpcError::BadRequest(msg) => write!(f, "Bad Request: {}", msg),
186            IpcError::NotFound(msg) => write!(f, "Not Found: {}", msg),
187            IpcError::InternalError(msg) => write!(f, "Internal Error: {}", msg),
188            IpcError::ParseError(msg) => write!(f, "Parse Error: {}", msg),
189            IpcError::Unauthorized => write!(f, "Unauthorized"),
190            IpcError::Forbidden(msg) => write!(f, "Forbidden: {}", msg),
191        }
192    }
193}
194
195impl std::error::Error for IpcError {}
196
197impl From<IpcError> for IpcResponse {
198    fn from(error: IpcError) -> Self {
199        match error {
200            IpcError::BadRequest(msg) => IpcResponse::bad_request(&msg),
201            IpcError::NotFound(msg) => IpcResponse::not_found(&msg),
202            IpcError::InternalError(msg) => IpcResponse::internal_error(&msg),
203            IpcError::ParseError(msg) => {
204                IpcResponse::bad_request(&format!("Parse error: {}", msg))
205            }
206            IpcError::Unauthorized => IpcResponse::with_status(
207                401,
208                serde_json::json!({
209                    "error": "Unauthorized",
210                    "message": "Authentication required"
211                }),
212            ),
213            IpcError::Forbidden(msg) => IpcResponse::with_status(
214                403,
215                serde_json::json!({
216                    "error": "Forbidden",
217                    "message": msg
218                }),
219            ),
220        }
221    }
222}