io_smtp/coroutine.rs
1//! Generator-shape coroutine contract. 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 caller 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 /// The caller should read more bytes and feed them back on
33 /// resume.
34 WantsRead,
35 /// The caller should write these bytes; the next resume takes
36 /// `None`.
37 WantsWrite(Vec<u8>),
38}
39
40/// Coroutine `?`: forwards `Yielded` (via `Into`), short-circuits
41/// on `Err`, evaluates to the inner `Ok` value.
42#[macro_export]
43macro_rules! smtp_try {
44 ($coroutine:expr, $arg:expr $(,)?) => {
45 match $crate::coroutine::SmtpCoroutine::resume($coroutine, $arg) {
46 $crate::coroutine::SmtpCoroutineState::Yielded(y) => {
47 return $crate::coroutine::SmtpCoroutineState::Yielded(y.into());
48 }
49 $crate::coroutine::SmtpCoroutineState::Complete(Err(err)) => {
50 return $crate::coroutine::SmtpCoroutineState::Complete(Err(err.into()));
51 }
52 $crate::coroutine::SmtpCoroutineState::Complete(Ok(value)) => value,
53 }
54 };
55}