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 flare_core::common::Reliability;
288/// use std::collections::HashMap;
289///
290/// let mut metadata = HashMap::new();
291/// metadata.insert("conflict_device".to_string(), "device-123".as_bytes().to_vec());
292///
293/// let kick_cmd = kicked("设备冲突:同一平台已有其他设备在线", Some(metadata));
294/// let frame = frame_with_system_command(kick_cmd, Reliability::AtLeastOnce);
295/// ```
296pub 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
308// ============================================================================
309// 载荷命令构建方法
310// ============================================================================
311
312/// 创建载荷命令(内部辅助函数)
313fn 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
329/// 创建 MESSAGE 载荷命令(业务消息,需 ACK)
330pub 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
339/// 创建 EVENT 载荷命令(事件)
340pub 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
349/// 创建 ACK 载荷命令(确认)
350pub 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
363/// 创建 DATA 载荷命令(无需 ACK)
364pub 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
373// ============================================================================
374// 通知命令构建方法
375// ============================================================================
376
377/// 创建通知命令
378pub 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
392// ============================================================================
393// 自定义命令构建方法
394// ============================================================================
395
396/// 创建自定义命令
397pub 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
409// ============================================================================
410// Frame 快速构建方法
411// ============================================================================
412
413/// 创建包含命令的 Frame(内部辅助函数)
414fn 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
423/// 创建包含系统命令的 Frame
424pub fn frame_with_system_command(system_command: SystemCommand, reliability: Reliability) -> Frame {
425    create_frame_with_command(CommandType::System(system_command), reliability)
426}
427
428/// 创建包含载荷命令的 Frame(载荷类型:MESSAGE/EVENT/ACK/DATA)
429///
430/// Frame 的 message_id 使用 PayloadCommand.message_id;若为空则自动生成并回写。
431pub 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
448/// Alias for [`frame_with_payload_command`] (backward compatibility for examples).
449pub fn frame_with_message_command(
450    payload_command: PayloadCommand,
451    reliability: Reliability,
452) -> Frame {
453    frame_with_payload_command(payload_command, reliability)
454}
455
456/// 创建包含通知命令的 Frame
457pub 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
464/// 创建包含自定义命令的 Frame
465pub 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}