io_jmap/coroutine.rs
1//! Generator-shape coroutine contract.
2//!
3//! Mirrors `core::ops::Coroutine`: a `Yield` associated type for intermediate
4//! progress, a `Return` for terminal output, and a two-variant
5//! [`JmapCoroutineState`].
6//!
7//! Most coroutines pick the standard [`JmapYield`] (I/O-only); redirect-aware
8//! ones declare their own (e.g. [`JmapRedirectYield`]).
9//!
10//! [`JmapRedirectYield`]: crate::rfc8620::coroutine::JmapRedirectYield
11
12use alloc::vec::Vec;
13
14/// State yielded by a [`JmapCoroutine::resume`] step.
15#[derive(Debug)]
16pub enum JmapCoroutineState<Y, R> {
17 /// Intermediate yield: the caller reacts and resumes.
18 Yielded(Y),
19 /// Terminal yield. By convention `R = Result<Output, Error>`.
20 Complete(R),
21}
22
23/// Standard-shape JMAP coroutine.
24pub trait JmapCoroutine {
25 /// Intermediate value handed back on every step.
26 type Yield;
27 /// Terminal value. By convention `Result<Output, Error>`.
28 type Return;
29
30 /// Advances the coroutine one step.
31 ///
32 /// Pass [`None`] on the initial call or after a [`JmapYield::WantsWrite`].
33 /// Pass `Some(data)` after a [`JmapYield::WantsRead`]; `Some(&[])` signals
34 /// EOF.
35 fn resume(&mut self, arg: Option<&[u8]>) -> JmapCoroutineState<Self::Yield, Self::Return>;
36}
37
38/// Standard I/O-only Yield for coroutines that only read/write socket bytes.
39#[derive(Debug)]
40pub enum JmapYield {
41 /// The caller reads more bytes and feeds them back on the next resume.
42 WantsRead,
43 /// The caller writes these bytes; the next resume typically takes `None`.
44 WantsWrite(Vec<u8>),
45}
46
47/// Coroutine `?`: forwards `Yielded` (via `Into`), short-circuits on
48/// `Err` (via `Into`), evaluates to the inner `Ok` value.
49#[macro_export]
50macro_rules! jmap_try {
51 ($coroutine:expr, $arg:expr $(,)?) => {
52 match $crate::coroutine::JmapCoroutine::resume($coroutine, $arg) {
53 $crate::coroutine::JmapCoroutineState::Yielded(y) => {
54 return $crate::coroutine::JmapCoroutineState::Yielded(y.into());
55 }
56 $crate::coroutine::JmapCoroutineState::Complete(Err(err)) => {
57 return $crate::coroutine::JmapCoroutineState::Complete(Err(err.into()));
58 }
59 $crate::coroutine::JmapCoroutineState::Complete(Ok(value)) => value,
60 }
61 };
62}