vtx-sdk 0.1.14

Official SDK for developing VTX plugins using Rust and WebAssembly.
Documentation
//! Host-side HTTP request/response helpers.

use crate::bindings::vtx::api::vtx_types::{HttpRequest, HttpResponse};
use crate::error::VtxError;
use crate::modules::io::fs;

/// Standardized request and response type aliases (aligned with WIT interfaces).
pub type Request = HttpRequest;
pub type Response = HttpResponse;

/// HTTP response builder (suitable for plugin runtime).
///
/// Provides the following construction capabilities:
/// - Success response (JSON).
/// - Error response (automatic mapping of status codes and structures).
/// - File stream response (opens files based on host UUID).
/// - Status code response (pure status code, no body).
///
pub struct ResponseBuilder;

impl ResponseBuilder {
    /// Constructs a JSON response (200 OK).
    ///
    /// 鈿狅笍 If serialization fails, `[]` is returned as fallback content; **this does not indicate logic success**.
    pub fn json<T: serde::Serialize>(data: &T) -> Response {
        let json_bytes = serde_json::to_vec(data).unwrap_or_else(|_| b"[]".to_vec());

        // Encapsulate JSON data using the memory buffer provided by the host.
        let buffer = fs::create_memory_buffer(&json_bytes);
        HttpResponse {
            status: 200,
            body: Some(buffer),
        }
    }

    /// Constructs an error response (automatically maps HTTP status codes based on error type).
    ///
    /// - `AuthDenied(code)` 鈫?`code` (401 / 403)
    /// - `NotFound(_)` 鈫?404
    /// - `PermissionDenied(_)` 鈫?403
    /// - `SerializationError(_)` 鈫?400
    /// - `DatabaseError(_)`, `Internal(_)` 鈫?500
    ///
    /// Return structure:
    /// ```json
    /// {
    ///   "success": false,
    ///   "error": true,
    ///   "code": 403,
    ///   "type": "PermissionDenied",
    ///   "message": "You are not allowed to access this resource"
    /// }
    /// ```
    pub fn error(err: VtxError) -> Response {
        let (status, message) = match &err {
            VtxError::AuthDenied(code) => (*code, format!("Authentication failed: {}", err)),
            VtxError::NotFound(msg) => (404, msg.clone()),
            VtxError::PermissionDenied(msg) => (403, msg.clone()),
            VtxError::SerializationError(msg) => (400, format!("Bad Request: {}", msg)),
            VtxError::DatabaseError(msg) => (500, format!("Database Error: {}", msg)),
            VtxError::Internal(msg) => (500, format!("Internal Error: {}", msg)),
        };

        let error_body = serde_json::json!({
            "success": false,                  // Unified boolean failure indicator
            "error": true,                     // Marked as error response
            "code": status,                    // Mapped HTTP status code
            "type": format!("{:?}", err),      // Error type (for debugging)
            "message": message                 // Error message (user-visible)
        });

        let mut resp = Self::json(&error_body);
        resp.status = status;
        resp
    }

    /// Constructs a file stream response (opens via host interface by UUID).
    ///
    /// - Success: 200 + file content stream.
    /// - Failure: Returns 404 JSON error response.
    pub fn file(uri: &str) -> Response {
        match fs::open_uri(uri) {
            Ok(buffer) => HttpResponse {
                status: 200,
                body: Some(buffer),
            },
            Err(e) => Self::error(VtxError::NotFound(format!("File not found: {}", e))),
        }
    }

    /// Constructs a pure status code response (no body).
    ///
    /// Used for scenarios such as 204 No Content, 403 Forbidden, etc.
    pub fn status(code: u16) -> Response {
        HttpResponse {
            status: code,
            body: None,
        }
    }

    /// Constructs a standard 404 Not Found response (no body).
    pub fn not_found() -> Response {
        Self::status(404)
    }
}