| Crates.io | mindset |
| lib.rs | mindset |
| version | 0.1.1 |
| created_at | 2025-12-03 08:41:50.766428+00 |
| updated_at | 2025-12-15 04:49:17.455407+00 |
| description | A pure functional state machine library built on Stillwater's Effect system |
| homepage | |
| repository | https://github.com/iepathos/mindset |
| max_upload_size | |
| id | 1963598 |
| size | 1,032,722 |
A zero-cost, effect-based state machine library for Rust.
Mindset provides a flexible and type-safe state machine implementation that separates pure guard logic from effectful actions. Built on Stillwater's effect system, it enables you to write state machines that are:
use mindset::{StateMachine, State};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum ConnectionState {
Disconnected,
Connecting,
Connected,
}
impl State for ConnectionState {}
fn main() {
use mindset::builder::{StateMachineBuilder, TransitionBuilder};
let machine = StateMachineBuilder::new()
.initial(ConnectionState::Disconnected)
.add_transition(
TransitionBuilder::new()
.from(ConnectionState::Disconnected)
.to(ConnectionState::Connecting)
.succeeds()
.build()
.unwrap()
)
.add_transition(
TransitionBuilder::new()
.from(ConnectionState::Connecting)
.to(ConnectionState::Connected)
.succeeds()
.build()
.unwrap()
)
.build()
.unwrap();
// Zero-cost transitions - compiles to direct state updates
}
use mindset::{StateMachine, State};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum OrderState {
Draft,
Submitted,
Processing,
Completed,
}
impl State for OrderState {}
struct Order {
id: u64,
total: f64,
}
// Define environment capabilities as traits
trait PaymentProcessor {
fn charge(&mut self, amount: f64) -> Result<(), String>;
}
trait Logger {
fn log(&mut self, message: &str);
}
// Pure guard - no side effects
fn can_submit(order: &Order) -> bool {
order.total > 0.0
}
// Effectful action - explicit environment usage
fn submit_order<Env>(order: &mut Order, env: &mut Env) -> Result<(), String>
where
Env: PaymentProcessor + Logger,
{
env.log(&format!("Submitting order {}", order.id));
env.charge(order.total)?;
Ok(())
}
fn main() {
use mindset::builder::{StateMachineBuilder, TransitionBuilder};
let machine = StateMachineBuilder::new()
.initial(OrderState::Draft)
.add_transition(
TransitionBuilder::new()
.from(OrderState::Draft)
.to(OrderState::Submitted)
.succeeds()
.build()
.unwrap()
)
.add_transition(
TransitionBuilder::new()
.from(OrderState::Submitted)
.to(OrderState::Processing)
.succeeds()
.build()
.unwrap()
)
.build()
.unwrap();
// For custom effectful actions, see the Builder Guide
}
Pure Guards, Effectful Actions
Zero-Cost by Default
Environment Pattern
Explicit Over Implicit
A state machine in Mindset consists of:
Transitions come in two flavors:
machine.transition(State::A, State::B, |state| {
// Pure guard logic
state.is_valid()
});
Compiles to direct state updates with no runtime overhead.
machine.transition_with_effect(State::A, State::B, |state, env| {
// Guard check
if !state.is_valid() {
return Err("Invalid state");
}
// Effects
env.log("Transitioning");
env.save_to_db(state)?;
// State update
state.version += 1;
Ok(())
});
Effects are explicit via environment parameter. Only pay for what you use.
Run any example with cargo run --example <name>:
| Example | Demonstrates |
|---|---|
| basic_state_machine | Zero-cost state machine with pure transitions |
| effectful_state_machine | Environment pattern and effectful actions |
| testing_patterns | Testing with mock environments |
| traffic_light | Simple cyclic state machine |
| document_workflow | Multi-stage approval workflow |
| order_processing | E-commerce order lifecycle |
| account_management | Account states with validation |
| checkpoint_resume | Checkpoint and resume patterns |
| mapreduce_workflow | MapReduce workflow implementation |
| resource_management | Resource lifecycle management |
See examples/ directory for full code and examples/README.md for detailed explanations.
use mindset::state_enum;
use mindset::builder::{StateMachineBuilder, simple_transition};
state_enum! {
enum TrafficLight {
Red,
Yellow,
Green,
}
}
let machine = StateMachineBuilder::new()
.initial(TrafficLight::Red)
.transitions(vec![
simple_transition(TrafficLight::Red, TrafficLight::Green),
simple_transition(TrafficLight::Green, TrafficLight::Yellow),
simple_transition(TrafficLight::Yellow, TrafficLight::Red),
])
.build()
.unwrap();
use mindset::state_enum;
use mindset::builder::{StateMachineBuilder, TransitionBuilder};
state_enum! {
enum DocState {
Draft,
Review,
Approved,
Published,
}
final: [Published]
}
trait AuditLog {
fn log_transition(&mut self, from: DocState, to: DocState);
}
let machine = StateMachineBuilder::new()
.initial(DocState::Draft)
.add_transition(
TransitionBuilder::new()
.from(DocState::Draft)
.to(DocState::Review)
.succeeds()
.build()
.unwrap()
)
.add_transition(
TransitionBuilder::new()
.from(DocState::Review)
.to(DocState::Approved)
.succeeds()
.build()
.unwrap()
)
.build()
.unwrap();
struct Account {
balance: f64,
status: AccountStatus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum AccountStatus {
Active,
Suspended,
Closed,
}
impl State for AccountStatus {}
trait AccountRepository {
fn persist(&mut self, account: &Account) -> Result<(), String>;
}
fn can_close(account: &Account) -> bool {
account.balance == 0.0
}
fn close_account<Env>(account: &mut Account, env: &mut Env) -> Result<(), String>
where
Env: AccountRepository,
{
if !can_close(account) {
return Err("Cannot close account with non-zero balance".to_string());
}
account.status = AccountStatus::Closed;
env.persist(account)?;
Ok(())
}
The environment pattern makes testing straightforward:
#[cfg(test)]
mod tests {
use super::*;
struct MockEnv {
logged: Vec<String>,
saved: Vec<String>,
}
impl Logger for MockEnv {
fn log(&mut self, msg: &str) {
self.logged.push(msg.to_string());
}
}
impl Database for MockEnv {
fn save(&mut self, data: &str) -> Result<(), String> {
self.saved.push(data.to_string());
Ok(())
}
}
#[test]
fn test_effectful_transition() {
let mut state = State::Initial;
let mut env = MockEnv {
logged: vec![],
saved: vec![],
};
transition(&mut state, &mut env).unwrap();
assert_eq!(env.logged.len(), 1);
assert_eq!(env.saved.len(), 1);
}
}
Pure transitions have zero runtime overhead. This code:
machine.transition(State::A, State::B);
Compiles to the same assembly as:
state = State::B;
Effects only cost what you use:
On a typical modern CPU:
Mindset includes built-in checkpoint and resume functionality for long-running MapReduce workflows. This allows workflows to be paused and resumed without losing progress, making the system resilient to interruptions.
Long-running workflows benefit from checkpointing when:
# Start a workflow
./workflow run --config workflow.yaml
# Workflow saves checkpoints automatically...
# Interrupt with Ctrl+C or system failure
# Resume from checkpoint
./workflow resume --checkpoint ./checkpoints/latest.json
# Workflow continues from where it left off
For detailed documentation on checkpoint structure, resume behavior, best practices, and atomic write patterns, see the Checkpointing Guide.
This library implements the effect-based state machine foundation as specified in:
Contributions are welcome! Please ensure:
[License information to be added]