1use mxr_core::id::*;
2use mxr_core::types::*;
3
4impl super::Store {
5 pub async fn upsert_label(&self, label: &Label) -> Result<(), sqlx::Error> {
6 let id = label.id.as_str();
7 let account_id = label.account_id.as_str();
8 let kind = match label.kind {
9 LabelKind::System => "system",
10 LabelKind::Folder => "folder",
11 LabelKind::User => "user",
12 };
13 let unread_count = label.unread_count as i64;
14 let total_count = label.total_count as i64;
15
16 sqlx::query!(
20 "INSERT INTO labels (id, account_id, name, kind, color, provider_id, unread_count, total_count)
21 VALUES (?, ?, ?, ?, ?, ?, ?, ?)
22 ON CONFLICT(id) DO UPDATE SET
23 name = excluded.name,
24 kind = excluded.kind,
25 color = excluded.color,
26 provider_id = excluded.provider_id",
27 id,
28 account_id,
29 label.name,
30 kind,
31 label.color,
32 label.provider_id,
33 unread_count,
34 total_count,
35 )
36 .execute(self.writer())
37 .await?;
38
39 Ok(())
40 }
41
42 pub async fn list_labels_by_account(
43 &self,
44 account_id: &AccountId,
45 ) -> Result<Vec<Label>, sqlx::Error> {
46 let aid = account_id.as_str();
47 let rows = sqlx::query!(
48 r#"SELECT id as "id!", account_id as "account_id!", name as "name!",
49 kind as "kind!", color, provider_id as "provider_id!",
50 unread_count as "unread_count!", total_count as "total_count!"
51 FROM labels WHERE account_id = ?"#,
52 aid,
53 )
54 .fetch_all(self.reader())
55 .await?;
56
57 Ok(rows
58 .into_iter()
59 .map(|r| Label {
60 id: LabelId::from_uuid(uuid::Uuid::parse_str(&r.id).unwrap()),
61 account_id: AccountId::from_uuid(uuid::Uuid::parse_str(&r.account_id).unwrap()),
62 name: r.name,
63 kind: match r.kind.as_str() {
64 "system" => LabelKind::System,
65 "folder" => LabelKind::Folder,
66 _ => LabelKind::User,
67 },
68 color: r.color,
69 provider_id: r.provider_id,
70 unread_count: r.unread_count as u32,
71 total_count: r.total_count as u32,
72 })
73 .collect())
74 }
75
76 pub async fn update_label_counts(
77 &self,
78 label_id: &LabelId,
79 unread_count: u32,
80 total_count: u32,
81 ) -> Result<(), sqlx::Error> {
82 let lid = label_id.as_str();
83 let unread = unread_count as i64;
84 let total = total_count as i64;
85 sqlx::query!(
86 "UPDATE labels SET unread_count = ?, total_count = ? WHERE id = ?",
87 unread,
88 total,
89 lid,
90 )
91 .execute(self.writer())
92 .await?;
93 Ok(())
94 }
95
96 pub async fn delete_label(&self, label_id: &LabelId) -> Result<(), sqlx::Error> {
97 sqlx::query("DELETE FROM labels WHERE id = ?")
98 .bind(label_id.as_str())
99 .execute(self.writer())
100 .await?;
101 Ok(())
102 }
103
104 pub async fn replace_label(
105 &self,
106 old_label_id: &LabelId,
107 new_label: &Label,
108 ) -> Result<(), sqlx::Error> {
109 let mut tx = self.writer().begin().await?;
110
111 let existing: Option<(i64, i64)> =
112 sqlx::query_as("SELECT unread_count, total_count FROM labels WHERE id = ?")
113 .bind(old_label_id.as_str())
114 .fetch_optional(&mut *tx)
115 .await?;
116
117 let (unread_count, total_count) = existing.unwrap_or((0, 0));
118 let kind = match new_label.kind {
119 LabelKind::System => "system",
120 LabelKind::Folder => "folder",
121 LabelKind::User => "user",
122 };
123
124 sqlx::query(
125 "INSERT INTO labels (id, account_id, name, kind, color, provider_id, unread_count, total_count)
126 VALUES (?, ?, ?, ?, ?, ?, ?, ?)
127 ON CONFLICT(id) DO UPDATE SET
128 account_id = excluded.account_id,
129 name = excluded.name,
130 kind = excluded.kind,
131 color = excluded.color,
132 provider_id = excluded.provider_id,
133 unread_count = excluded.unread_count,
134 total_count = excluded.total_count",
135 )
136 .bind(new_label.id.as_str())
137 .bind(new_label.account_id.as_str())
138 .bind(&new_label.name)
139 .bind(kind)
140 .bind(&new_label.color)
141 .bind(&new_label.provider_id)
142 .bind(unread_count)
143 .bind(total_count)
144 .execute(&mut *tx)
145 .await?;
146
147 if old_label_id != &new_label.id {
148 sqlx::query("UPDATE message_labels SET label_id = ? WHERE label_id = ?")
149 .bind(new_label.id.as_str())
150 .bind(old_label_id.as_str())
151 .execute(&mut *tx)
152 .await?;
153
154 sqlx::query("DELETE FROM labels WHERE id = ?")
155 .bind(old_label_id.as_str())
156 .execute(&mut *tx)
157 .await?;
158 }
159
160 tx.commit().await?;
161 Ok(())
162 }
163
164 pub async fn recalculate_label_counts(
165 &self,
166 account_id: &AccountId,
167 ) -> Result<(), sqlx::Error> {
168 let aid = account_id.as_str();
169 sqlx::query!(
170 "UPDATE labels SET
171 total_count = (SELECT COUNT(*) FROM message_labels WHERE label_id = labels.id),
172 unread_count = (SELECT COUNT(*) FROM message_labels ml
173 JOIN messages m ON m.id = ml.message_id
174 WHERE ml.label_id = labels.id AND (m.flags & 1) = 0)
175 WHERE account_id = ?",
176 aid,
177 )
178 .execute(self.writer())
179 .await?;
180 Ok(())
181 }
182
183 pub async fn find_labels_by_provider_ids(
186 &self,
187 account_id: &AccountId,
188 provider_ids: &[String],
189 ) -> Result<Vec<LabelId>, sqlx::Error> {
190 if provider_ids.is_empty() {
191 return Ok(vec![]);
192 }
193 let aid = account_id.as_str();
194 let placeholders: Vec<String> = provider_ids.iter().map(|_| "?".to_string()).collect();
195 let sql = format!(
196 "SELECT id FROM labels WHERE account_id = ? AND provider_id IN ({})",
197 placeholders.join(", ")
198 );
199 let mut query = sqlx::query_scalar::<_, String>(&sql).bind(aid);
200 for pid in provider_ids {
201 query = query.bind(pid);
202 }
203 let rows = query.fetch_all(self.reader()).await?;
204 Ok(rows
205 .into_iter()
206 .map(|id| LabelId::from_uuid(uuid::Uuid::parse_str(&id).unwrap()))
207 .collect())
208 }
209
210 pub async fn find_label_by_provider_id(
211 &self,
212 account_id: &AccountId,
213 provider_id: &str,
214 ) -> Result<Option<Label>, sqlx::Error> {
215 let aid = account_id.as_str();
216 let row = sqlx::query!(
217 r#"SELECT id as "id!", account_id as "account_id!", name as "name!",
218 kind as "kind!", color, provider_id as "provider_id!",
219 unread_count as "unread_count!", total_count as "total_count!"
220 FROM labels WHERE account_id = ? AND provider_id = ?"#,
221 aid,
222 provider_id,
223 )
224 .fetch_optional(self.reader())
225 .await?;
226
227 Ok(row.map(|r| Label {
228 id: LabelId::from_uuid(uuid::Uuid::parse_str(&r.id).unwrap()),
229 account_id: AccountId::from_uuid(uuid::Uuid::parse_str(&r.account_id).unwrap()),
230 name: r.name,
231 kind: match r.kind.as_str() {
232 "system" => LabelKind::System,
233 "folder" => LabelKind::Folder,
234 _ => LabelKind::User,
235 },
236 color: r.color,
237 provider_id: r.provider_id,
238 unread_count: r.unread_count as u32,
239 total_count: r.total_count as u32,
240 }))
241 }
242}