1use mxr_core::id::*;
2use mxr_core::types::*;
3
4impl super::Store {
5 pub async fn insert_draft(&self, draft: &Draft) -> Result<(), sqlx::Error> {
6 let id = draft.id.as_str();
7 let account_id = draft.account_id.as_str();
8 let to_addrs = serde_json::to_string(&draft.to).unwrap();
9 let cc_addrs = serde_json::to_string(&draft.cc).unwrap();
10 let bcc_addrs = serde_json::to_string(&draft.bcc).unwrap();
11 let attachments = serde_json::to_string(&draft.attachments).unwrap();
12 let in_reply_to = draft
13 .reply_headers
14 .as_ref()
15 .map(|headers| serde_json::to_string(headers).unwrap());
16 let created_at = draft.created_at.timestamp();
17 let updated_at = draft.updated_at.timestamp();
18
19 sqlx::query!(
20 "INSERT INTO drafts (id, account_id, in_reply_to, to_addrs, cc_addrs, bcc_addrs, subject, body_markdown, attachments, created_at, updated_at)
21 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
22 id,
23 account_id,
24 in_reply_to,
25 to_addrs,
26 cc_addrs,
27 bcc_addrs,
28 draft.subject,
29 draft.body_markdown,
30 attachments,
31 created_at,
32 updated_at,
33 )
34 .execute(self.writer())
35 .await?;
36
37 Ok(())
38 }
39
40 pub async fn get_draft(&self, id: &DraftId) -> Result<Option<Draft>, sqlx::Error> {
41 let id_str = id.as_str();
42 let row = sqlx::query!(
43 r#"SELECT id as "id!", account_id as "account_id!", in_reply_to,
44 to_addrs as "to_addrs!", cc_addrs as "cc_addrs!", bcc_addrs as "bcc_addrs!",
45 subject as "subject!", body_markdown as "body_markdown!",
46 attachments as "attachments!", created_at as "created_at!", updated_at as "updated_at!"
47 FROM drafts WHERE id = ?"#,
48 id_str,
49 )
50 .fetch_optional(self.reader())
51 .await?;
52
53 Ok(row.map(|r| Draft {
54 id: DraftId::from_uuid(uuid::Uuid::parse_str(&r.id).unwrap()),
55 account_id: AccountId::from_uuid(uuid::Uuid::parse_str(&r.account_id).unwrap()),
56 reply_headers: parse_reply_headers(r.in_reply_to),
57 to: serde_json::from_str(&r.to_addrs).unwrap_or_default(),
58 cc: serde_json::from_str(&r.cc_addrs).unwrap_or_default(),
59 bcc: serde_json::from_str(&r.bcc_addrs).unwrap_or_default(),
60 subject: r.subject,
61 body_markdown: r.body_markdown,
62 attachments: serde_json::from_str(&r.attachments).unwrap_or_default(),
63 created_at: chrono::DateTime::from_timestamp(r.created_at, 0).unwrap_or_default(),
64 updated_at: chrono::DateTime::from_timestamp(r.updated_at, 0).unwrap_or_default(),
65 }))
66 }
67
68 pub async fn list_drafts(&self, account_id: &AccountId) -> Result<Vec<Draft>, sqlx::Error> {
69 let aid = account_id.as_str();
70 let rows = sqlx::query!(
71 r#"SELECT id as "id!", account_id as "account_id!", in_reply_to,
72 to_addrs as "to_addrs!", cc_addrs as "cc_addrs!", bcc_addrs as "bcc_addrs!",
73 subject as "subject!", body_markdown as "body_markdown!",
74 attachments as "attachments!", created_at as "created_at!", updated_at as "updated_at!"
75 FROM drafts WHERE account_id = ? ORDER BY updated_at DESC"#,
76 aid,
77 )
78 .fetch_all(self.reader())
79 .await?;
80
81 Ok(rows
82 .into_iter()
83 .map(|r| Draft {
84 id: DraftId::from_uuid(uuid::Uuid::parse_str(&r.id).unwrap()),
85 account_id: AccountId::from_uuid(uuid::Uuid::parse_str(&r.account_id).unwrap()),
86 reply_headers: parse_reply_headers(r.in_reply_to),
87 to: serde_json::from_str(&r.to_addrs).unwrap_or_default(),
88 cc: serde_json::from_str(&r.cc_addrs).unwrap_or_default(),
89 bcc: serde_json::from_str(&r.bcc_addrs).unwrap_or_default(),
90 subject: r.subject,
91 body_markdown: r.body_markdown,
92 attachments: serde_json::from_str(&r.attachments).unwrap_or_default(),
93 created_at: chrono::DateTime::from_timestamp(r.created_at, 0).unwrap_or_default(),
94 updated_at: chrono::DateTime::from_timestamp(r.updated_at, 0).unwrap_or_default(),
95 })
96 .collect())
97 }
98
99 pub async fn delete_draft(&self, id: &DraftId) -> Result<(), sqlx::Error> {
100 let id_str = id.as_str();
101 sqlx::query!("DELETE FROM drafts WHERE id = ?", id_str)
102 .execute(self.writer())
103 .await?;
104 Ok(())
105 }
106}
107
108fn parse_reply_headers(raw: Option<String>) -> Option<ReplyHeaders> {
109 let raw = raw?;
110 serde_json::from_str(&raw).ok().or_else(|| {
111 if raw.trim().is_empty() {
112 None
113 } else {
114 Some(ReplyHeaders {
115 in_reply_to: raw,
116 references: Vec::new(),
117 })
118 }
119 })
120}