Skip to main content

rustbridge_transport/
envelope.rs

1//! Request and response envelope types for FFI transport
2
3use serde::{Deserialize, Serialize, de::DeserializeOwned};
4
5/// Request envelope wrapping a message for FFI transport
6///
7/// The type_tag identifies the handler, and payload contains the
8/// serialized request data.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct RequestEnvelope {
11    /// Message type identifier (e.g., "user.create", "order.submit")
12    pub type_tag: String,
13
14    /// Serialized request payload (JSON)
15    pub payload: serde_json::Value,
16
17    /// Optional request ID for correlation
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub request_id: Option<u64>,
20
21    /// Optional correlation ID for distributed tracing
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub correlation_id: Option<String>,
24}
25
26impl RequestEnvelope {
27    /// Create a new request envelope
28    pub fn new(type_tag: impl Into<String>, payload: serde_json::Value) -> Self {
29        Self {
30            type_tag: type_tag.into(),
31            payload,
32            request_id: None,
33            correlation_id: None,
34        }
35    }
36
37    /// Set request ID
38    pub fn with_request_id(mut self, id: u64) -> Self {
39        self.request_id = Some(id);
40        self
41    }
42
43    /// Set correlation ID
44    pub fn with_correlation_id(mut self, id: impl Into<String>) -> Self {
45        self.correlation_id = Some(id.into());
46        self
47    }
48
49    /// Create from type tag and serializable payload
50    pub fn from_typed<T: Serialize>(
51        type_tag: impl Into<String>,
52        payload: &T,
53    ) -> Result<Self, serde_json::Error> {
54        Ok(Self {
55            type_tag: type_tag.into(),
56            payload: serde_json::to_value(payload)?,
57            request_id: None,
58            correlation_id: None,
59        })
60    }
61
62    /// Deserialize the payload to a typed value
63    ///
64    /// This method deserializes directly from the JSON value without cloning.
65    pub fn payload_as<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
66        T::deserialize(&self.payload)
67    }
68
69    /// Serialize to bytes for FFI transport
70    pub fn to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
71        serde_json::to_vec(self)
72    }
73
74    /// Deserialize from bytes
75    pub fn from_bytes(data: &[u8]) -> Result<Self, serde_json::Error> {
76        serde_json::from_slice(data)
77    }
78}
79
80/// Response status indicating success or failure
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
82#[serde(rename_all = "snake_case")]
83pub enum ResponseStatus {
84    /// Request completed successfully
85    Success,
86    /// Request failed with an error
87    Error,
88}
89
90/// Response envelope wrapping a response for FFI transport
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct ResponseEnvelope {
93    /// Response status
94    pub status: ResponseStatus,
95
96    /// Serialized response payload (on success) or null
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub payload: Option<serde_json::Value>,
99
100    /// Error code (on failure)
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub error_code: Option<u32>,
103
104    /// Error message (on failure)
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub error_message: Option<String>,
107
108    /// Original request ID for correlation
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub request_id: Option<u64>,
111}
112
113impl ResponseEnvelope {
114    /// Create a success response with payload
115    pub fn success(payload: serde_json::Value) -> Self {
116        Self {
117            status: ResponseStatus::Success,
118            payload: Some(payload),
119            error_code: None,
120            error_message: None,
121            request_id: None,
122        }
123    }
124
125    /// Create a success response from a serializable value
126    pub fn success_typed<T: Serialize>(value: &T) -> Result<Self, serde_json::Error> {
127        Ok(Self::success(serde_json::to_value(value)?))
128    }
129
130    /// Create a success response from raw bytes (already JSON-encoded)
131    pub fn success_raw(data: &[u8]) -> Result<Self, serde_json::Error> {
132        let payload: serde_json::Value = serde_json::from_slice(data)?;
133        Ok(Self::success(payload))
134    }
135
136    /// Create an error response
137    pub fn error(code: u32, message: impl Into<String>) -> Self {
138        Self {
139            status: ResponseStatus::Error,
140            payload: None,
141            error_code: Some(code),
142            error_message: Some(message.into()),
143            request_id: None,
144        }
145    }
146
147    /// Create an error response from a PluginError
148    pub fn from_error(err: &rustbridge_core::PluginError) -> Self {
149        Self::error(err.error_code(), err.to_string())
150    }
151
152    /// Set request ID for correlation
153    pub fn with_request_id(mut self, id: u64) -> Self {
154        self.request_id = Some(id);
155        self
156    }
157
158    /// Check if this is a success response
159    pub fn is_success(&self) -> bool {
160        self.status == ResponseStatus::Success
161    }
162
163    /// Get the payload if success
164    ///
165    /// This method deserializes directly from the JSON value without cloning.
166    pub fn payload_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
167        match &self.payload {
168            Some(v) => Ok(Some(T::deserialize(v)?)),
169            None => Ok(None),
170        }
171    }
172
173    /// Serialize to bytes for FFI transport
174    pub fn to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
175        serde_json::to_vec(self)
176    }
177
178    /// Deserialize from bytes
179    pub fn from_bytes(data: &[u8]) -> Result<Self, serde_json::Error> {
180        serde_json::from_slice(data)
181    }
182}
183
184impl Default for ResponseEnvelope {
185    fn default() -> Self {
186        Self::success(serde_json::Value::Null)
187    }
188}
189
190#[cfg(test)]
191#[path = "envelope/envelope_tests.rs"]
192mod envelope_tests;