Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/imbolc/step-machine
Run your Rust CLI programs as state machines with persistence and recovery abilities
https://github.com/imbolc/step-machine
cli rust state-machine
Last synced: about 2 months ago
JSON representation
Run your Rust CLI programs as state machines with persistence and recovery abilities
- Host: GitHub
- URL: https://github.com/imbolc/step-machine
- Owner: imbolc
- Created: 2021-08-21T07:43:42.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2023-06-06T07:50:59.000Z (over 1 year ago)
- Last Synced: 2024-10-28T17:18:04.605Z (2 months ago)
- Topics: cli, rust, state-machine
- Language: Rust
- Homepage:
- Size: 27.3 KB
- Stars: 34
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
[![version-badge][]][crate-url]
[![docs-badge][]][docs-url]
[![license-badge][]][crate-url]# step-machine
Run your CLI programs as state machines with persistence and recovery abilities. When such a
program breaks you'll have opportunity to change the external world (create a missing folder,
change a file permissions or something) and continue the program from the step it was
interrupted on.## Usage
Let's toss two coins and make sure they both landed on the same side. We express the behaviour
as two states of our machine. Step logic is implemented in `State::next()` methods which
return the next state or `None` for the last step (the full code is in `examples/coin.rs`).
```rust
#[derive(Debug, Serialize, Deserialize, From)]
enum Machine {
FirstToss(FirstToss),
SecondToss(SecondToss),
}#[derive(Debug, Serialize, Deserialize)]
struct FirstToss;
impl FirstToss {
fn next(self) -> StepResult {
let first_coin = Coin::toss();
println!("First coin: {:?}", first_coin);
Ok(Some(SecondToss { first_coin }.into()))
}
}#[derive(Debug, Serialize, Deserialize)]
struct SecondToss {
first_coin: Coin,
}
impl SecondToss {
fn next(self) -> StepResult {
let second_coin = Coin::toss();
println!("Second coin: {:?}", second_coin);
ensure!(second_coin == self.first_coin, "Coins landed differently");
println!("Coins match");
Ok(None)
}
}
```Then we start our machine like this:
```rust
let init_state = FirstToss.into();
let mut engine = Engine::::new(init_state)?.restore()?;
engine.drop_error()?;
engine.run()?;
```
We initialize the `Engine` with the first step. Then we restore the previous state if the
process was interrupted (e.g. by an error). Then we drop a possible error and run all the steps
to completion.Let's run it now:
```sh
$ cargo run --example coin
First coin: Heads
Second coin: Tails
Error: Coins landed differently
```We weren't lucky this time and the program resulted in an error. Let's run it again:
```sh
$ cargo run --example coin
Second coin: Heads
Coins match
```Notice that, thanks to the `restore()`, our machine run from the step it was interrupted,
knowing about the first coin landed on heads.[version-badge]: https://img.shields.io/crates/v/step-machine.svg
[docs-badge]: https://docs.rs/step-machine/badge.svg
[license-badge]: https://img.shields.io/crates/l/step-machine.svg
[crate-url]: https://crates.io/crates/step-machine
[docs-url]: https://docs.rs/step-machine