Skip to main content

dingtalk_stream/frames/down_message/
mod.rs

1use serde::{Deserialize, Deserializer, Serialize};
2use std::collections::HashMap;
3
4pub mod callback_message;
5pub mod event_message;
6pub mod system_message;
7/// Base message structure for downstream messages
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct DownStreamMessage {
10    #[serde(rename = "specVersion")]
11    pub spec_version: Option<String>,
12    pub headers: MessageHeaders,
13    #[serde(rename = "type")]
14    pub r#type: MessageType,
15    pub data: Option<String>,
16    #[serde(flatten)]
17    pub extensions: HashMap<String, serde_json::Value>,
18}
19
20#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
21pub enum MessageType {
22    #[serde(rename = "SYSTEM")]
23    System,
24    #[serde(rename = "EVENT")]
25    Event,
26    #[serde(rename = "CALLBACK")]
27    Callback,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub enum Data {
32    System(callback_message::MessageData),
33    Event(event_message::EventMessage),
34    Callback(callback_message::CallbackMessage),
35}
36
37/// Headers for all message types
38#[derive(Debug, Clone, Serialize, Deserialize, Default)]
39pub struct MessageHeaders {
40    #[serde(rename = "appId", skip_serializing_if = "Option::is_none")]
41    pub app_id: Option<String>,
42    #[serde(rename = "connectionId", skip_serializing_if = "Option::is_none")]
43    pub connection_id: Option<String>,
44    #[serde(rename = "contentType", skip_serializing_if = "Option::is_none")]
45    pub content_type: Option<String>,
46    #[serde(rename = "messageId", skip_serializing_if = "Option::is_none")]
47    pub message_id: Option<String>,
48    #[serde(rename = "time", skip_serializing_if = "Option::is_none")]
49    pub time: Option<String>,
50    #[serde(rename = "topic", skip_serializing_if = "Option::is_none")]
51    pub topic: Option<MessageTopic>,
52    // Event fields
53    #[serde(rename = "eventBornTime", skip_serializing_if = "Option::is_none")]
54    pub event_born_time: Option<i64>,
55    #[serde(rename = "eventCorpId", skip_serializing_if = "Option::is_none")]
56    pub event_corp_id: Option<String>,
57    #[serde(rename = "eventId", skip_serializing_if = "Option::is_none")]
58    pub event_id: Option<String>,
59    #[serde(rename = "eventType", skip_serializing_if = "Option::is_none")]
60    pub event_type: Option<String>,
61    #[serde(rename = "eventUnifiedAppId", skip_serializing_if = "Option::is_none")]
62    pub event_unified_app_id: Option<String>,
63    /// Additional extension fields
64    #[serde(flatten)]
65    pub extensions: HashMap<String, serde_json::Value>,
66}
67
68impl MessageHeaders {
69    pub fn new() -> Self {
70        Self::default()
71    }
72
73    pub fn with_message_id(mut self, message_id: String) -> Self {
74        self.message_id = Some(message_id);
75        self
76    }
77
78    pub fn with_content_type(mut self, content_type: &str) -> Self {
79        self.content_type = Some(content_type.to_string());
80        self
81    }
82}
83
84/// System message topic enum
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Hash)]
86#[serde(untagged)]
87pub enum MessageTopic {
88    #[serde(rename = "CONNECTED")]
89    Connected,
90    #[serde(rename = "REGISTERED")]
91    Registered,
92    #[serde(rename = "disconnect")]
93    Disconnect,
94    #[serde(rename = "KEEPALIVE")]
95    KeepAlive,
96    #[serde(rename = "ping")]
97    Ping,
98    Callback(String),
99}
100
101impl From<String> for MessageTopic {
102    fn from(s: String) -> Self {
103        let uppercase = s.to_uppercase();
104        match uppercase.as_str() {
105            "CONNECTED" => MessageTopic::Connected,
106            "REGISTERED" => MessageTopic::Registered,
107            "DISCONNECT" => MessageTopic::Disconnect,
108            "KEEPALIVE" => MessageTopic::KeepAlive,
109            "PING" => MessageTopic::Ping,
110            _ => MessageTopic::Callback(s),
111        }
112    }
113}
114
115impl<'de> Deserialize<'de> for MessageTopic {
116    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117    where
118        D: Deserializer<'de>,
119    {
120        let str = String::deserialize(deserializer)?;
121        Ok(Self::from(str))
122    }
123}
124
125impl std::fmt::Display for MessageTopic {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        let str = match self {
128            MessageTopic::Connected => "CONNECTED",
129            MessageTopic::Registered => "REGISTERED",
130            MessageTopic::Disconnect => "disconnect",
131            MessageTopic::KeepAlive => "KEEPALIVE",
132            MessageTopic::Ping => "ping",
133            MessageTopic::Callback(s) => s,
134        };
135        write!(f, "{str}")
136    }
137}