3 个版本
0.1.2 | 2023 年 10 月 8 日 |
---|---|
0.1.1 | 2023 年 10 月 8 日 |
0.1.0 | 2023 年 10 月 8 日 |
#212 in 过程宏
61KB
716 行
fsmentry
一个有限状态机 (FSM) 代码生成器,具有以下特性
- 将您的机器定义为一个图,例如
DOT
。 - 一个
entry
API 用于转换状态机。 - 非法状态和转换不可表示。
- 状态可以包含数据。
- 支持自定义
#[derive(..)]
。 #![no_std]
支持。- 文档中的状态机内联 SVG 图表。
// define the machine.
// you can also use the DOT language if you prefer.
fsmentry::dsl! {
/// This is a state machine for a traffic light
// Documentation on nodes and states will appear in the generated code
pub TrafficLight {
Red; // this is a state
Green: String; // this state has data inside it.
/// Cars speed up
// this documentation is shared among all the edges
Red -> RedAmber -> Green;
// ^ states are implicitly created
/// Cars slow down
Green -> Amber -"make sure you stop!"-> Red;
// ^ this documentation is for this edge only
}
}
use traffic_light::{TrafficLight, Entry};
// instantiate the machine
let mut machine = TrafficLight::new(traffic_light::State::Red);
loop {
match machine.entry() {
Entry::Red(it) => it.red_amber(), // transition the state machine
// when you transition to a state with data,
// you must provide the data
Entry::RedAmber(it) => it.green(String::from("this is some data")),
Entry::Green(mut it) => {
// you can inspect or mutate the data in a state...
let data: &String = it.get();
let data: &mut String = it.get_mut();
// ...and you get it back when you transition out of a state
let data: String = it.amber();
},
Entry::Amber(it) => break,
}
}
Cargo 功能
macros
(默认): 包含 [dot
] 和 [dsl
] 宏。svg
(默认): 如果可用,宏将调用dot
并为文档生成状态机图表。std
(默认): 包含FSMGenerator
,用于自定义代码生成工具。cli
: 这不会影响库,但如果您
您将获得一个cargo install fsmentry --features=cli
fsmentry
二进制文件,您可以使用它来生成代码。
高级使用
fsmentry::dsl! {
#[derive(Clone, Debug, derive_quickcheck_arbitrary::Arbitrary)] // attach `#[derive(..)]`s here
pub MyStateMachine { .. }
}
use my_state_machine::{MyStateMachine, State, Entry};
// ^ A module with matching publicity is generated for the state machine.
// The `#[derive(..)]`s apply to the `State` and the `MyStateMachine` items.
let mut machine = MyStateMachine::arbitrary(g); // we can use derived traits!
// you can also inspect and mutate the state yourself.
let state: &State = machine.state();
let state: &mut State = machine.state_mut();
match machine.entry() {
// states with no transitions and no data are empty entries
Entry::DeadEnd => {},
// states with no transitions give you the data
Entry::DeadEndWithData(data) => {
let _: &mut String = data;
},
Entry::WithTransitions(handle) => {
// otherwise, you get a struct which allows you to transition the machine.
// (It will have getters for data as appropriate).
handle.dead_end();
}
// ...
}
分层状态机
fsmentry
对子状态机无需特殊考虑 - 只需在相关节点上存储一个!以下是来自 statig
crate 的示例
┌─────────────────────────┐
│ Blinking │🞀─────────┐
│ ┌───────────────┐ │ │
│ ┌─🞂│ LedOn │──┐ │ ┌───────────────┐
│ │ └───────────────┘ │ │ │ NotBlinking │
│ │ ┌───────────────┐ │ │ └───────────────┘
│ └──│ LedOff │🞀─┘ │ 🞁
│ └───────────────┘ │──────────┘
└─────────────────────────┘
fsmentry::dsl! { // the outer state machine
pub Webcam {
NotBlinking -> Blinking -> NotBlinking;
Blinking: super::led::Led; // The `Blinking` state contains a state machine
}
}
fsmentry::dsl! { // the inner state machine
pub Led {
LedOn -> LedOff -> LedOn;
}
}
let mut machine = webcam::Webcam::new(webcam::State::NotBlinking);
loop {
match machine.entry() { // transition the outer machine
webcam::Entry::Blinking(mut webcam) => match webcam.get_mut().entry() { // transition the inner machine
led::Entry::LedOff(it) => it.led_on(),
led::Entry::LedOn(it) => {
it.led_off();
webcam.not_blinking();
}
},
webcam::Entry::NotBlinking(webcam) => {
webcam.blinking(led::Led::new(led::State::LedOff))
}
}
}
与其他状态机库的比较
crate | 非法状态/转换不可表示 | 状态包含数据 | 状态机定义 | 注释 |
---|---|---|---|---|
fsmentry |
是 | 是 | 图 | |
sm |
是 | 否 | 状态、事件、转换 | |
rust-fsm |
否 | 是(手动) | 状态、事件、转换 | |
finny |
否 | 是 | 构建器 | |
sfsm |
否 | 否 | 状态和转换 | |
statig |
? | ? | ? | 复杂的API! |
sad_machine |
是 | 否 | 状态、事件、转换 | |
机器 |
否 | 是 | 状态、事件、转换 |
依赖项
~0–9.5MB
~48K SLoC