1use super::flare::core::commands::{
5 command::Type as CommandType,
6 notification_command::Type as NotificationType,
7 payload_command::Type as PayloadType,
8 system_command::{SerializationFormat, Type as SystemType},
9};
10use super::flare::core::{
11 Frame, Reliability,
12 commands::{Command, CustomCommand, NotificationCommand, PayloadCommand, SystemCommand},
13};
14use crate::common::platform;
15use std::collections::HashMap;
16use std::sync::atomic::{AtomicU64, Ordering};
17
18static COUNTER: AtomicU64 = AtomicU64::new(0);
19
20pub fn generate_message_id() -> String {
22 let timestamp = platform::wall_clock_ms();
23 let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
24 format!("{}-{:016x}", timestamp, counter)
25}
26
27pub fn current_timestamp() -> u64 {
29 platform::wall_clock_ms()
30}
31
32fn create_base_system_command(r#type: SystemType, format: SerializationFormat) -> SystemCommand {
38 SystemCommand {
39 r#type: r#type as i32,
40 format: format as i32,
41 message: String::new(),
42 metadata: HashMap::new(),
43 data: Vec::new(),
44 compression: String::new(),
45 encryption: String::new(),
46 }
47}
48
49fn create_system_command_with_message(
51 r#type: SystemType,
52 format: SerializationFormat,
53 message: impl Into<String>,
54 metadata: Option<HashMap<String, Vec<u8>>>,
55) -> SystemCommand {
56 SystemCommand {
57 r#type: r#type as i32,
58 format: format as i32,
59 message: message.into(),
60 metadata: metadata.unwrap_or_default(),
61 data: Vec::new(),
62 compression: String::new(),
63 encryption: String::new(),
64 }
65}
66
67fn create_system_command_with_data(
69 r#type: SystemType,
70 format: SerializationFormat,
71 message: impl Into<String>,
72 metadata: Option<HashMap<String, Vec<u8>>>,
73 data: Option<Vec<u8>>,
74) -> SystemCommand {
75 SystemCommand {
76 r#type: r#type as i32,
77 format: format as i32,
78 message: message.into(),
79 metadata: metadata.unwrap_or_default(),
80 data: data.unwrap_or_default(),
81 compression: String::new(),
82 encryption: String::new(),
83 }
84}
85
86pub struct FrameBuilder {
92 command: Option<Command>,
93 message_id: Option<String>,
94 reliability: Reliability,
95 timestamp: Option<u64>,
96 metadata: HashMap<String, Vec<u8>>,
97}
98
99impl FrameBuilder {
100 pub fn new() -> Self {
102 Self {
103 command: None,
104 message_id: None,
105 reliability: Reliability::BestEffort,
106 timestamp: None,
107 metadata: HashMap::new(),
108 }
109 }
110
111 #[must_use]
113 pub fn with_command(mut self, command: Command) -> Self {
114 self.command = Some(command);
115 self
116 }
117
118 #[must_use]
120 pub fn with_message_id(mut self, message_id: String) -> Self {
121 self.message_id = Some(message_id);
122 self
123 }
124
125 #[must_use]
127 pub fn with_reliability(mut self, reliability: Reliability) -> Self {
128 self.reliability = reliability;
129 self
130 }
131
132 #[must_use]
134 pub fn with_timestamp(mut self, timestamp: u64) -> Self {
135 self.timestamp = Some(timestamp);
136 self
137 }
138
139 #[must_use]
141 pub fn with_metadata(mut self, key: String, value: Vec<u8>) -> Self {
142 self.metadata.insert(key, value);
143 self
144 }
145
146 #[must_use]
148 pub fn with_metadata_str(mut self, key: String, value: String) -> Self {
149 self.metadata.insert(key, value.into_bytes());
150 self
151 }
152
153 pub fn build(self) -> Frame {
155 Frame {
156 command: self.command,
157 message_id: self.message_id.unwrap_or_else(generate_message_id),
158 reliability: self.reliability as i32,
159 timestamp: self.timestamp.unwrap_or_else(current_timestamp),
160 metadata: self.metadata,
161 }
162 }
163}
164
165impl Default for FrameBuilder {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171pub fn ping() -> SystemCommand {
177 create_base_system_command(SystemType::Ping, SerializationFormat::Protobuf)
178}
179
180pub fn pong() -> SystemCommand {
182 create_base_system_command(SystemType::Pong, SerializationFormat::Protobuf)
183}
184
185pub fn connect(format: SerializationFormat, metadata: HashMap<String, Vec<u8>>) -> SystemCommand {
187 SystemCommand {
188 r#type: SystemType::Connect as i32,
189 format: format as i32,
190 message: String::new(),
191 metadata,
192 data: Vec::new(),
193 compression: String::new(),
194 encryption: String::new(),
195 }
196}
197
198pub fn connect_ack(
200 format: SerializationFormat,
201 compression: Option<&str>,
202 encryption: Option<&str>,
203 metadata: HashMap<String, Vec<u8>>,
204) -> SystemCommand {
205 SystemCommand {
206 r#type: SystemType::ConnectAck as i32,
207 format: format as i32,
208 message: String::new(),
209 metadata,
210 data: Vec::new(),
211 compression: compression.unwrap_or("none").to_string(),
212 encryption: encryption.unwrap_or("none").to_string(),
213 }
214}
215
216pub fn close(message: Option<String>, metadata: Option<HashMap<String, Vec<u8>>>) -> SystemCommand {
218 create_system_command_with_message(
219 SystemType::Close,
220 SerializationFormat::Protobuf,
221 message.unwrap_or_default(),
222 metadata,
223 )
224}
225
226pub fn error(message: String, metadata: Option<HashMap<String, Vec<u8>>>) -> SystemCommand {
228 create_system_command_with_message(
229 SystemType::Error,
230 SerializationFormat::Protobuf,
231 message,
232 metadata,
233 )
234}
235
236pub fn event(
238 message: String,
239 metadata: Option<HashMap<String, Vec<u8>>>,
240 data: Option<Vec<u8>>,
241) -> SystemCommand {
242 create_system_command_with_data(
243 SystemType::Event,
244 SerializationFormat::Protobuf,
245 message,
246 metadata,
247 data,
248 )
249}
250
251pub fn auth(metadata: HashMap<String, Vec<u8>>, data: Option<Vec<u8>>) -> SystemCommand {
253 SystemCommand {
254 r#type: SystemType::Auth as i32,
255 format: SerializationFormat::Protobuf as i32,
256 message: String::new(),
257 metadata,
258 data: data.unwrap_or_default(),
259 compression: String::new(),
260 encryption: String::new(),
261 }
262}
263
264pub fn auth_ack(
266 message: Option<String>,
267 metadata: Option<HashMap<String, Vec<u8>>>,
268) -> SystemCommand {
269 create_system_command_with_message(
270 SystemType::AuthAck,
271 SerializationFormat::Protobuf,
272 message.unwrap_or_default(),
273 metadata,
274 )
275}
276
277pub fn kicked(
296 reason: impl Into<String>,
297 metadata: Option<HashMap<String, Vec<u8>>>,
298) -> SystemCommand {
299 create_system_command_with_message(
300 SystemType::Kicked,
301 SerializationFormat::Protobuf,
302 reason,
303 metadata,
304 )
305}
306
307fn create_payload_command(
313 r#type: PayloadType,
314 message_id: String,
315 payload: Vec<u8>,
316 metadata: Option<HashMap<String, Vec<u8>>>,
317 seq: Option<u64>,
318) -> PayloadCommand {
319 PayloadCommand {
320 r#type: r#type as i32,
321 message_id,
322 payload,
323 metadata: metadata.unwrap_or_default(),
324 seq: seq.unwrap_or(0),
325 }
326}
327
328pub fn send_message(
330 message_id: String,
331 payload: Vec<u8>,
332 metadata: Option<HashMap<String, Vec<u8>>>,
333 seq: Option<u64>,
334) -> PayloadCommand {
335 create_payload_command(PayloadType::Message, message_id, payload, metadata, seq)
336}
337
338pub fn event_message(
340 message_id: String,
341 payload: Vec<u8>,
342 metadata: Option<HashMap<String, Vec<u8>>>,
343 seq: Option<u64>,
344) -> PayloadCommand {
345 create_payload_command(PayloadType::Event, message_id, payload, metadata, seq)
346}
347
348pub fn ack_message(
350 message_id: String,
351 metadata: Option<HashMap<String, Vec<u8>>>,
352) -> PayloadCommand {
353 PayloadCommand {
354 r#type: PayloadType::Ack as i32,
355 message_id,
356 payload: Vec::new(),
357 metadata: metadata.unwrap_or_default(),
358 seq: 0,
359 }
360}
361
362pub fn data_message(
364 message_id: String,
365 payload: Vec<u8>,
366 metadata: Option<HashMap<String, Vec<u8>>>,
367 seq: Option<u64>,
368) -> PayloadCommand {
369 create_payload_command(PayloadType::Data, message_id, payload, metadata, seq)
370}
371
372pub fn notification(
378 notification_type: NotificationType,
379 title: String,
380 content: Vec<u8>,
381 metadata: Option<HashMap<String, Vec<u8>>>,
382) -> NotificationCommand {
383 NotificationCommand {
384 r#type: notification_type as i32,
385 title,
386 content,
387 metadata: metadata.unwrap_or_default(),
388 }
389}
390
391pub fn custom_command(
397 name: String,
398 data: Vec<u8>,
399 metadata: Option<HashMap<String, Vec<u8>>>,
400) -> CustomCommand {
401 CustomCommand {
402 name,
403 data,
404 metadata: metadata.unwrap_or_default(),
405 }
406}
407
408fn create_frame_with_command(command_type: CommandType, reliability: Reliability) -> Frame {
414 FrameBuilder::new()
415 .with_command(Command {
416 r#type: Some(command_type),
417 })
418 .with_reliability(reliability)
419 .build()
420}
421
422pub fn frame_with_system_command(system_command: SystemCommand, reliability: Reliability) -> Frame {
424 create_frame_with_command(CommandType::System(system_command), reliability)
425}
426
427pub fn frame_with_payload_command(
431 mut payload_command: PayloadCommand,
432 reliability: Reliability,
433) -> Frame {
434 if payload_command.message_id.is_empty() {
435 payload_command.message_id = generate_message_id();
436 }
437 let message_id = payload_command.message_id.clone();
438 FrameBuilder::new()
439 .with_command(Command {
440 r#type: Some(CommandType::Payload(payload_command)),
441 })
442 .with_message_id(message_id)
443 .with_reliability(reliability)
444 .build()
445}
446
447pub fn frame_with_message_command(
449 payload_command: PayloadCommand,
450 reliability: Reliability,
451) -> Frame {
452 frame_with_payload_command(payload_command, reliability)
453}
454
455pub fn frame_with_notification_command(
457 notification_command: NotificationCommand,
458 reliability: Reliability,
459) -> Frame {
460 create_frame_with_command(CommandType::Notification(notification_command), reliability)
461}
462
463pub fn frame_with_custom_command(custom_command: CustomCommand, reliability: Reliability) -> Frame {
465 create_frame_with_command(CommandType::Custom(custom_command), reliability)
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471
472 #[test]
473 fn test_ping_pong() {
474 let ping_cmd = ping();
475 assert_eq!(ping_cmd.r#type, SystemType::Ping as i32);
476
477 let pong_cmd = pong();
478 assert_eq!(pong_cmd.r#type, SystemType::Pong as i32);
479 }
480
481 #[test]
482 fn test_generate_message_id() {
483 let id1 = generate_message_id();
484 let id2 = generate_message_id();
485 assert_ne!(id1, id2);
486 }
487
488 #[test]
489 fn test_frame_builder() {
490 let frame = FrameBuilder::new()
491 .with_command(Command {
492 r#type: Some(CommandType::System(ping())),
493 })
494 .with_reliability(Reliability::AtLeastOnce)
495 .build();
496
497 assert!(!frame.message_id.is_empty());
498 assert!(frame.timestamp > 0);
499 }
500}