Skip to main content

io_m2dir/
coroutine.rs

1//! # Generator-shape coroutine contract
2//!
3//! Mirrors the shape of `core::ops::Coroutine`: a `Yield` associated type for
4//! intermediate progress, a `Return` associated type for terminal output, and a
5//! two-variant [`M2dirCoroutineState`] (`Yielded` / `Complete`).
6//!
7//! io-m2dir is filesystem-flavoured, so every coroutine in the crate picks the
8//! standard [`M2dirYield`] directly: it gathers every filesystem (and
9//! environmental) primitive the crate emits, namely directory create / remove /
10//! read, file create / read / exists / remove, path rename, and the process id
11//! / random bytes inputs needed to mint m2dir entry identifiers.
12//!
13//! [`M2dirClient::run`] runs any standard-Yield coroutine to completion
14//! against the local filesystem.
15//!
16//! [`M2dirClient::run`]: crate::client::M2dirClient::run
17
18use alloc::{
19    collections::{BTreeMap, BTreeSet},
20    vec::Vec,
21};
22
23use crate::path::M2dirPath;
24
25/// State yielded by an [`M2dirCoroutine::resume`] step.
26///
27/// Two-variant by design (matches std's `core::ops::CoroutineState`): any
28/// further variation lives inside the per-coroutine `Yield` type.
29#[derive(Debug)]
30pub enum M2dirCoroutineState<Y, R> {
31    /// Intermediate yield. The caller reacts to `Y` (do filesystem I/O, supply
32    /// pid / random bytes…) and resumes the coroutine again.
33    Yielded(Y),
34    /// Terminal yield. By convention `R = Result<Output, Error>`.
35    Complete(R),
36}
37
38/// Standard-shape m2dir coroutine.
39///
40/// Implementors own their internal state machine and declare their per-step
41/// `Yield` plus a terminal `Return`. The caller reacts to each `Yield` variant
42/// and resumes until `Complete`.
43pub trait M2dirCoroutine {
44    /// Intermediate value handed back on every step. Per-coroutine: each
45    /// implementor picks exactly the variants it needs. In io-m2dir every
46    /// coroutine picks [`M2dirYield`].
47    type Yield;
48    /// Terminal value. By convention `Result<Output, Error>`; the "ok" arm
49    /// carries the operation's final output, the "error" arm carries the cause.
50    type Return;
51
52    /// Advances the coroutine one step.
53    ///
54    /// Pass [`None`] on the initial call. Pass `Some(arg)` carrying the value
55    /// matching the previous `Yielded` variant.
56    fn resume(&mut self, arg: Option<M2dirArg>) -> M2dirCoroutineState<Self::Yield, Self::Return>;
57}
58
59/// Standard filesystem-only Yield. Every io-m2dir coroutine picks `type Yield =
60/// M2dirYield`.
61///
62/// Each variant is paired with the matching [`M2dirArg`] variant the caller
63/// feeds back on the next `resume`.
64#[derive(Debug)]
65pub enum M2dirYield {
66    /// Caller must supply the current process id and feed back
67    /// [`M2dirArg::Pid`].
68    WantsPid,
69    /// Caller must supply `len` random bytes and feed back
70    /// [`M2dirArg::Random`].
71    WantsRandom { len: usize },
72    /// Caller must check each path for existence as a regular file and feed
73    /// back [`M2dirArg::FileExists`].
74    WantsFileExists(BTreeSet<M2dirPath>),
75    /// Caller must list each directory's entries and feed back
76    /// [`M2dirArg::DirRead`].
77    WantsDirRead(BTreeSet<M2dirPath>),
78    /// Caller must create each directory (with parents) and feed back
79    /// [`M2dirArg::DirCreate`].
80    WantsDirCreate(BTreeSet<M2dirPath>),
81    /// Caller must recursively remove each directory and feed back
82    /// [`M2dirArg::DirRemove`].
83    WantsDirRemove(BTreeSet<M2dirPath>),
84    /// Caller must read each file's bytes and feed back [`M2dirArg::FileRead`].
85    WantsFileRead(BTreeSet<M2dirPath>),
86    /// Caller must write each `(path, bytes)` pair and feed back
87    /// [`M2dirArg::FileCreate`].
88    WantsFileCreate(BTreeMap<M2dirPath, Vec<u8>>),
89    /// Caller must remove each file and feed back [`M2dirArg::FileRemove`].
90    WantsFileRemove(BTreeSet<M2dirPath>),
91    /// Caller must rename each `(from, to)` pair and feed back
92    /// [`M2dirArg::Rename`].
93    WantsRename(Vec<(M2dirPath, M2dirPath)>),
94}
95
96/// Reply fed back into [`M2dirCoroutine::resume`] by the caller.
97///
98/// Each variant matches the corresponding [`M2dirYield`] request and carries
99/// the value the caller gathered while servicing it.
100#[derive(Clone, Debug)]
101pub enum M2dirArg {
102    /// Reply to [`M2dirYield::WantsPid`].
103    Pid(u32),
104    /// Reply to [`M2dirYield::WantsRandom`].
105    Random(Vec<u8>),
106    /// Reply to [`M2dirYield::WantsFileExists`]: probed path to whether it
107    /// exists as a regular file.
108    FileExists(BTreeMap<M2dirPath, bool>),
109    /// Reply to [`M2dirYield::WantsDirRead`]: probed directory to the set of
110    /// paths found inside.
111    DirRead(BTreeMap<M2dirPath, BTreeSet<M2dirPath>>),
112    /// Reply to [`M2dirYield::WantsDirCreate`].
113    DirCreate,
114    /// Reply to [`M2dirYield::WantsDirRemove`].
115    DirRemove,
116    /// Reply to [`M2dirYield::WantsFileRead`]: probed file to its bytes (or an
117    /// empty buffer when the file is missing).
118    FileRead(BTreeMap<M2dirPath, Vec<u8>>),
119    /// Reply to [`M2dirYield::WantsFileCreate`].
120    FileCreate,
121    /// Reply to [`M2dirYield::WantsFileRemove`].
122    FileRemove,
123    /// Reply to [`M2dirYield::WantsRename`].
124    Rename,
125}