Skip to main content

atm_storage/schema/
inbox_message.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::{Map, Value};
4use std::fmt;
5use ulid::Ulid;
6use uuid::Uuid;
7
8use crate::types::{AgentName, IsoTimestamp, TaskId, TeamName};
9
10#[derive(Debug, Clone)]
11pub struct AtmMessageIdParseError(String);
12
13impl fmt::Display for AtmMessageIdParseError {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        f.write_str(&self.0)
16    }
17}
18
19impl std::error::Error for AtmMessageIdParseError {}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
22#[serde(transparent)]
23pub struct AtmMessageId(Ulid);
24
25impl AtmMessageId {
26    pub fn new() -> Self {
27        Self(Ulid::new())
28    }
29
30    pub fn from_uuid_wire(value: Uuid) -> Self {
31        Self(Ulid::from_bytes(value.into_bytes()))
32    }
33
34    pub fn into_uuid_wire(self) -> Uuid {
35        Uuid::from_bytes(self.0.to_bytes())
36    }
37
38    pub fn into_ulid(self) -> Ulid {
39        self.0
40    }
41
42    pub fn timestamp(self) -> IsoTimestamp {
43        let datetime: DateTime<Utc> = self.0.datetime().into();
44        IsoTimestamp::from_datetime(datetime)
45    }
46
47    pub fn new_with_timestamp() -> (Self, IsoTimestamp) {
48        let message_id = Self::new();
49        let timestamp = message_id.timestamp();
50        (message_id, timestamp)
51    }
52}
53
54impl Default for AtmMessageId {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60impl From<Ulid> for AtmMessageId {
61    fn from(value: Ulid) -> Self {
62        Self(value)
63    }
64}
65
66impl From<Uuid> for AtmMessageId {
67    fn from(value: Uuid) -> Self {
68        Self::from_uuid_wire(value)
69    }
70}
71
72impl From<AtmMessageId> for Ulid {
73    fn from(value: AtmMessageId) -> Self {
74        value.0
75    }
76}
77
78impl From<AtmMessageId> for Uuid {
79    fn from(value: AtmMessageId) -> Self {
80        value.into_uuid_wire()
81    }
82}
83
84impl std::str::FromStr for AtmMessageId {
85    type Err = AtmMessageIdParseError;
86
87    fn from_str(s: &str) -> Result<Self, Self::Err> {
88        Ulid::from_string(s)
89            .map(Self)
90            .or_else(|_| Uuid::parse_str(s).map(Self::from_uuid_wire))
91            .map_err(|error| AtmMessageIdParseError(error.to_string()))
92    }
93}
94
95impl fmt::Display for AtmMessageId {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        self.0.fmt(f)
98    }
99}
100
101mod atm_message_id_uuid_wire {
102    use super::AtmMessageId;
103    use serde::{Deserialize, Deserializer, Serializer};
104    use uuid::Uuid;
105
106    pub fn serialize<S>(value: &AtmMessageId, serializer: S) -> Result<S::Ok, S::Error>
107    where
108        S: Serializer,
109    {
110        serializer.serialize_str(&value.into_uuid_wire().to_string())
111    }
112
113    pub fn deserialize<'de, D>(deserializer: D) -> Result<AtmMessageId, D::Error>
114    where
115        D: Deserializer<'de>,
116    {
117        let raw = String::deserialize(deserializer)?;
118        Uuid::parse_str(&raw)
119            .map(AtmMessageId::from_uuid_wire)
120            .map_err(serde::de::Error::custom)
121    }
122}
123
124mod option_atm_message_id_uuid_wire {
125    use super::AtmMessageId;
126    use serde::{Deserialize, Deserializer, Serializer};
127    use uuid::Uuid;
128
129    pub fn serialize<S>(value: &Option<AtmMessageId>, serializer: S) -> Result<S::Ok, S::Error>
130    where
131        S: Serializer,
132    {
133        match value {
134            Some(value) => serializer.serialize_some(&value.into_uuid_wire().to_string()),
135            None => serializer.serialize_none(),
136        }
137    }
138
139    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<AtmMessageId>, D::Error>
140    where
141        D: Deserializer<'de>,
142    {
143        Option::<String>::deserialize(deserializer)?
144            .map(|raw| {
145                let uuid = Uuid::parse_str(&raw).map_err(serde::de::Error::custom)?;
146                Ok(AtmMessageId::from_uuid_wire(uuid))
147            })
148            .transpose()
149    }
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
153#[serde(rename_all = "kebab-case")]
154pub enum ThreadMode {
155    AddDetails,
156    Supersede,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
160pub enum AlertKind {
161    MissingTeamConfig,
162    Unknown(String),
163}
164
165impl AlertKind {
166    pub fn as_str(&self) -> &str {
167        match self {
168            Self::MissingTeamConfig => "missing_team_config",
169            Self::Unknown(value) => value,
170        }
171    }
172}
173
174impl From<String> for AlertKind {
175    fn from(value: String) -> Self {
176        match value.as_str() {
177            "missing_team_config" => Self::MissingTeamConfig,
178            _ => Self::Unknown(value),
179        }
180    }
181}
182
183impl From<AlertKind> for String {
184    fn from(value: AlertKind) -> Self {
185        match value {
186            AlertKind::MissingTeamConfig => "missing_team_config".to_string(),
187            AlertKind::Unknown(value) => value,
188        }
189    }
190}
191
192impl Serialize for AlertKind {
193    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
194    where
195        S: serde::Serializer,
196    {
197        serializer.serialize_str(self.as_str())
198    }
199}
200
201impl<'de> Deserialize<'de> for AlertKind {
202    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
203    where
204        D: serde::Deserializer<'de>,
205    {
206        Ok(Self::from(String::deserialize(deserializer)?))
207    }
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
211pub struct MessageEnvelope {
212    pub from: AgentName,
213    pub text: String,
214    pub timestamp: IsoTimestamp,
215    pub read: bool,
216    #[serde(skip_serializing_if = "Option::is_none")]
217    pub source_team: Option<TeamName>,
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub summary: Option<String>,
220    #[serde(default, skip_serializing_if = "Option::is_none")]
221    #[serde(with = "option_atm_message_id_uuid_wire")]
222    pub message_id: Option<AtmMessageId>,
223    #[serde(rename = "pendingAckAt", skip_serializing_if = "Option::is_none")]
224    pub pending_ack_at: Option<IsoTimestamp>,
225    #[serde(rename = "acknowledgedAt", skip_serializing_if = "Option::is_none")]
226    pub acknowledged_at: Option<IsoTimestamp>,
227    #[serde(
228        rename = "acknowledgesMessageId",
229        default,
230        skip_serializing_if = "Option::is_none"
231    )]
232    #[serde(with = "option_atm_message_id_uuid_wire")]
233    pub acknowledges_message_id: Option<AtmMessageId>,
234    #[serde(
235        rename = "parentMessageId",
236        default,
237        skip_serializing_if = "Option::is_none"
238    )]
239    #[serde(with = "option_atm_message_id_uuid_wire")]
240    pub parent_message_id: Option<AtmMessageId>,
241    #[serde(rename = "threadMode", skip_serializing_if = "Option::is_none")]
242    pub thread_mode: Option<ThreadMode>,
243    #[serde(rename = "expiresAt", skip_serializing_if = "Option::is_none")]
244    pub expires_at: Option<IsoTimestamp>,
245    #[serde(rename = "taskId", skip_serializing_if = "Option::is_none")]
246    pub task_id: Option<TaskId>,
247    #[serde(flatten)]
248    pub extra: Map<String, Value>,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
252pub struct PendingAck {
253    #[serde(with = "atm_message_id_uuid_wire")]
254    pub message_id: AtmMessageId,
255    pub from: AgentName,
256    pub acked: bool,
257    pub acked_at: Option<IsoTimestamp>,
258}