#state-machine #state-transition #traits #macro #finite #events

finite-state-machine

基于类型和特质的有限状态机宏

3个不稳定版本

0.2.0 2023年4月13日
0.1.1 2023年4月12日
0.1.0 2023年4月12日

算法中排名第2373

MIT授权

9KB
90

有限状态机

使用宏在Rust中创建有限状态机。宏生成一个结构体、事件枚举和特质以及一个决策特质。决策特质用于决定转换到哪个状态。每个状态都有一个函数,当进入该状态时自动调用。自动添加无效状态和结束状态。您还需要为每个转换实现一个函数。宏自动生成您需要实现的所有内容的特质,以便在编译时检查其有效性。

用法

use finite_state_machine::state_machine;
// Debug is only needed if the verbose feature is enabled
#[derive(Debug, Default)]
struct Data {
    ...
}
state_machine!(
    // The name of the state machine and the type of the data, you can also use livetimes here
    CircuitBreaker(Data);
    // the first state will automatically become the start state, no matter the name
    Closed {
        Ok => Closed, // on Ok event go to Closed state
        AmperageTooHigh => Open // on AmperageTooHigh event go to open state
    },
    Open {
        AttemptReset => HalfOpen,
        Wait => Open
    },
    HalfOpen {
        Success => Closed,
        AmperageTooHigh => Open,
        MaxAttemps => End
    }
);

use circuit_breaker::*;

// now you need to implement the decider trait which emits events which decide which state to transition to
impl Deciders for CircuitBreaker {
    fn closed(&self) -> circuit_breaker::ClosedEvents {
        if self.data.current_amperage > self.data.max_amperage {
            circuit_breaker::ClosedEvents::AmperageTooHigh
        } else {
            circuit_breaker::ClosedEvents::Ok
        }
    }
    ...
}

// now we need to implement the transition trait for each state
impl ClosedTransitions for CircuitBreaker {
    fn amperage_too_high(&mut self) -> Result<(), &'static str> {
        self.data.tripped_at = Some(SystemTime::now());
        Ok(())
    }
    fn ok(&mut self) -> Result<(), &'static str> {
        self.data.current_amperage += 1;
        std::thread::sleep(Duration::from_millis(500));
        Ok(())
    }
    fn illegal(&mut self) {}
}

有关更多详细信息,请查看示例文件夹。

工作原理

  • 宏将创建一个模块,其名称为状态机的名称,并转换为蛇形大小写。在示例中: CircuitBreaker -> circuit_breaker
  • 宏将生成一个决策特质。这个特质为每个状态都有一个函数。您需要为您的结构体实现这个特质。在我们的例子中 impl Deciders for CircuitBreaker。决策函数只接受一个不可变引用 &self,并必须返回一个枚举,其中包含每个转换的变体。这个枚举由宏生成。在我们的例子中 circuit_breaker::ClosedEvents等。还有一个 Illegal 变体,用于遇到结构体的非法/不可能状态时。这将转换到 Invalid 状态。在孤立的状态机中,这不应该发生。但是如果您在更大的系统中使用状态机,这可能会发生。
  • 为每个您创建的状态,将生成一个 <StateName>Transitions 特性,您需要为您的结构体实现它。在我们的例子中 impl ClosedTransitions for CircuitBreaker。这些函数会得到对 self 的可变引用 &mut self 并必须返回一个 Result<(), &'static str>&'static str 用于返回错误信息。如果您返回 Err,状态机将过渡到自动添加的无效状态。当进入此状态时,机器停止并返回错误信息。

调试

您可以通过启用特性 verbose 来获取更多关于状态机的信息。这将打印当前状态、转换和当前数据。

依赖关系