{"id":19935984,"url":"https://github.com/imbolc/step-machine","last_synced_at":"2025-05-03T12:31:34.570Z","repository":{"id":57668656,"uuid":"398497940","full_name":"imbolc/step-machine","owner":"imbolc","description":"Run your Rust CLI programs as state machines with persistence and recovery abilities","archived":false,"fork":false,"pushed_at":"2023-06-06T07:50:59.000Z","size":28,"stargazers_count":36,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-07T16:08:16.406Z","etag":null,"topics":["cli","rust","state-machine"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/imbolc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-21T07:43:42.000Z","updated_at":"2025-03-31T05:18:09.000Z","dependencies_parsed_at":"2024-11-12T23:24:36.175Z","dependency_job_id":"297a180e-5e04-4af8-98b1-57fa01eec91d","html_url":"https://github.com/imbolc/step-machine","commit_stats":{"total_commits":9,"total_committers":1,"mean_commits":9.0,"dds":0.0,"last_synced_commit":"676da777665300e5326a99dd95dd28ff07c02408"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imbolc%2Fstep-machine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imbolc%2Fstep-machine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imbolc%2Fstep-machine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imbolc%2Fstep-machine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/imbolc","download_url":"https://codeload.github.com/imbolc/step-machine/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252191055,"owners_count":21709003,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","rust","state-machine"],"created_at":"2024-11-12T23:22:55.174Z","updated_at":"2025-05-03T12:31:34.115Z","avatar_url":"https://github.com/imbolc.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![version-badge][]][crate-url]\n[![docs-badge][]][docs-url]\n[![license-badge][]][crate-url]\n\n# step-machine\n\nRun your CLI programs as state machines with persistence and recovery abilities. When such a\nprogram breaks you'll have opportunity to change the external world (create a missing folder,\nchange a file permissions or something) and continue the program from the step it was\ninterrupted on.\n\n## Usage\n\nLet's toss two coins and make sure they both landed on the same side. We express the behaviour\nas two states of our machine. Step logic is implemented in `State::next()` methods which\nreturn the next state or `None` for the last step (the full code is in `examples/coin.rs`).\n```rust\n#[derive(Debug, Serialize, Deserialize, From)]\nenum Machine {\n    FirstToss(FirstToss),\n    SecondToss(SecondToss),\n}\n\n#[derive(Debug, Serialize, Deserialize)]\nstruct FirstToss;\nimpl FirstToss {\n    fn next(self) -\u003e StepResult {\n        let first_coin = Coin::toss();\n        println!(\"First coin: {:?}\", first_coin);\n        Ok(Some(SecondToss { first_coin }.into()))\n    }\n}\n\n#[derive(Debug, Serialize, Deserialize)]\nstruct SecondToss {\n    first_coin: Coin,\n}\nimpl SecondToss {\n    fn next(self) -\u003e StepResult {\n        let second_coin = Coin::toss();\n        println!(\"Second coin: {:?}\", second_coin);\n        ensure!(second_coin == self.first_coin, \"Coins landed differently\");\n        println!(\"Coins match\");\n        Ok(None)\n    }\n}\n```\n\nThen we start our machine like this:\n```rust\nlet init_state = FirstToss.into();\nlet mut engine = Engine::\u003cMachine\u003e::new(init_state)?.restore()?;\nengine.drop_error()?;\nengine.run()?;\n```\nWe initialize the `Engine` with the first step. Then we restore the previous state if the\nprocess was interrupted (e.g. by an error). Then we drop a possible error and run all the steps\nto completion.\n\nLet's run it now:\n```sh\n$ cargo run --example coin\nFirst coin: Heads\nSecond coin: Tails\nError: Coins landed differently\n```\n\nWe weren't lucky this time and the program resulted in an error. Let's run it again:\n```sh\n$ cargo run --example coin\nSecond coin: Heads\nCoins match\n```\n\nNotice that, thanks to the `restore()`, our machine run from the step it was interrupted,\nknowing about the first coin landed on heads.\n\n[version-badge]: https://img.shields.io/crates/v/step-machine.svg\n[docs-badge]: https://docs.rs/step-machine/badge.svg\n[license-badge]: https://img.shields.io/crates/l/step-machine.svg\n[crate-url]: https://crates.io/crates/step-machine\n[docs-url]: https://docs.rs/step-machine\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimbolc%2Fstep-machine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimbolc%2Fstep-machine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimbolc%2Fstep-machine/lists"}