io_gmail/coroutine.rs
1//! Coroutine contract shared by every Gmail exchange.
2//!
3//! Defines the `GmailCoroutine` trait, its `GmailYield` and
4//! `GmailCoroutineState` companions, and the `gmail_try!` macro (the
5//! coroutine equivalent of `?`).
6
7use alloc::vec::Vec;
8
9/// Progress of a coroutine after a resume: either an I/O request the
10/// caller must fulfill, or the terminal value.
11#[derive(Debug)]
12pub enum GmailCoroutineState<Y, R> {
13 /// The coroutine needs I/O before it can go further.
14 Yielded(Y),
15 /// The coroutine finished with its terminal value.
16 Complete(R),
17}
18
19/// A resumable, I/O-free Gmail operation.
20pub trait GmailCoroutine {
21 /// The I/O request type yielded while the exchange is in progress.
22 type Yield;
23 /// The terminal value type produced when the exchange completes.
24 type Return;
25
26 /// Advances the coroutine with the bytes read since the last yield
27 /// (`None` when there is nothing to feed).
28 fn resume(&mut self, arg: Option<&[u8]>) -> GmailCoroutineState<Self::Yield, Self::Return>;
29}
30
31/// The I/O request a coroutine yields: read bytes from the stream, or
32/// write the given bytes to it.
33#[derive(Debug)]
34pub enum GmailYield {
35 /// The coroutine wants bytes read from the stream.
36 WantsRead,
37 /// The coroutine wants the given bytes written to the stream.
38 WantsWrite(Vec<u8>),
39}
40
41/// Resumes an inner coroutine, forwarding its yields and
42/// short-circuiting its errors: the coroutine equivalent of `?`.
43#[macro_export]
44macro_rules! gmail_try {
45 ($coroutine:expr, $arg:expr $(,)?) => {
46 match $crate::coroutine::GmailCoroutine::resume($coroutine, $arg) {
47 $crate::coroutine::GmailCoroutineState::Yielded(y) => {
48 return $crate::coroutine::GmailCoroutineState::Yielded(y.into());
49 }
50 $crate::coroutine::GmailCoroutineState::Complete(Err(err)) => {
51 log::trace!("error during coroutine execution: {err}");
52 return $crate::coroutine::GmailCoroutineState::Complete(Err(err.into()));
53 }
54 $crate::coroutine::GmailCoroutineState::Complete(Ok(value)) => value,
55 }
56 };
57}