Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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

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