Crates.io | stateflow |
lib.rs | stateflow |
version | 0.4.0 |
source | src |
created_at | 2024-09-30 04:18:27.10449 |
updated_at | 2024-10-23 21:49:24.834632 |
description | A simple and extensible state machine implementation in Rust |
homepage | |
repository | https://github.com/Lifestreams-ai/statemachine |
max_upload_size | |
id | 1391316 |
size | 80,384 |
A simple and extensible state machine library in Rust.
Arc
and RwLock
for safe concurrent use.Display
trait.This project provides a simple and extensible state machine library in Rust. It allows for defining states, transitions, actions, and validations triggered during state changes. The state machine configuration can be loaded from a JSON file, and custom actions can be executed on state transitions using a user-defined action handler. The state machine also supports custom context objects, allowing you to maintain additional state or data throughout the state machine's lifecycle.
Add Dependency
Add the following to your Cargo.toml
:
[dependencies]
stateflow = "0.4.0"
Update Crates
Run:
cargo update
Here's how to integrate the state machine into your project.
Create a config.json
file:
{
"states": [
{
"name": "Idle",
"on_enter_actions": [],
"on_exit_actions": [],
"validations": []
},
{
"name": "Processing",
"on_enter_actions": [],
"on_exit_actions": [],
"validations": []
},
{
"name": "Finished",
"on_enter_actions": [],
"on_exit_actions": [],
"validations": []
}
],
"transitions": [
{
"from": "Idle",
"event": "start",
"to": "Processing",
"actions": [],
"validations": []
},
{
"from": "Processing",
"event": "finish",
"to": "Finished",
"actions": [],
"validations": []
}
]
}
Create an asynchronous function to handle actions, with access to both the memory and your custom context:
use stateflow::{Action, StateMachine};
use serde_json::{Map, Value};
struct MyContext {
// Your custom context fields
counter: i32,
}
async fn action_handler(
action: &Action,
memory: &mut Map<String, Value>,
context: &mut MyContext,
) {
match action.action_type.as_str() {
"log" => println!("Logging: {}", action.command),
"increment_counter" => {
context.counter += 1;
println!("Counter incremented to {}", context.counter);
}
_ => eprintln!("Unknown action: {}", action.command),
}
}
use stateflow::StateMachine;
use serde_json::{Map, Value};
use std::fs;
#[tokio::main]
async fn main() -> Result<(), String> {
let config_content = fs::read_to_string("config.json")
.map_err(|e| format!("Failed to read config file: {}", e))?;
// Initialize memory (can be empty or pre-populated)
let memory = Map::new();
// Initialize your custom context
let context = MyContext { counter: 0 };
let state_machine = StateMachine::new(
&config_content,
Some("Idle".to_string()),
|action, memory, context| Box::pin(action_handler(action, memory, context)),
memory,
context,
)?;
// Trigger events
state_machine.trigger("start").await?;
state_machine.trigger("finish").await?;
// Access the context after transitions
{
let context = state_machine.context.read().await;
println!("Final counter value: {}", context.counter);
}
Ok(())
}
Compile and run your application:
cargo run
To configure the cache size, set the STATEFLOW_LRU_CACHE_SIZE
environment variable before running your application.
export STATEFLOW_LRU_CACHE_SIZE=200
If not set, the cache size defaults to 100
.
The state machine is highly configurable via a JSON file.
name
, on_enter_actions
, on_exit_actions
, and validations
.from
state, event
triggering the transition, to
state, any actions
, and validations
.action_type
and a command
, which the action handler interprets.Example of a state with validations:
{
"name": "Processing",
"on_enter_actions": [],
"on_exit_actions": [],
"validations": [
{
"field": "age",
"rules": [
{ "type": "type_check", "expected_type": "number" },
{ "type": "min_value", "value": 18 }
]
}
]
}
Example of a transition with an action:
{
"from": "Idle",
"event": "start",
"to": "Processing",
"actions": [
{
"action_type": "log",
"command": "Transitioning to Processing state"
}
],
"validations": []
}
We welcome contributions!
Fork the Repository
Create a Feature Branch
git checkout -b feature/YourFeature
Commit Your Changes
Push to Your Branch
git push origin feature/YourFeature
Open a Pull Request
For detailed guidelines, see CONTRIBUTING.md.
This project is licensed under the MIT License. See the LICENSE file for details.
If you have any questions or issues, please open an issue on GitHub.
See CHANGELOG.md for version history.
Q: Can I use this library in a multi-threaded environment?
A: Yes, the state machine is thread-safe using Arc
and RwLock
.
Q: How does the configuration caching work?
A: The state machine caches parsed configurations using an LRU cache. It uses a hash of the JSON configuration string to detect changes and invalidate cache entries, improving performance by avoiding redundant parsing and validation.
Q: How do I handle custom action types?
A: Implement your logic within the action_handler
function based on the action_type
.
Q: Can I pass my own context to the state machine?
A: Yes, you can pass a custom context of any type to the state machine, which is accessible in the action handler.
We expect all contributors to adhere to our Code of Conduct.
This README was updated to provide a comprehensive overview of the Stateflow library in Rust. We hope it helps you get started quickly!