1

Rust状态机实现源码

 1 year ago
source link: https://www.jdon.com/63020
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Rust状态机实现源码

22-10-25 banq


物品可以展示,可以下订单购买,提供购买详
细信息以验证订单,最后,物品被运送。发货前可以取消订单。下面示意性地描述了该状态机:

完整的源代码可以在这里找到

状态机接受的消息:

enum Message {
  PlaceOrder,
  Verify(Verify),
  FinalConfirmation,
  Cancel,
}
enum Verify {
 Address,
 PaymentDetails,
}

接下来,描述状态;为了示范性的目的,我选择了Placed状态,因为它需要一些输入才能被输入。

struct Placed {
  is_address_present: bool,
  is_payment_ok: bool,
  is_canceled: bool,
}
impl Placed {
  fn new() -> Self {
    Self {
      is_address_present: false,
      is_payment_ok: false,
      is_canceled: false,
    }
  }
}
impl State<Types> for Placed {
  fn deliver(
    &mut self,
    message: Message,
  ) -> DeliveryStatus<Message, <Types as StateTypes>::Err> {
    match message {
      Message::Cancel => self.is_canceled = true,
      Message::Verify(Verify::Address) => self.is_address_present = true,
      Message::Verify(Verify::PaymentDetails) => self.is_payment_ok = true,
      _ => return DeliveryStatus::Unexpected(message),
    }
    DeliveryStatus::Delivered
  }
  fn advance(&self) -> Result<Transition<Types>, <Types as StateTypes>::Err> {
    if self.is_canceled {
      return Ok(Transition::Next(Box::new(Canceled)));
    }
    Ok(if self.is_payment_ok && self.is_address_present {
        Transition::Next(Box::new(Verified::new()))
      } else {
        Transition::Same
      }
    )
  }
}

现在很清楚,deliver更新了相应的字段,而advance则根据其内部值动态地选择下一个状态。
所有其他的状态都有类似的描述,可以在测试代码中查找。

为了把所有的东西连接在一起,我们需要创建一个有时间限制的运行器,把它启动,然后给它一些消息,使状态机在各个状态中进展。观察一下。

// Initial state.
let on_display = OnDisplay::new();
// Fake a feed of un-ordered messages.
let mut feed = VecDeque::from(vec![
  Message::Verify(Verify::Address),
  Message::PlaceOrder,
  Message::FinalConfirmation,
  Message::Verify(Verify::PaymentDetails),
]);
let mut feeding_interval = time::interval(Duration::from_millis(100));
feeding_interval.tick().await;
let mut state_machine_runner = TimeBoundStateMachineRunner::new(
  “Order”.to_string(),
  Box::new(on_display),
  Duration::from_secs(5), // time budget
);
let (state_machine_tx, mut state_machine_rx) =    
  mpsc::unbounded_channel();                                     state_machine_runner.run(state_machine_tx);
                                                                   let res: TimeBoundStateMachineResult<Types> = loop {                                          
  select! {                                              
    Some(Either::Result{ result, ..} ) = 
      state_machine_rx.recv() => { break result; }                                               
    _ = feeding_interval.tick() => {                                                 
      // feed a message if present.                                                 
      if let Some(msg) = feed.pop_front() {                                                     
        let _ = state_machine_runner.deliver(msg);                                                      
      }
    }
  }
};
let order = res.unwrap_or_else(
  |_| panic!(“State machine did not complete in time”)
);
assert!(order.is::<Shipped>());

代码是不言自明的:我们从正在显示的项目开始,鉴于消息的顺序,我们希望有时间限制的状态机运行器在5秒内完成,终端状态为已发货。

这个例子完成了所提出的功能必须被使用的基本场景。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK