Skip to content

Instantly share code, notes, and snippets.

@kayvank
Created April 18, 2025 18:01
Show Gist options
  • Select an option

  • Save kayvank/aad6fcfc78b741276cfb76cb2b907362 to your computer and use it in GitHub Desktop.

Select an option

Save kayvank/aad6fcfc78b741276cfb76cb2b907362 to your computer and use it in GitHub Desktop.
Type driven State Machine in Rust, using nix script
#!/usr/bin/env nix-shell
//! ```cargo
//! [dependencies]
//! time = "0.1.25"
//! ```
/*
#!nix-shell -i rust-script -p rustc -p rust-script -p cargo
*/
#[derive(Debug, Clone, Copy)]
struct BottleFillingMachineConfig {
waiting_time: std::time::Duration,
filling_rate: usize,
}
#[derive(Debug)]
struct BottleFillingMachine<S> {
config: BottleFillingMachineConfig,
state: S,
}
// some states
#[derive(Debug)]
struct Waiting {
waiting_time: std::time::Duration,
}
#[derive(Debug)]
struct Filling {
filling_rate: usize,
is_full: bool,
}
#[derive(Debug)]
struct Done;
#[derive(Debug)]
struct BottleIsNotFull;
#[derive(Debug)]
struct BottleIsFull;
// we leave these here, in case we need to carry state when we're done
enum BottleInMachineStatus {
NotFull(BottleIsNotFull),
Full(BottleIsFull),
}
// the machine starts @waiting states
impl BottleFillingMachine<Waiting> {
fn new(config: BottleFillingMachineConfig) -> Self {
let waiting_time: std::time::Duration = config.waiting_time;
BottleFillingMachine {
config,
state: Waiting { waiting_time },
}
}
}
impl BottleFillingMachine<Filling> {
// TODO make this async and alternate states
fn bottle_status_dettector(&self) -> BottleInMachineStatus {
if self.config.filling_rate > 0 {
BottleInMachineStatus::Full(BottleIsFull)
} else {
BottleInMachineStatus::NotFull(BottleIsNotFull)
}
}
}
impl From<BottleFillingMachine<Done>> for BottleFillingMachine<Waiting> {
fn from(machine: BottleFillingMachine<Done>) -> Self {
BottleFillingMachine {
config: machine.config,
state: Waiting {waiting_time: machine.config.waiting_time},
}
}
}
impl From<BottleFillingMachine<Waiting>> for BottleFillingMachine<Filling> {
fn from(machine: BottleFillingMachine<Waiting>) -> Self {
BottleFillingMachine {
config: machine.config,
state: Filling {
filling_rate: machine.config.filling_rate,
is_full: false,
},
}
}
}
impl TryFrom<BottleFillingMachine<Filling>> for BottleFillingMachine<Done> {
type Error = BottleIsNotFull;
fn try_from(machine: BottleFillingMachine<Filling>) -> Result<Self, Self::Error> {
match machine.bottle_status_dettector() {
BottleInMachineStatus::Full(BottleIsFull) => Ok(BottleFillingMachine {
config: machine.config,
state: Done,
}),
BottleInMachineStatus::NotFull(BottleIsNotFull) => Err(BottleIsNotFull),
}
}
}
// transitions between states
fn main() {
let config = BottleFillingMachineConfig {
waiting_time: std::time::Duration::new(10, 10),
filling_rate: 10usize,
};
let machine: BottleFillingMachine<Waiting> = BottleFillingMachine::new(config);
println!("machine initial state : {:?}", machine);
let filling_machine: BottleFillingMachine<Filling> = machine.into();
println!("machine filling state : {:?}", filling_machine);
let done_machine: Result<BottleFillingMachine<Done>, BottleIsNotFull> =
filling_machine.try_into();
let back_waiting_machine: Option<BottleFillingMachine<Waiting>> =
match done_machine {
Ok(d) => {
println!("machine done state : {:?}", d);
let machine: BottleFillingMachine<Waiting> = d.into();
Some(machine)
},
e => {
println!("machine not done state : {:?}", e);
None
},
};
println!("optional reset state: {:?}", back_waiting_machine);
println!("hello rust script");
}
@kayvank
Copy link
Author

kayvank commented Apr 18, 2025

Prerequisit

  • nixos

How to use it

  • Create the file, factory_bottle.rs and copy/paste the code in it
chmod 755 ./factory_bottle.rs
./factory_bottle.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment