| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
use chrono::Utc; |
| 5 |
use goingson_core::{ |
| 6 |
CoreError, DbValue, Email, EmailId, NewEmail, NewEmailWithTracking, Result, UserId, |
| 7 |
}; |
| 8 |
use rusqlite::{Connection, params}; |
| 9 |
|
| 10 |
use crate::utils::{execute, format_datetime, format_datetime_opt}; |
| 11 |
|
| 12 |
use super::query; |
| 13 |
|
| 14 |
|
| 15 |
pub(super) fn create(conn: &Connection, user_id: UserId, email: &NewEmail) -> Result<Email> { |
| 16 |
let id = EmailId::new(); |
| 17 |
let received_at = format_datetime(&email.received_at.unwrap_or_else(Utc::now)); |
| 18 |
execute( |
| 19 |
conn, |
| 20 |
"INSERT INTO emails (id, user_id, project_id, from_address, to_address, subject, body, is_read, received_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", |
| 21 |
params![ |
| 22 |
id.to_string(), |
| 23 |
user_id.to_string(), |
| 24 |
email.project_id.as_ref().map(ToString::to_string), |
| 25 |
&email.from_address, |
| 26 |
&email.to_address, |
| 27 |
&email.subject, |
| 28 |
&email.body, |
| 29 |
i32::from(email.is_read), |
| 30 |
&received_at |
| 31 |
], |
| 32 |
)?; |
| 33 |
query::get_by_id(conn, id, user_id)? |
| 34 |
.ok_or_else(|| CoreError::internal("Failed to retrieve created email")) |
| 35 |
} |
| 36 |
|
| 37 |
|
| 38 |
pub(super) fn restore(conn: &Connection, user_id: UserId, email: &Email) -> Result<()> { |
| 39 |
|
| 40 |
|
| 41 |
|
| 42 |
|
| 43 |
|
| 44 |
|
| 45 |
let labels_json = serde_json::to_string(&email.labels).unwrap_or_else(|_| "[]".to_string()); |
| 46 |
execute( |
| 47 |
conn, |
| 48 |
"INSERT OR IGNORE INTO emails (id, user_id, project_id, from_address, to_address, subject, body, body_format, html_body, is_read, is_archived, received_at, message_id, in_reply_to, thread_id, is_outgoing, labels, is_draft, cc_address, bcc_address, snoozed_until, waiting_for_response, waiting_since, expected_response_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", |
| 49 |
params![ |
| 50 |
email.id.to_string(), |
| 51 |
user_id.to_string(), |
| 52 |
email.project_id.map(|p| p.to_string()), |
| 53 |
&email.from, |
| 54 |
&email.to, |
| 55 |
&email.subject, |
| 56 |
&email.body, |
| 57 |
email.body_format.db_value(), |
| 58 |
&email.html_body, |
| 59 |
i32::from(email.is_read), |
| 60 |
i32::from(email.is_archived), |
| 61 |
format_datetime(&email.received_at), |
| 62 |
&email.message_id, |
| 63 |
&email.in_reply_to, |
| 64 |
&email.thread_id, |
| 65 |
i32::from(email.is_outgoing), |
| 66 |
&labels_json, |
| 67 |
i32::from(email.is_draft), |
| 68 |
&email.cc_address, |
| 69 |
&email.bcc_address, |
| 70 |
format_datetime_opt(email.snoozed_until), |
| 71 |
i32::from(email.waiting_for_response), |
| 72 |
format_datetime_opt(email.waiting_since), |
| 73 |
format_datetime_opt(email.expected_response_date) |
| 74 |
], |
| 75 |
)?; |
| 76 |
Ok(()) |
| 77 |
} |
| 78 |
|
| 79 |
|
| 80 |
pub(super) fn create_with_tracking( |
| 81 |
conn: &Connection, |
| 82 |
user_id: UserId, |
| 83 |
email: &NewEmailWithTracking, |
| 84 |
) -> Result<Email> { |
| 85 |
let id = goingson_core::deterministic_email_id(email.message_id.as_deref()); |
| 86 |
let received_at = format_datetime(&email.received_at.unwrap_or_else(Utc::now)); |
| 87 |
execute( |
| 88 |
conn, |
| 89 |
"INSERT INTO emails (id, user_id, project_id, from_address, to_address, subject, body, body_format, html_body, is_read, is_archived, received_at, message_id, in_reply_to, thread_id, email_account_id, is_outgoing, imap_uid, source_folder, attachment_meta, body_truncated, jmap_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", |
| 90 |
params![ |
| 91 |
id.to_string(), |
| 92 |
user_id.to_string(), |
| 93 |
email.project_id.as_ref().map(ToString::to_string), |
| 94 |
&email.from_address, |
| 95 |
&email.to_address, |
| 96 |
&email.subject, |
| 97 |
&email.body, |
| 98 |
email.body_format.db_value(), |
| 99 |
&email.html_body, |
| 100 |
i32::from(email.is_read), |
| 101 |
i32::from(email.is_archived), |
| 102 |
&received_at, |
| 103 |
&email.message_id, |
| 104 |
&email.in_reply_to, |
| 105 |
&email.thread_id, |
| 106 |
email.email_account_id.as_ref().map(ToString::to_string), |
| 107 |
i32::from(email.is_outgoing), |
| 108 |
email.imap_uid, |
| 109 |
&email.source_folder, |
| 110 |
&email.attachment_meta, |
| 111 |
i32::from(email.body_truncated), |
| 112 |
&email.jmap_id |
| 113 |
], |
| 114 |
)?; |
| 115 |
query::get_by_id(conn, id, user_id)? |
| 116 |
.ok_or_else(|| CoreError::internal("Failed to retrieve created email")) |
| 117 |
} |
| 118 |
|
| 119 |
|
| 120 |
pub(super) fn create_with_tracking_batch( |
| 121 |
conn: &mut Connection, |
| 122 |
user_id: UserId, |
| 123 |
emails: Vec<NewEmailWithTracking>, |
| 124 |
) -> Result<usize> { |
| 125 |
if emails.is_empty() { |
| 126 |
return Ok(0); |
| 127 |
} |
| 128 |
|
| 129 |
let mut count = 0usize; |
| 130 |
let uid = user_id.to_string(); |
| 131 |
|
| 132 |
let tx = conn.transaction().map_err(CoreError::database)?; |
| 133 |
|
| 134 |
for email in emails { |
| 135 |
let id = goingson_core::deterministic_email_id(email.message_id.as_deref()); |
| 136 |
let received_at = format_datetime(&email.received_at.unwrap_or_else(Utc::now)); |
| 137 |
let result = execute( |
| 138 |
&tx, |
| 139 |
"INSERT OR IGNORE INTO emails (id, user_id, project_id, from_address, to_address, subject, body, body_format, html_body, is_read, is_archived, received_at, message_id, in_reply_to, thread_id, email_account_id, is_outgoing, imap_uid, source_folder, attachment_meta, body_truncated, jmap_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", |
| 140 |
params![ |
| 141 |
id.to_string(), |
| 142 |
&uid, |
| 143 |
email.project_id.map(|p| p.to_string()), |
| 144 |
&email.from_address, |
| 145 |
&email.to_address, |
| 146 |
&email.subject, |
| 147 |
&email.body, |
| 148 |
email.body_format.db_value(), |
| 149 |
&email.html_body, |
| 150 |
i32::from(email.is_read), |
| 151 |
i32::from(email.is_archived), |
| 152 |
&received_at, |
| 153 |
&email.message_id, |
| 154 |
&email.in_reply_to, |
| 155 |
&email.thread_id, |
| 156 |
email.email_account_id.map(|a| a.to_string()), |
| 157 |
i32::from(email.is_outgoing), |
| 158 |
email.imap_uid, |
| 159 |
&email.source_folder, |
| 160 |
&email.attachment_meta, |
| 161 |
i32::from(email.body_truncated), |
| 162 |
&email.jmap_id |
| 163 |
], |
| 164 |
)?; |
| 165 |
if result > 0 { |
| 166 |
count += 1; |
| 167 |
} |
| 168 |
} |
| 169 |
|
| 170 |
tx.commit().map_err(CoreError::database)?; |
| 171 |
Ok(count) |
| 172 |
} |
| 173 |
|
| 174 |
|
| 175 |
pub(super) fn delete(conn: &Connection, id: EmailId, user_id: UserId) -> Result<bool> { |
| 176 |
let result = execute( |
| 177 |
conn, |
| 178 |
"DELETE FROM emails WHERE id = ? AND user_id = ?", |
| 179 |
params![id.to_string(), user_id.to_string()], |
| 180 |
)?; |
| 181 |
Ok(result > 0) |
| 182 |
} |
| 183 |
|