use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use crate::{ChannelError, ChannelId, ConversationId};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Participant {
pub id: String,
pub display_name: Option<String>,
pub handle: Option<String>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct MessageBody {
pub text: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub attachments: Vec<Attachment>,
}
impl MessageBody {
pub fn text(value: impl Into<String>) -> Result<Self, ChannelError> {
let body = Self {
text: value.into(),
attachments: Vec::new(),
};
body.validate()?;
Ok(body)
}
pub fn validate(&self) -> Result<(), ChannelError> {
if self.text.is_empty() && self.attachments.is_empty() {
return Err(ChannelError::InvalidMessage(
"message body is empty".to_owned(),
));
}
if self.text.contains('\0') {
return Err(ChannelError::InvalidMessage(
"message text contains NUL".to_owned(),
));
}
Ok(())
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Attachment {
pub url: String,
pub name: Option<String>,
pub mime: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct MessageTarget {
pub channel: ChannelId,
pub conversation: ConversationId,
pub thread: Option<String>,
pub reply_to: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct InboundMessage {
pub id: String,
pub target: MessageTarget,
pub sender: Participant,
pub body: MessageBody,
pub timestamp_ms: Option<u64>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub metadata: BTreeMap<String, String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct OutboundMessage {
pub target: MessageTarget,
pub body: MessageBody,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub metadata: BTreeMap<String, String>,
}