1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//! Backend integration layer.
//!
//! The backend module defines the abstraction that the TUI uses to communicate with
//! different mail providers. Backends expose a push-based event stream for mailbox
//! updates and a pull-based channel for action completion status. The contract is
//! intentionally asynchronous so the UI thread never blocks on network or disk I/O.
//! See [`MailBackend`] for details about the required behaviour.
use crate;
use Result;
use Receiver;
/// Notifications that backends emit when something about the mailbox changes.
///
/// The UI subscribes to the channel returned by [`MailBackend::load_mailbox`] and keeps
/// its local state in sync with the events that arrive.
/// Result information for a committed action.
///
/// Backends must send one result per action passed to [`MailBackend::apply_actions`].
/// Successful operations report `Ok(())`; failures should capture the backend-specific
/// error text so the UI can surface it to the user.
/// Snapshot of a mailbox returned by [`MailBackend::load_mailbox`].
/// Data associated with an outgoing message created from the compose view.
/// Abstraction over a mail provider implementation.
///
/// The trait is purposely synchronous from the caller's perspective while the
/// implementation is free to spawn its own async tasks. `apply_actions` returns an
/// `mpsc::Receiver` for streaming status updates; the UI polls that receiver every
/// frame so the terminal stays responsive during long-running commits.
///
/// # Design Notes
///
/// * `load_inbox` produces both the initial message list and a channel that streams
/// [`BackendEvent`] updates. This ensures there is a single source of truth for
/// mailbox mutations.
/// * `load_message` stays synchronous because the UI only invokes it when opening a
/// single message; implementors are free to run async code internally.
/// * `apply_actions` accepts the full action batch and must never block the caller.
/// Implementations should spawn work (e.g. on a thread or async runtime) and send
/// [`ActionStatus`] entries as each action completes.
///
/// # Examples
///
/// ```
/// use elma_rs::backend::{ActionStatus, MailBackend, MailboxSnapshot};
/// use elma_rs::model::{Action, ActionType, MessageId};
/// # struct DemoBackend;
/// # impl MailBackend for DemoBackend {
/// # fn load_inbox(&self) -> anyhow::Result<(MailboxSnapshot, std::sync::mpsc::Receiver<_>)> {
/// # unimplemented!()
/// # }
/// # fn load_message(&self, _id: MessageId) -> anyhow::Result<_> { unimplemented!() }
/// # fn apply_actions(&self, actions: Vec<Action>) -> anyhow::Result<std::sync::mpsc::Receiver<ActionStatus>> {
/// # // start background work here
/// # unimplemented!()
/// # }
/// # }
/// let backend = DemoBackend;
/// let statuses = backend.apply_actions(vec![Action::new(ActionType::Archive, MessageId(42))])?;
/// // UI polls `statuses` until every ActionStatus has been received.
/// # Ok::<_, anyhow::Error>(())
/// ```