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}