Skip to main content

remendo/models/
message.rs

1//! Message domain types from the Sashiko API.
2//!
3//! Named `EmailMessage` (not `Message`) to avoid collision with the TEA
4//! `Message` enum in `src/update.rs`.
5
6use serde::Deserialize;
7
8/// A message record from the Sashiko API (`/api/messages`, `/api/message`).
9#[derive(Debug, Clone, PartialEq, Deserialize)]
10pub struct EmailMessage {
11    /// Database identifier.
12    #[serde(default)]
13    pub id: i64,
14    /// RFC 2822 `Message-ID`.
15    #[serde(default)]
16    pub message_id: String,
17    /// Thread identifier.
18    #[serde(default)]
19    pub thread_id: Option<i64>,
20    /// `Message-ID` this message replies to.
21    #[serde(default)]
22    pub in_reply_to: Option<String>,
23    /// Author email or name.
24    #[serde(default)]
25    pub author: Option<String>,
26    /// Subject line.
27    #[serde(default)]
28    pub subject: Option<String>,
29    /// Unix timestamp.
30    #[serde(default)]
31    pub date: Option<i64>,
32    /// Email body text.
33    #[serde(default)]
34    pub body: Option<String>,
35    /// `To` header recipients.
36    #[serde(default)]
37    pub to: Option<String>,
38    /// `Cc` header recipients.
39    #[serde(default)]
40    pub cc: Option<String>,
41    /// NNTP group / mailing list identifier.
42    #[serde(default)]
43    pub mailing_list: Option<String>,
44    /// Patch diff content, if applicable.
45    #[serde(default)]
46    pub diff: Option<String>,
47}
48
49/// A message entry from the patchset detail thread section.
50#[derive(Debug, Clone, PartialEq, Deserialize)]
51pub struct ThreadMessage {
52    /// Database identifier.
53    #[serde(default)]
54    pub id: i64,
55    /// RFC 2822 `Message-ID`.
56    #[serde(default)]
57    pub message_id: Option<String>,
58    /// Author email or name.
59    #[serde(default)]
60    pub author: Option<String>,
61    /// Unix timestamp.
62    #[serde(default)]
63    pub date: Option<i64>,
64    /// Subject line.
65    #[serde(default)]
66    pub subject: Option<String>,
67    /// `Message-ID` this message replies to.
68    #[serde(default)]
69    pub in_reply_to: Option<String>,
70}
71
72#[cfg(test)]
73#[allow(clippy::expect_used)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn deserialize_email_message() {
79        let json = r#"{
80            "id": 141626,
81            "message_id": "20260513-test@example.com",
82            "thread_id": 25647,
83            "author": "dev@example.com",
84            "subject": "[PATCH v2 1/4] fix null deref",
85            "date": 1778690984,
86            "mailing_list": "org.kernel.vger.linux-devicetree"
87        }"#;
88        let msg: EmailMessage = serde_json::from_str(json).expect("deserialize email");
89        assert_eq!(msg.id, 141_626);
90        assert_eq!(msg.message_id, "20260513-test@example.com");
91        assert_eq!(msg.thread_id, Some(25647));
92    }
93
94    #[test]
95    fn deserialize_thread_message() {
96        let json = r#"{
97            "id": 1,
98            "message_id": "msg@example.com",
99            "author": "test@example.com",
100            "date": 1778575801,
101            "subject": "Re: [PATCH]",
102            "in_reply_to": "parent@example.com"
103        }"#;
104        let tm: ThreadMessage = serde_json::from_str(json).expect("deserialize thread msg");
105        assert_eq!(tm.id, 1);
106        assert_eq!(tm.in_reply_to.as_deref(), Some("parent@example.com"));
107    }
108}