use serde::{Deserialize, Serialize};
use crate::file::Attachment;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Author {
pub user_id: String,
pub user_name: String,
pub full_name: String,
pub is_bot: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum PostableMessage {
Text(String),
Markdown(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum AdapterPostableMessage {
Text(String),
Markdown(String),
}
impl From<String> for PostableMessage {
fn from(text: String) -> Self {
Self::Text(text)
}
}
impl From<&str> for PostableMessage {
fn from(text: &str) -> Self {
Self::Text(text.to_owned())
}
}
pub struct SentMessage {
pub id: String,
pub thread_id: String,
pub adapter_name: String,
pub raw: Option<Box<dyn std::any::Any + Send + Sync>>,
}
impl std::fmt::Debug for SentMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SentMessage")
.field("id", &self.id)
.field("thread_id", &self.thread_id)
.field("adapter_name", &self.adapter_name)
.field("raw", &self.raw.as_ref().map(|_| "..."))
.finish()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EphemeralMessage {
pub id: String,
pub thread_id: String,
pub used_fallback: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IncomingMessage {
pub id: String,
pub text: String,
pub author: Author,
pub attachments: Vec<Attachment>,
pub is_mention: bool,
pub thread_id: String,
pub timestamp: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn author_serde_roundtrip() {
let author = Author {
user_id: "U123".into(),
user_name: "alice".into(),
full_name: "Alice Smith".into(),
is_bot: false,
};
let json = serde_json::to_string(&author).expect("serialize");
let back: Author = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.user_id, "U123");
assert!(!back.is_bot);
}
#[test]
fn postable_message_serde_roundtrip() {
let msg = PostableMessage::Text("hello".into());
let json = serde_json::to_string(&msg).expect("serialize");
let back: PostableMessage = serde_json::from_str(&json).expect("deserialize");
assert!(matches!(back, PostableMessage::Text(t) if t == "hello"));
}
#[test]
fn sent_message_debug() {
let sm = SentMessage {
id: "msg-1".into(),
thread_id: "t-1".into(),
adapter_name: "slack".into(),
raw: None,
};
let dbg = format!("{sm:?}");
assert!(dbg.contains("msg-1"));
}
#[test]
fn ephemeral_message_serde() {
let em =
EphemeralMessage { id: "e-1".into(), thread_id: "t-1".into(), used_fallback: true };
let json = serde_json::to_string(&em).expect("serialize");
assert!(json.contains("used_fallback"));
}
#[test]
fn incoming_message_debug() {
let msg = IncomingMessage {
id: "m-1".into(),
text: "hi bot".into(),
author: Author {
user_id: "U1".into(),
user_name: "bob".into(),
full_name: "Bob".into(),
is_bot: false,
},
attachments: vec![],
is_mention: true,
thread_id: "t-1".into(),
timestamp: Some("2026-03-03T12:00:00Z".into()),
};
let dbg = format!("{msg:?}");
assert!(dbg.contains("hi bot"));
}
}