8 个版本 (5 个重大更改)
0.7.0 | 2024 年 6 月 1 日 |
---|---|
0.6.2 | 2024 年 5 月 11 日 |
0.6.1 | 2022 年 12 月 24 日 |
0.6.0 | 2021 年 8 月 24 日 |
0.2.0 | 2019 年 5 月 22 日 |
#145 在 Rust 模式
每月 <11,385 次下载
在 3 个 包中使用 (2 个直接使用)
21KB
79 行
为 Rust 构建有限状态机的框架和 DSL
《rust-fsm》包提供了一种简单通用的框架,用于在 Rust 中以最小的努力构建状态机。
此包的核心是 StateMachineImpl
特性。此特性允许开发者提供严格的状态机定义,例如指定其
- 输入字母表 - 状态机作为输入接受的一组实体,并根据它们执行状态转换。
- 可能的状态 - 该机器可能处于的状态集合。
- 输出字母表 - 状态机可能作为其工作结果输出的实体集合。
- 转换函数 - 根据当前状态和提供的输入更改状态机的状态的函数。
- 输出函数 - 根据当前状态和提供的输入从输出字母表中输出内容的函数。
- 机器的初始状态。
请注意,在实现层面上,这种抽象允许构建任何类型的状态机
- 通过仅提供输入字母表、一组状态和转换函数来构建经典状态机。
- 通过提供上述所有实体来构建 Mealy 机器。
- 通过提供一个不依赖于提供的输入的输出函数来构建 Moore 机器。
功能标志
默认
std
- 实现需要std
环境的功能。见下文。dsl
- 从rust-fsm
中重新导出rust-fsm-dsl
。建议保留此功能以获得最佳开发体验。
非默认
diagram
- 在文档字符串中生成Mermaid状态图。见下文。
在no_std
环境中的使用
此库有一个名为std
的功能,默认启用。您可以将此库导入为rust-fsm = { version = "0.7", default-features = false, features = ["dsl"] }
,以便在no_std
环境中使用。这仅影响错误类型(Error
trait仅在std
中可用)。
DSL实现的重导出受名为dsl
的功能控制,该功能默认启用。
使用
最初,此库旨在构建一个易于使用的DSL,用于在其上定义状态机。使用DSL需要连接一个额外的crate rust-fsm-dsl
(这是由于过程宏系统的限制)。
使用DSL定义状态机
DSL由state_machine
宏进行解析。以下是一个小示例。
use rust_fsm::*;
state_machine! {
#[derive(Debug)]
#[repr(C)]
/// A Circuit Breaker state machine.
circuit_breaker(Closed)
Closed(Unsuccessful) => Open [SetupTimer],
Open(TimerTriggered) => HalfOpen,
HalfOpen => {
Successful => Closed,
Unsuccessful => Open [SetupTimer]
}
}
此代码示例
- 定义了一个名为
circuit_breaker
的状态机; - 为它推导了
Debug
trait。您在这里使用的所有属性(如#[repr(C)]
)将应用于此宏生成的所有类型。如果您想将属性或文档字符串应用于此宏生成的mod
,只需将其放在宏调用之前即可。 - 将此状态机的初始状态设置为
Closed
; - 定义状态转换。例如:在
HalfOpen
状态下收到Successful
输入时,机器必须移动到Closed
状态; - 定义输出。例如:在
Closed
状态下收到Unsuccessful
时,机器必须输出SetupTimer
。
可以使用以下方式使用此状态机
// Initialize the state machine. The state is `Closed` now.
let mut machine = circuit_breaker::StateMachine::new();
// Consume the `Successful` input. No state transition is performed.
let _ = machine.consume(&circuit_breaker::Input::Successful);
// Consume the `Unsuccesful` input. The machine is moved to the `Open`
// state. The output is `SetupTimer`.
let output = machine.consume(&circuit_breaker::Input::Unsuccessful).unwrap();
// Check the output
if let Some(circuit_breaker::Output::SetupTimer) = output {
// Set up the timer...
}
// Check the state
if let circuit_breaker::State::Open = machine.state() {
// Do something...
}
以下实体被生成
- 一个空的实现结构
circuit_breaker::Impl
,该结构实现了StateMachineImpl
trait。 - 枚举
circuit_breaker::State
、circuit_breaker::Input
和circuit_breaker::Output
,分别代表状态、输入字母表和输出字母表。 - 类型别名
circuit_breaker::StateMachine
,它展开为StateMachine<circuit_breaker::Impl>
。
请注意,如果在规范中没有输出,输出字母表是一个空枚举。由于许多Rust属性的技術限制,无法将其应用于任何属性(例如 derive
、repr
)。
在 state_machine
宏内部,您必须定义至少一个状态转换。
可见性
您可以通过以下方式指定可见性:
use rust_fsm::*;
state_machine! {
pub CircuitBreaker(Closed)
Closed(Unsuccessful) => Open [SetupTimer],
Open(TimerTriggered) => HalfOpen,
HalfOpen => {
Successful => Closed,
Unsuccessful => Open [SetupTimer],
}
}
默认可见性为私有。
自定义字母表类型
您可以为输入、输出或状态提供自己的类型。所有这些都是可选的:您可以使用其中之一或同时使用所有这些类型。当前的限制是您必须提供一个完全限定的类型路径。
use rust_fsm::*;
pub enum Input {
Successful,
Unsuccessful,
TimerTriggered,
}
pub enum State {
Closed,
HalfOpen,
Open,
}
pub enum Output {
SetupTimer,
}
state_machine! {
#[state_machine(input(crate::Input), state(crate::State), output(crate::Output))]
circuit_breaker(Closed)
Closed(Unsuccessful) => Open [SetupTimer],
Open(TimerTriggered) => HalfOpen,
HalfOpen => {
Successful => Closed,
Unsuccessful => Open [SetupTimer]
}
}
图表
state_machine
宏可以使用图表来注释您的状态机。这由非默认的 diagram
功能控制。这些图表以 Mermaid 格式生成。此功能将Mermaid脚本包含在文档页面中。
要查看实际效果,请下载存储库并运行:
cargo doc -p doc-example --open
无DSL
state_machine
宏的功能有限(例如,状态无法携带任何附加数据),因此在某些复杂情况下,用户可能希望手动编写更复杂的状态机。
构建状态机所需的所有操作就是实现 StateMachineImpl
特性,并使用它与提供的包装器之一(目前只有 StateMachine
)一起使用。
您可以在 项目存储库 中看到一个电路断路器状态机的示例。
依赖关系
~0–480KB
~11K SLoC