truffle_core/envelope/
mod.rs1pub mod codec;
17
18use serde::{Deserialize, Serialize};
19
20pub use codec::{EnvelopeCodec, JsonCodec};
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct Envelope {
45 pub namespace: String,
48
49 pub msg_type: String,
51
52 pub payload: serde_json::Value,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub timestamp: Option<u64>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub from: Option<String>,
62}
63
64impl Envelope {
65 pub fn new(namespace: &str, msg_type: &str, payload: serde_json::Value) -> Self {
70 Self {
71 namespace: namespace.to_string(),
72 msg_type: msg_type.to_string(),
73 payload,
74 timestamp: None,
75 from: None,
76 }
77 }
78
79 pub fn with_timestamp(mut self) -> Self {
81 let now = std::time::SystemTime::now()
82 .duration_since(std::time::UNIX_EPOCH)
83 .unwrap_or_default()
84 .as_millis() as u64;
85 self.timestamp = Some(now);
86 self
87 }
88
89 pub fn serialize(&self) -> Result<Vec<u8>, EnvelopeError> {
91 serde_json::to_vec(self).map_err(EnvelopeError::Serialization)
92 }
93
94 pub fn deserialize(data: &[u8]) -> Result<Self, EnvelopeError> {
96 serde_json::from_slice(data).map_err(EnvelopeError::Deserialization)
97 }
98}
99
100#[derive(Debug, thiserror::Error)]
106pub enum EnvelopeError {
107 #[error("envelope serialization error: {0}")]
109 Serialization(serde_json::Error),
110
111 #[error("envelope deserialization error: {0}")]
113 Deserialization(serde_json::Error),
114}
115
116#[cfg(test)]
121mod tests {
122 use super::*;
123 use serde_json::json;
124
125 #[test]
126 fn test_envelope_serialize_deserialize_roundtrip() {
127 let envelope = Envelope::new("ft", "offer", json!({"file": "test.txt", "size": 1024}))
128 .with_timestamp();
129
130 let bytes = envelope.serialize().expect("serialize");
131 let decoded = Envelope::deserialize(&bytes).expect("deserialize");
132
133 assert_eq!(decoded.namespace, "ft");
134 assert_eq!(decoded.msg_type, "offer");
135 assert_eq!(decoded.payload["file"], "test.txt");
136 assert_eq!(decoded.payload["size"], 1024);
137 assert!(decoded.timestamp.is_some());
138 assert_eq!(decoded.timestamp, envelope.timestamp);
139 }
140
141 #[test]
142 fn test_envelope_timestamp_generation() {
143 let before = std::time::SystemTime::now()
144 .duration_since(std::time::UNIX_EPOCH)
145 .unwrap()
146 .as_millis() as u64;
147
148 let envelope = Envelope::new("test", "ping", json!(null)).with_timestamp();
149
150 let after = std::time::SystemTime::now()
151 .duration_since(std::time::UNIX_EPOCH)
152 .unwrap()
153 .as_millis() as u64;
154
155 let ts = envelope.timestamp.expect("timestamp should be set");
156 assert!(ts >= before, "timestamp {ts} should be >= {before}");
157 assert!(ts <= after, "timestamp {ts} should be <= {after}");
158 }
159
160 #[test]
161 fn test_envelope_unknown_fields_ignored() {
162 let json_str = r#"{
164 "namespace": "chat",
165 "msg_type": "message",
166 "payload": {"text": "hello"},
167 "timestamp": 1234567890,
168 "from": "peer-1",
169 "future_field": "should be ignored",
170 "another_new_field": 42
171 }"#;
172
173 let envelope: Envelope =
174 serde_json::from_str(json_str).expect("should ignore unknown fields");
175 assert_eq!(envelope.namespace, "chat");
176 assert_eq!(envelope.msg_type, "message");
177 assert_eq!(envelope.payload["text"], "hello");
178 assert_eq!(envelope.timestamp, Some(1234567890));
179 assert_eq!(envelope.from.as_deref(), Some("peer-1"));
180 }
181
182 #[test]
183 fn test_envelope_empty_payload() {
184 let envelope = Envelope::new("bus", "heartbeat", json!(null));
185 let bytes = envelope.serialize().expect("serialize");
186 let decoded = Envelope::deserialize(&bytes).expect("deserialize");
187
188 assert_eq!(decoded.namespace, "bus");
189 assert_eq!(decoded.msg_type, "heartbeat");
190 assert!(decoded.payload.is_null());
191 assert!(decoded.timestamp.is_none());
192 assert!(decoded.from.is_none());
193 }
194
195 #[test]
196 fn test_envelope_large_payload() {
197 let large_data: Vec<u8> = (0..100_000).map(|i| (i % 256) as u8).collect();
199 let payload = json!({"data": large_data});
200
201 let envelope = Envelope::new("stream", "chunk", payload.clone()).with_timestamp();
202 let bytes = envelope.serialize().expect("serialize");
203 let decoded = Envelope::deserialize(&bytes).expect("deserialize");
204
205 assert_eq!(decoded.namespace, "stream");
206 assert_eq!(decoded.msg_type, "chunk");
207 assert_eq!(decoded.payload, payload);
208 }
209
210 #[test]
211 fn test_envelope_optional_fields_not_serialized_when_none() {
212 let envelope = Envelope::new("test", "msg", json!("data"));
213
214 let json_str = serde_json::to_string(&envelope).expect("serialize");
215 assert!(!json_str.contains("timestamp"), "None timestamp should be skipped");
216 assert!(!json_str.contains("from"), "None from should be skipped");
217 }
218
219 #[test]
220 fn test_envelope_deserialization_error() {
221 let bad_json = b"not valid json";
222 let result = Envelope::deserialize(bad_json);
223 assert!(result.is_err());
224 }
225}