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}