1use mxr_core::id::*;
2use mxr_core::types::*;
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct IpcMessage {
7 pub id: u64,
8 pub payload: IpcPayload,
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(tag = "type")]
13#[allow(clippy::large_enum_variant)]
14pub enum IpcPayload {
15 Request(Request),
16 Response(Response),
17 Event(DaemonEvent),
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21#[serde(tag = "cmd")]
22pub enum Request {
23 ListEnvelopes {
24 label_id: Option<LabelId>,
25 account_id: Option<AccountId>,
26 limit: u32,
27 offset: u32,
28 },
29 ListEnvelopesByIds {
30 message_ids: Vec<MessageId>,
31 },
32 GetEnvelope {
33 message_id: MessageId,
34 },
35 GetBody {
36 message_id: MessageId,
37 },
38 DownloadAttachment {
39 message_id: MessageId,
40 attachment_id: AttachmentId,
41 },
42 OpenAttachment {
43 message_id: MessageId,
44 attachment_id: AttachmentId,
45 },
46 ListBodies {
47 message_ids: Vec<MessageId>,
48 },
49 GetThread {
50 thread_id: ThreadId,
51 },
52 ListLabels {
53 account_id: Option<AccountId>,
54 },
55 CreateLabel {
56 name: String,
57 color: Option<String>,
58 account_id: Option<AccountId>,
59 },
60 DeleteLabel {
61 name: String,
62 account_id: Option<AccountId>,
63 },
64 RenameLabel {
65 old: String,
66 new: String,
67 account_id: Option<AccountId>,
68 },
69 ListRules,
70 ListAccounts,
71 ListAccountsConfig,
72 AuthorizeAccountConfig {
73 account: AccountConfigData,
74 reauthorize: bool,
75 },
76 UpsertAccountConfig {
77 account: AccountConfigData,
78 },
79 SetDefaultAccount {
80 key: String,
81 },
82 TestAccountConfig {
83 account: AccountConfigData,
84 },
85 GetRule {
86 rule: String,
87 },
88 GetRuleForm {
89 rule: String,
90 },
91 UpsertRule {
92 rule: serde_json::Value,
93 },
94 UpsertRuleForm {
95 existing_rule: Option<String>,
96 name: String,
97 condition: String,
98 action: String,
99 priority: i32,
100 enabled: bool,
101 },
102 DeleteRule {
103 rule: String,
104 },
105 DryRunRules {
106 rule: Option<String>,
107 all: bool,
108 after: Option<String>,
109 },
110 ListEvents {
111 limit: u32,
112 level: Option<String>,
113 category: Option<String>,
114 },
115 GetLogs {
116 limit: u32,
117 level: Option<String>,
118 },
119 GetDoctorReport,
120 GenerateBugReport {
121 verbose: bool,
122 full_logs: bool,
123 since: Option<String>,
124 },
125 ListRuleHistory {
126 rule: Option<String>,
127 limit: u32,
128 },
129 Search {
130 query: String,
131 limit: u32,
132 },
133 SyncNow {
134 account_id: Option<AccountId>,
135 },
136 GetSyncStatus {
137 account_id: AccountId,
138 },
139 SetFlags {
140 message_id: MessageId,
141 flags: MessageFlags,
142 },
143 Count {
144 query: String,
145 },
146 GetHeaders {
147 message_id: MessageId,
148 },
149 ListSavedSearches,
150 ListSubscriptions {
151 limit: u32,
152 },
153 CreateSavedSearch {
154 name: String,
155 query: String,
156 },
157 DeleteSavedSearch {
158 name: String,
159 },
160 RunSavedSearch {
161 name: String,
162 limit: u32,
163 },
164 Mutation(MutationCommand),
166 Unsubscribe {
167 message_id: MessageId,
168 },
169 Snooze {
170 message_id: MessageId,
171 wake_at: chrono::DateTime<chrono::Utc>,
172 },
173 Unsnooze {
174 message_id: MessageId,
175 },
176 ListSnoozed,
177 PrepareReply {
179 message_id: MessageId,
180 reply_all: bool,
181 },
182 PrepareForward {
183 message_id: MessageId,
184 },
185 SendDraft {
186 draft: Draft,
187 },
188 SaveDraftToServer {
190 draft: Draft,
191 },
192 ListDrafts,
193
194 ExportThread {
196 thread_id: ThreadId,
197 format: ExportFormat,
198 },
199 ExportSearch {
200 query: String,
201 format: ExportFormat,
202 },
203
204 GetStatus,
205 Ping,
206 Shutdown,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211#[serde(tag = "mutation")]
212pub enum MutationCommand {
213 Archive {
214 message_ids: Vec<MessageId>,
215 },
216 Trash {
217 message_ids: Vec<MessageId>,
218 },
219 Spam {
220 message_ids: Vec<MessageId>,
221 },
222 Star {
223 message_ids: Vec<MessageId>,
224 starred: bool,
225 },
226 SetRead {
227 message_ids: Vec<MessageId>,
228 read: bool,
229 },
230 ModifyLabels {
231 message_ids: Vec<MessageId>,
232 add: Vec<String>,
233 remove: Vec<String>,
234 },
235 Move {
236 message_ids: Vec<MessageId>,
237 target_label: String,
238 },
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct ReplyContext {
244 pub in_reply_to: String,
245 pub references: Vec<String>,
246 pub reply_to: String,
247 pub cc: String,
248 pub subject: String,
249 pub from: String,
250 pub thread_context: String,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct ForwardContext {
256 pub subject: String,
257 pub from: String,
258 pub forwarded_content: String,
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize)]
262#[serde(tag = "status")]
263#[allow(clippy::large_enum_variant)]
264pub enum Response {
265 Ok { data: ResponseData },
266 Error { message: String },
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
270#[serde(tag = "kind")]
271#[allow(clippy::large_enum_variant)]
272pub enum ResponseData {
273 Envelopes {
274 envelopes: Vec<Envelope>,
275 },
276 Envelope {
277 envelope: Envelope,
278 },
279 Body {
280 body: MessageBody,
281 },
282 AttachmentFile {
283 file: AttachmentFile,
284 },
285 Bodies {
286 bodies: Vec<MessageBody>,
287 },
288 Thread {
289 thread: Thread,
290 messages: Vec<Envelope>,
291 },
292 Labels {
293 labels: Vec<Label>,
294 },
295 Label {
296 label: Label,
297 },
298 Rules {
299 rules: Vec<serde_json::Value>,
300 },
301 RuleData {
302 rule: serde_json::Value,
303 },
304 Accounts {
305 accounts: Vec<AccountSummaryData>,
306 },
307 AccountsConfig {
308 accounts: Vec<AccountConfigData>,
309 },
310 AccountOperation {
311 result: AccountOperationResult,
312 },
313 RuleFormData {
314 form: RuleFormData,
315 },
316 RuleDryRun {
317 results: Vec<serde_json::Value>,
318 },
319 EventLogEntries {
320 entries: Vec<EventLogEntry>,
321 },
322 LogLines {
323 lines: Vec<String>,
324 },
325 DoctorReport {
326 report: DoctorReport,
327 },
328 BugReport {
329 content: String,
330 },
331 RuleHistory {
332 entries: Vec<serde_json::Value>,
333 },
334 SearchResults {
335 results: Vec<SearchResultItem>,
336 },
337 SyncStatus {
338 sync: AccountSyncStatus,
339 },
340 Count {
341 count: u32,
342 },
343 Headers {
344 headers: Vec<(String, String)>,
345 },
346 SavedSearches {
347 searches: Vec<mxr_core::types::SavedSearch>,
348 },
349 Subscriptions {
350 subscriptions: Vec<mxr_core::types::SubscriptionSummary>,
351 },
352 SavedSearchData {
353 search: mxr_core::types::SavedSearch,
354 },
355 Status {
356 uptime_secs: u64,
357 accounts: Vec<String>,
358 total_messages: u32,
359 daemon_pid: Option<u32>,
360 sync_statuses: Vec<AccountSyncStatus>,
361 },
362 ReplyContext {
363 context: ReplyContext,
364 },
365 ForwardContext {
366 context: ForwardContext,
367 },
368 Drafts {
369 drafts: Vec<Draft>,
370 },
371 SnoozedMessages {
372 snoozed: Vec<Snoozed>,
373 },
374 ExportResult {
375 content: String,
376 },
377 Pong,
378 Ack,
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct SearchResultItem {
383 pub message_id: MessageId,
384 pub account_id: AccountId,
385 pub thread_id: ThreadId,
386 pub score: f32,
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize)]
390pub struct AttachmentFile {
391 pub attachment_id: AttachmentId,
392 pub filename: String,
393 pub path: String,
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct EventLogEntry {
398 pub timestamp: i64,
399 pub level: String,
400 pub category: String,
401 pub account_id: Option<AccountId>,
402 pub message_id: Option<String>,
403 pub rule_id: Option<String>,
404 pub summary: String,
405 pub details: Option<String>,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
409pub struct AccountSyncStatus {
410 pub account_id: AccountId,
411 pub account_name: String,
412 pub last_attempt_at: Option<String>,
413 pub last_success_at: Option<String>,
414 pub last_error: Option<String>,
415 pub failure_class: Option<String>,
416 pub consecutive_failures: u32,
417 pub backoff_until: Option<String>,
418 pub sync_in_progress: bool,
419 pub current_cursor_summary: Option<String>,
420 pub last_synced_count: u32,
421 pub healthy: bool,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct DoctorReport {
426 pub healthy: bool,
427 pub data_dir_exists: bool,
428 pub database_exists: bool,
429 pub index_exists: bool,
430 pub socket_exists: bool,
431 pub socket_reachable: bool,
432 pub stale_socket: bool,
433 pub daemon_running: bool,
434 pub daemon_pid: Option<u32>,
435 pub index_lock_held: bool,
436 pub index_lock_error: Option<String>,
437 pub database_path: String,
438 pub database_size_bytes: u64,
439 pub index_path: String,
440 pub index_size_bytes: u64,
441 pub log_path: String,
442 pub log_size_bytes: u64,
443 pub sync_statuses: Vec<AccountSyncStatus>,
444 pub recent_sync_events: Vec<EventLogEntry>,
445 pub recent_error_logs: Vec<String>,
446 pub recommended_next_steps: Vec<String>,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize)]
450pub struct RuleFormData {
451 pub id: Option<String>,
452 pub name: String,
453 pub condition: String,
454 pub action: String,
455 pub priority: i32,
456 pub enabled: bool,
457}
458
459#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct AccountConfigData {
461 pub key: String,
462 pub name: String,
463 pub email: String,
464 pub sync: Option<AccountSyncConfigData>,
465 pub send: Option<AccountSendConfigData>,
466 pub is_default: bool,
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
470#[serde(rename_all = "snake_case")]
471pub enum AccountSourceData {
472 Runtime,
473 Config,
474 Both,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
478#[serde(rename_all = "snake_case")]
479pub enum AccountEditModeData {
480 Full,
481 RuntimeOnly,
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct AccountSummaryData {
486 pub account_id: AccountId,
487 pub key: Option<String>,
488 pub name: String,
489 pub email: String,
490 pub provider_kind: String,
491 pub sync_kind: Option<String>,
492 pub send_kind: Option<String>,
493 pub enabled: bool,
494 pub is_default: bool,
495 pub source: AccountSourceData,
496 pub editable: AccountEditModeData,
497 pub sync: Option<AccountSyncConfigData>,
498 pub send: Option<AccountSendConfigData>,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
502#[serde(rename_all = "snake_case")]
503pub enum GmailCredentialSourceData {
504 #[default]
505 Bundled,
506 Custom,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize)]
510#[serde(tag = "type", rename_all = "snake_case")]
511pub enum AccountSyncConfigData {
512 Gmail {
513 #[serde(default)]
514 credential_source: GmailCredentialSourceData,
515 client_id: String,
516 client_secret: Option<String>,
517 token_ref: String,
518 },
519 Imap {
520 host: String,
521 port: u16,
522 username: String,
523 password_ref: String,
524 password: Option<String>,
525 use_tls: bool,
526 },
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize)]
530pub struct AccountOperationStep {
531 pub ok: bool,
532 pub detail: String,
533}
534
535#[derive(Debug, Clone, Serialize, Deserialize)]
536pub struct AccountOperationResult {
537 pub ok: bool,
538 pub summary: String,
539 pub save: Option<AccountOperationStep>,
540 pub auth: Option<AccountOperationStep>,
541 pub sync: Option<AccountOperationStep>,
542 pub send: Option<AccountOperationStep>,
543}
544
545#[derive(Debug, Clone, Serialize, Deserialize)]
546#[serde(tag = "type", rename_all = "snake_case")]
547pub enum AccountSendConfigData {
548 Gmail,
549 Smtp {
550 host: String,
551 port: u16,
552 username: String,
553 password_ref: String,
554 password: Option<String>,
555 use_tls: bool,
556 },
557}
558
559#[derive(Debug, Clone, Serialize, Deserialize)]
560#[serde(tag = "event")]
561pub enum DaemonEvent {
562 SyncCompleted {
563 account_id: AccountId,
564 messages_synced: u32,
565 },
566 SyncError {
567 account_id: AccountId,
568 error: String,
569 },
570 NewMessages {
571 envelopes: Vec<Envelope>,
572 },
573 MessageUnsnoozed {
574 message_id: MessageId,
575 },
576 LabelCountsUpdated {
577 counts: Vec<LabelCount>,
578 },
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize)]
582pub struct LabelCount {
583 pub label_id: LabelId,
584 pub unread_count: u32,
585 pub total_count: u32,
586}