vtx_sdk/modules/net/http.rs
1//! Host-side HTTP request/response helpers.
2
3use crate::bindings::vtx::api::vtx_types::{HttpRequest, HttpResponse};
4use crate::error::VtxError;
5use crate::modules::io::fs;
6
7/// Standardized request and response type aliases (aligned with WIT interfaces).
8pub type Request = HttpRequest;
9pub type Response = HttpResponse;
10
11/// HTTP response builder (suitable for plugin runtime).
12///
13/// Provides the following construction capabilities:
14/// - Success response (JSON).
15/// - Error response (automatic mapping of status codes and structures).
16/// - File stream response (opens files based on host UUID).
17/// - Status code response (pure status code, no body).
18///
19pub struct ResponseBuilder;
20
21impl ResponseBuilder {
22 /// Constructs a JSON response (200 OK).
23 ///
24 /// 鈿狅笍 If serialization fails, `[]` is returned as fallback content; **this does not indicate logic success**.
25 pub fn json<T: serde::Serialize>(data: &T) -> Response {
26 let json_bytes = serde_json::to_vec(data).unwrap_or_else(|_| b"[]".to_vec());
27
28 // Encapsulate JSON data using the memory buffer provided by the host.
29 let buffer = fs::create_memory_buffer(&json_bytes);
30 HttpResponse {
31 status: 200,
32 body: Some(buffer),
33 }
34 }
35
36 /// Constructs an error response (automatically maps HTTP status codes based on error type).
37 ///
38 /// - `AuthDenied(code)` 鈫?`code` (401 / 403)
39 /// - `NotFound(_)` 鈫?404
40 /// - `PermissionDenied(_)` 鈫?403
41 /// - `SerializationError(_)` 鈫?400
42 /// - `DatabaseError(_)`, `Internal(_)` 鈫?500
43 ///
44 /// Return structure:
45 /// ```json
46 /// {
47 /// "success": false,
48 /// "error": true,
49 /// "code": 403,
50 /// "type": "PermissionDenied",
51 /// "message": "You are not allowed to access this resource"
52 /// }
53 /// ```
54 pub fn error(err: VtxError) -> Response {
55 let (status, message) = match &err {
56 VtxError::AuthDenied(code) => (*code, format!("Authentication failed: {}", err)),
57 VtxError::NotFound(msg) => (404, msg.clone()),
58 VtxError::PermissionDenied(msg) => (403, msg.clone()),
59 VtxError::SerializationError(msg) => (400, format!("Bad Request: {}", msg)),
60 VtxError::DatabaseError(msg) => (500, format!("Database Error: {}", msg)),
61 VtxError::Internal(msg) => (500, format!("Internal Error: {}", msg)),
62 };
63
64 let error_body = serde_json::json!({
65 "success": false, // Unified boolean failure indicator
66 "error": true, // Marked as error response
67 "code": status, // Mapped HTTP status code
68 "type": format!("{:?}", err), // Error type (for debugging)
69 "message": message // Error message (user-visible)
70 });
71
72 let mut resp = Self::json(&error_body);
73 resp.status = status;
74 resp
75 }
76
77 /// Constructs a file stream response (opens via host interface by UUID).
78 ///
79 /// - Success: 200 + file content stream.
80 /// - Failure: Returns 404 JSON error response.
81 pub fn file(uri: &str) -> Response {
82 match fs::open_uri(uri) {
83 Ok(buffer) => HttpResponse {
84 status: 200,
85 body: Some(buffer),
86 },
87 Err(e) => Self::error(VtxError::NotFound(format!("File not found: {}", e))),
88 }
89 }
90
91 /// Constructs a pure status code response (no body).
92 ///
93 /// Used for scenarios such as 204 No Content, 403 Forbidden, etc.
94 pub fn status(code: u16) -> Response {
95 HttpResponse {
96 status: code,
97 body: None,
98 }
99 }
100
101 /// Constructs a standard 404 Not Found response (no body).
102 pub fn not_found() -> Response {
103 Self::status(404)
104 }
105}