1use serde::{Deserialize, Serialize};
4
5use crate::file::Attachment;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Author {
14 pub user_id: String,
16 pub user_name: String,
18 pub full_name: String,
20 pub is_bot: bool,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
33#[serde(tag = "type", content = "value")]
34pub enum PostableMessage {
35 Text(String),
37 Markdown(String),
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
47#[serde(tag = "type", content = "value")]
48pub enum AdapterPostableMessage {
49 Text(String),
51 Markdown(String),
53}
54
55impl From<String> for PostableMessage {
56 fn from(text: String) -> Self {
57 Self::Text(text)
58 }
59}
60
61impl From<&str> for PostableMessage {
62 fn from(text: &str) -> Self {
63 Self::Text(text.to_owned())
64 }
65}
66
67pub struct SentMessage {
76 pub id: String,
78 pub thread_id: String,
80 pub adapter_name: String,
82 pub raw: Option<Box<dyn std::any::Any + Send + Sync>>,
84}
85
86impl std::fmt::Debug for SentMessage {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 f.debug_struct("SentMessage")
89 .field("id", &self.id)
90 .field("thread_id", &self.thread_id)
91 .field("adapter_name", &self.adapter_name)
92 .field("raw", &self.raw.as_ref().map(|_| "..."))
93 .finish()
94 }
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct EphemeralMessage {
100 pub id: String,
102 pub thread_id: String,
104 pub used_fallback: bool,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct IncomingMessage {
112 pub id: String,
114 pub text: String,
116 pub author: Author,
118 pub attachments: Vec<Attachment>,
120 pub is_mention: bool,
122 pub thread_id: String,
124 pub timestamp: Option<String>,
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn author_serde_roundtrip() {
134 let author = Author {
135 user_id: "U123".into(),
136 user_name: "alice".into(),
137 full_name: "Alice Smith".into(),
138 is_bot: false,
139 };
140 let json = serde_json::to_string(&author).expect("serialize");
141 let back: Author = serde_json::from_str(&json).expect("deserialize");
142 assert_eq!(back.user_id, "U123");
143 assert!(!back.is_bot);
144 }
145
146 #[test]
147 fn postable_message_serde_roundtrip() {
148 let msg = PostableMessage::Text("hello".into());
149 let json = serde_json::to_string(&msg).expect("serialize");
150 let back: PostableMessage = serde_json::from_str(&json).expect("deserialize");
151 assert!(matches!(back, PostableMessage::Text(t) if t == "hello"));
152 }
153
154 #[test]
155 fn sent_message_debug() {
156 let sm = SentMessage {
157 id: "msg-1".into(),
158 thread_id: "t-1".into(),
159 adapter_name: "slack".into(),
160 raw: None,
161 };
162 let dbg = format!("{sm:?}");
163 assert!(dbg.contains("msg-1"));
164 }
165
166 #[test]
167 fn ephemeral_message_serde() {
168 let em =
169 EphemeralMessage { id: "e-1".into(), thread_id: "t-1".into(), used_fallback: true };
170 let json = serde_json::to_string(&em).expect("serialize");
171 assert!(json.contains("used_fallback"));
172 }
173
174 #[test]
175 fn incoming_message_debug() {
176 let msg = IncomingMessage {
177 id: "m-1".into(),
178 text: "hi bot".into(),
179 author: Author {
180 user_id: "U1".into(),
181 user_name: "bob".into(),
182 full_name: "Bob".into(),
183 is_bot: false,
184 },
185 attachments: vec![],
186 is_mention: true,
187 thread_id: "t-1".into(),
188 timestamp: Some("2026-03-03T12:00:00Z".into()),
189 };
190 let dbg = format!("{msg:?}");
191 assert!(dbg.contains("hi bot"));
192 }
193}