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(
297 reason: impl Into<String>,
298 metadata: Option<HashMap<String, Vec<u8>>>,
299) -> SystemCommand {
300 create_system_command_with_message(
301 SystemType::Kicked,
302 SerializationFormat::Protobuf,
303 reason,
304 metadata,
305 )
306}
307
308fn create_payload_command(
314 r#type: PayloadType,
315 message_id: String,
316 payload: Vec<u8>,
317 metadata: Option<HashMap<String, Vec<u8>>>,
318 seq: Option<u64>,
319) -> PayloadCommand {
320 PayloadCommand {
321 r#type: r#type as i32,
322 message_id,
323 payload,
324 metadata: metadata.unwrap_or_default(),
325 seq: seq.unwrap_or(0),
326 }
327}
328
329pub fn send_message(
331 message_id: String,
332 payload: Vec<u8>,
333 metadata: Option<HashMap<String, Vec<u8>>>,
334 seq: Option<u64>,
335) -> PayloadCommand {
336 create_payload_command(PayloadType::Message, message_id, payload, metadata, seq)
337}
338
339pub fn event_message(
341 message_id: String,
342 payload: Vec<u8>,
343 metadata: Option<HashMap<String, Vec<u8>>>,
344 seq: Option<u64>,
345) -> PayloadCommand {
346 create_payload_command(PayloadType::Event, message_id, payload, metadata, seq)
347}
348
349pub fn ack_message(
351 message_id: String,
352 metadata: Option<HashMap<String, Vec<u8>>>,
353) -> PayloadCommand {
354 PayloadCommand {
355 r#type: PayloadType::Ack as i32,
356 message_id,
357 payload: Vec::new(),
358 metadata: metadata.unwrap_or_default(),
359 seq: 0,
360 }
361}
362
363pub fn data_message(
365 message_id: String,
366 payload: Vec<u8>,
367 metadata: Option<HashMap<String, Vec<u8>>>,
368 seq: Option<u64>,
369) -> PayloadCommand {
370 create_payload_command(PayloadType::Data, message_id, payload, metadata, seq)
371}
372
373pub fn notification(
379 notification_type: NotificationType,
380 title: String,
381 content: Vec<u8>,
382 metadata: Option<HashMap<String, Vec<u8>>>,
383) -> NotificationCommand {
384 NotificationCommand {
385 r#type: notification_type as i32,
386 title,
387 content,
388 metadata: metadata.unwrap_or_default(),
389 }
390}
391
392pub fn custom_command(
398 name: String,
399 data: Vec<u8>,
400 metadata: Option<HashMap<String, Vec<u8>>>,
401) -> CustomCommand {
402 CustomCommand {
403 name,
404 data,
405 metadata: metadata.unwrap_or_default(),
406 }
407}
408
409fn create_frame_with_command(command_type: CommandType, reliability: Reliability) -> Frame {
415 FrameBuilder::new()
416 .with_command(Command {
417 r#type: Some(command_type),
418 })
419 .with_reliability(reliability)
420 .build()
421}
422
423pub fn frame_with_system_command(system_command: SystemCommand, reliability: Reliability) -> Frame {
425 create_frame_with_command(CommandType::System(system_command), reliability)
426}
427
428pub fn frame_with_payload_command(
432 mut payload_command: PayloadCommand,
433 reliability: Reliability,
434) -> Frame {
435 if payload_command.message_id.is_empty() {
436 payload_command.message_id = generate_message_id();
437 }
438 let message_id = payload_command.message_id.clone();
439 FrameBuilder::new()
440 .with_command(Command {
441 r#type: Some(CommandType::Payload(payload_command)),
442 })
443 .with_message_id(message_id)
444 .with_reliability(reliability)
445 .build()
446}
447
448pub fn frame_with_message_command(
450 payload_command: PayloadCommand,
451 reliability: Reliability,
452) -> Frame {
453 frame_with_payload_command(payload_command, reliability)
454}
455
456pub fn frame_with_notification_command(
458 notification_command: NotificationCommand,
459 reliability: Reliability,
460) -> Frame {
461 create_frame_with_command(CommandType::Notification(notification_command), reliability)
462}
463
464pub fn frame_with_custom_command(custom_command: CustomCommand, reliability: Reliability) -> Frame {
466 create_frame_with_command(CommandType::Custom(custom_command), reliability)
467}
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472
473 #[test]
474 fn test_ping_pong() {
475 let ping_cmd = ping();
476 assert_eq!(ping_cmd.r#type, SystemType::Ping as i32);
477
478 let pong_cmd = pong();
479 assert_eq!(pong_cmd.r#type, SystemType::Pong as i32);
480 }
481
482 #[test]
483 fn test_generate_message_id() {
484 let id1 = generate_message_id();
485 let id2 = generate_message_id();
486 assert_ne!(id1, id2);
487 }
488
489 #[test]
490 fn test_frame_builder() {
491 let frame = FrameBuilder::new()
492 .with_command(Command {
493 r#type: Some(CommandType::System(ping())),
494 })
495 .with_reliability(Reliability::AtLeastOnce)
496 .build();
497
498 assert!(!frame.message_id.is_empty());
499 assert!(frame.timestamp > 0);
500 }
501}