Skip to main content

flare_core/common/protocol/
builder.rs

1//! 快速构建命令和 Frame 消息的辅助模块
2//! 提供便捷方法创建各种类型的命令,自动生成消息 ID 和时间戳
3
4use 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
20/// 生成唯一的消息 ID(基于时间戳和递增计数器)
21pub 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
27/// 获取当前时间戳(毫秒)
28pub fn current_timestamp() -> u64 {
29    platform::wall_clock_ms()
30}
31
32// ============================================================================
33// SystemCommand 构建辅助函数
34// ============================================================================
35
36/// 创建基础 SystemCommand(内部辅助函数)
37fn 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
49/// 创建带消息的 SystemCommand(内部辅助函数)
50fn 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
67/// 创建带数据的 SystemCommand(内部辅助函数)
68fn 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
86// ============================================================================
87// Frame 构建器
88// ============================================================================
89
90/// Frame 构建器
91pub 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    /// 创建新的 Frame 构建器
101    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    /// 设置命令
112    #[must_use]
113    pub fn with_command(mut self, command: Command) -> Self {
114        self.command = Some(command);
115        self
116    }
117
118    /// 设置消息 ID(不设置则自动生成)
119    #[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    /// 设置可靠性等级
126    #[must_use]
127    pub fn with_reliability(mut self, reliability: Reliability) -> Self {
128        self.reliability = reliability;
129        self
130    }
131
132    /// 设置时间戳(不设置则使用当前时间)
133    #[must_use]
134    pub fn with_timestamp(mut self, timestamp: u64) -> Self {
135        self.timestamp = Some(timestamp);
136        self
137    }
138
139    /// 添加元数据
140    #[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    /// 添加字符串元数据
147    #[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    /// 构建 Frame
154    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
171// ============================================================================
172// 系统命令构建方法
173// ============================================================================
174
175/// 创建 PING 命令
176pub fn ping() -> SystemCommand {
177    create_base_system_command(SystemType::Ping, SerializationFormat::Protobuf)
178}
179
180/// 创建 PONG 命令
181pub fn pong() -> SystemCommand {
182    create_base_system_command(SystemType::Pong, SerializationFormat::Protobuf)
183}
184
185/// 创建 CONNECT 命令
186pub 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
198/// 创建 CONNECT_ACK 命令
199pub 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
216/// 创建 CLOSE 命令
217pub 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
226/// 创建 ERROR 命令
227pub 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
236/// 创建 EVENT 命令
237pub 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
251/// 创建 AUTH 命令
252pub 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
264/// 创建 AUTH_ACK 命令
265pub 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
277/// 创建 KICKED 命令(被踢下线)
278///
279/// # 参数
280/// - `reason`: 被踢的原因(必需)
281/// - `metadata`: 可选的元数据(如设备信息、冲突连接ID等)
282///
283/// # 示例
284/// ```rust
285/// use flare_core::common::protocol::builder::kicked;
286/// use flare_core::common::protocol::frame_with_system_command;
287/// use std::collections::HashMap;
288///
289/// let mut metadata = HashMap::new();
290/// metadata.insert("conflict_device".to_string(), "device-123".as_bytes().to_vec());
291///
292/// let kick_cmd = kicked("设备冲突:同一平台已有其他设备在线", Some(metadata));
293/// let frame = frame_with_system_command(kick_cmd, Reliability::AtLeastOnce);
294/// ```
295pub 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
307// ============================================================================
308// 载荷命令构建方法
309// ============================================================================
310
311/// 创建载荷命令(内部辅助函数)
312fn 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
328/// 创建 MESSAGE 载荷命令(业务消息,需 ACK)
329pub 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
338/// 创建 EVENT 载荷命令(事件)
339pub 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
348/// 创建 ACK 载荷命令(确认)
349pub 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
362/// 创建 DATA 载荷命令(无需 ACK)
363pub 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
372// ============================================================================
373// 通知命令构建方法
374// ============================================================================
375
376/// 创建通知命令
377pub 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
391// ============================================================================
392// 自定义命令构建方法
393// ============================================================================
394
395/// 创建自定义命令
396pub 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
408// ============================================================================
409// Frame 快速构建方法
410// ============================================================================
411
412/// 创建包含命令的 Frame(内部辅助函数)
413fn 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
422/// 创建包含系统命令的 Frame
423pub fn frame_with_system_command(system_command: SystemCommand, reliability: Reliability) -> Frame {
424    create_frame_with_command(CommandType::System(system_command), reliability)
425}
426
427/// 创建包含载荷命令的 Frame(载荷类型:MESSAGE/EVENT/ACK/DATA)
428///
429/// Frame 的 message_id 使用 PayloadCommand.message_id;若为空则自动生成并回写。
430pub 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
447/// Alias for [`frame_with_payload_command`] (backward compatibility for examples).
448pub fn frame_with_message_command(
449    payload_command: PayloadCommand,
450    reliability: Reliability,
451) -> Frame {
452    frame_with_payload_command(payload_command, reliability)
453}
454
455/// 创建包含通知命令的 Frame
456pub 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
463/// 创建包含自定义命令的 Frame
464pub 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}