物品可以展示,可以下订单购买,提供购买详
细信息以验证订单,最后,物品被运送。发货前可以取消订单。下面示意性地描述了该状态机:
完整的源代码可以在这里找到
状态机接受的消息:
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秒内完成,终端状态为已发货。
这个例子完成了所提出的功能必须被使用的基本场景。