io_smtp/coroutine.rs
1//! Generator-shape coroutine driver. Mirrors `core::ops::Coroutine`:
2//! `Yield` for intermediate progress, `Return` for terminal output,
3//! [`SmtpCoroutineState`] for both.
4
5use alloc::vec::Vec;
6
7/// State yielded by an [`SmtpCoroutine::resume`] step.
8#[derive(Debug)]
9pub enum SmtpCoroutineState<Y, R> {
10 /// Intermediate yield; the driver reacts and resumes.
11 Yielded(Y),
12 /// Terminal yield; by convention `R = Result<Output, Error>`.
13 Complete(R),
14}
15
16/// Standard-shape SMTP coroutine.
17pub trait SmtpCoroutine {
18 /// Per-step value.
19 type Yield;
20 /// Terminal value; by convention `Result<Output, Error>`.
21 type Return;
22
23 /// Advances the coroutine one step. Pass `None` for the initial
24 /// call or after a [`SmtpYield::WantsWrite`]; `Some(data)` after
25 /// a [`SmtpYield::WantsRead`]; `Some(&[])` to signal EOF.
26 fn resume(&mut self, arg: Option<&[u8]>) -> SmtpCoroutineState<Self::Yield, Self::Return>;
27}
28
29/// Standard I/O-only Yield; every coroutine in this crate picks it.
30#[derive(Debug)]
31pub enum SmtpYield {
32 /// Driver should read more bytes and feed them back on resume.
33 WantsRead,
34 /// Driver should write these bytes; the next resume takes `None`.
35 WantsWrite(Vec<u8>),
36}
37
38/// Coroutine `?`: forwards `Yielded` (via `Into`), short-circuits
39/// on `Err`, evaluates to the inner `Ok` value.
40#[macro_export]
41macro_rules! smtp_try {
42 ($coroutine:expr, $arg:expr $(,)?) => {
43 match $crate::coroutine::SmtpCoroutine::resume($coroutine, $arg) {
44 $crate::coroutine::SmtpCoroutineState::Yielded(y) => {
45 return $crate::coroutine::SmtpCoroutineState::Yielded(y.into());
46 }
47 $crate::coroutine::SmtpCoroutineState::Complete(Err(err)) => {
48 return $crate::coroutine::SmtpCoroutineState::Complete(Err(err.into()));
49 }
50 $crate::coroutine::SmtpCoroutineState::Complete(Ok(value)) => value,
51 }
52 };
53}