Skip to main content

io_vdir/
coroutine.rs

1//! # Generator-shape coroutine driver
2//!
3//! Mirrors the shape of `core::ops::Coroutine`: a `Yield` associated
4//! type for intermediate progress, a `Return` associated type for
5//! terminal output, and a two-variant [`VdirCoroutineState`]
6//! (`Yielded` / `Complete`).
7//!
8//! io-vdir is filesystem-flavoured, so every coroutine in the crate
9//! picks the standard [`VdirYield`] directly: it gathers every
10//! filesystem primitive the crate emits, namely directory create /
11//! remove / read / exists, file create / read / exists / remove, path
12//! rename / copy, plus the random bytes input needed to mint new item
13//! identifiers.
14//!
15//! [`VdirClient::run`] drives any standard-Yield coroutine to
16//! completion against the local filesystem.
17//!
18//! [`VdirClient::run`]: crate::client::VdirClient::run
19
20use alloc::{
21    collections::{BTreeMap, BTreeSet},
22    vec::Vec,
23};
24
25use crate::path::VdirPath;
26
27/// State yielded by a [`VdirCoroutine::resume`] step.
28///
29/// Two-variant by design (matches std's `core::ops::CoroutineState`):
30/// any further variation lives inside the per-coroutine `Yield` type.
31#[derive(Debug)]
32pub enum VdirCoroutineState<Y, R> {
33    /// Intermediate yield. The driver reacts to `Y` (perform the
34    /// requested filesystem op, supply random bytes) and resumes the
35    /// coroutine again.
36    Yielded(Y),
37    /// Terminal yield. By convention `R = Result<Output, Error>`.
38    Complete(R),
39}
40
41/// Standard-shape Vdir coroutine.
42///
43/// Implementors own their internal state machine and declare their
44/// per-step `Yield` plus a terminal `Return`. The driver reacts to
45/// each `Yield` variant and resumes until `Complete`.
46pub trait VdirCoroutine {
47    /// Intermediate value handed back on every step. Per-coroutine:
48    /// each implementor picks exactly the variants it needs. In
49    /// io-vdir every coroutine picks [`VdirYield`].
50    type Yield;
51    /// Terminal value. By convention `Result<Output, Error>`; the "ok"
52    /// arm carries the operation's final output, the "error" arm
53    /// carries the cause.
54    type Return;
55
56    /// Advances the coroutine one step.
57    ///
58    /// Pass [`None`] on the initial call. Pass `Some(arg)` carrying the
59    /// value matching the previous `Yielded` variant.
60    fn resume(&mut self, arg: Option<VdirReply>) -> VdirCoroutineState<Self::Yield, Self::Return>;
61}
62
63/// Standard filesystem Yield. Every io-vdir coroutine picks `type Yield
64/// = VdirYield`.
65///
66/// Each variant is paired with the matching [`VdirReply`] variant the
67/// driver feeds back on the next `resume`.
68#[derive(Debug)]
69pub enum VdirYield {
70    /// Driver must supply `len` random bytes and feed back
71    /// [`VdirReply::Random`].
72    WantsRandom {
73        /// Number of random bytes the driver must supply.
74        len: usize,
75    },
76
77    /// Driver must check each path for existence as a regular file and
78    /// feed back [`VdirReply::FileExists`].
79    WantsFileExists(BTreeSet<VdirPath>),
80
81    /// Driver must check each path for existence as a directory and
82    /// feed back [`VdirReply::DirExists`].
83    WantsDirExists(BTreeSet<VdirPath>),
84
85    /// Driver must list each directory's entries and feed back
86    /// [`VdirReply::DirRead`].
87    WantsDirRead(BTreeSet<VdirPath>),
88
89    /// Driver must read each file's bytes and feed back
90    /// [`VdirReply::FileRead`].
91    WantsFileRead(BTreeSet<VdirPath>),
92
93    /// Driver must write each `(path, bytes)` pair and feed back
94    /// [`VdirReply::FileCreate`].
95    WantsFileCreate(BTreeMap<VdirPath, Vec<u8>>),
96
97    /// Driver must create each directory (with parents) and feed back
98    /// [`VdirReply::DirCreate`].
99    WantsDirCreate(BTreeSet<VdirPath>),
100
101    /// Driver must recursively remove each directory and feed back
102    /// [`VdirReply::DirRemove`].
103    WantsDirRemove(BTreeSet<VdirPath>),
104
105    /// Driver must remove each file and feed back
106    /// [`VdirReply::FileRemove`].
107    WantsFileRemove(BTreeSet<VdirPath>),
108
109    /// Driver must rename each `(from, to)` pair and feed back
110    /// [`VdirReply::Rename`].
111    WantsRename(Vec<(VdirPath, VdirPath)>),
112
113    /// Driver must copy each `(from, to)` pair and feed back
114    /// [`VdirReply::Copy`].
115    WantsCopy(Vec<(VdirPath, VdirPath)>),
116}
117
118/// Reply fed back into [`VdirCoroutine::resume`] by the driver.
119///
120/// One variant per [`VdirYield`] request; the coroutine asserts the
121/// variant it expects and ignores the rest.
122#[derive(Clone, Debug)]
123pub enum VdirReply {
124    /// Reply to [`VdirYield::WantsRandom`].
125    Random(Vec<u8>),
126
127    /// Reply to [`VdirYield::WantsFileExists`].
128    FileExists(BTreeMap<VdirPath, bool>),
129
130    /// Reply to [`VdirYield::WantsDirExists`].
131    DirExists(BTreeMap<VdirPath, bool>),
132
133    /// Reply to [`VdirYield::WantsDirRead`].
134    DirRead(BTreeMap<VdirPath, BTreeSet<VdirPath>>),
135
136    /// Reply to [`VdirYield::WantsFileRead`].
137    FileRead(BTreeMap<VdirPath, Vec<u8>>),
138
139    /// Acknowledgement of [`VdirYield::WantsFileCreate`].
140    FileCreate,
141
142    /// Acknowledgement of [`VdirYield::WantsDirCreate`].
143    DirCreate,
144
145    /// Acknowledgement of [`VdirYield::WantsDirRemove`].
146    DirRemove,
147
148    /// Acknowledgement of [`VdirYield::WantsFileRemove`].
149    FileRemove,
150
151    /// Acknowledgement of [`VdirYield::WantsRename`].
152    Rename,
153
154    /// Acknowledgement of [`VdirYield::WantsCopy`].
155    Copy,
156}
157
158/// Coroutine `?`: forwards `Yielded` (via `Into`), short-circuits on
159/// `Err`, evaluates to the inner `Ok` value.
160#[macro_export]
161macro_rules! vdir_try {
162    ($coroutine:expr, $arg:expr $(,)?) => {
163        match $crate::coroutine::VdirCoroutine::resume($coroutine, $arg) {
164            $crate::coroutine::VdirCoroutineState::Yielded(y) => {
165                return $crate::coroutine::VdirCoroutineState::Yielded(y.into());
166            }
167            $crate::coroutine::VdirCoroutineState::Complete(Err(err)) => {
168                return $crate::coroutine::VdirCoroutineState::Complete(Err(err.into()));
169            }
170            $crate::coroutine::VdirCoroutineState::Complete(Ok(value)) => value,
171        }
172    };
173}