1use crate::error::RoutingError;
6use std::fmt::Debug;
7
8pub trait WalkStateUnit<Rhs = Self> {
10 type Error: Into<RoutingError>;
11
12 fn validate_next(&self, next_state: &Rhs) -> Result<(), Self::Error>;
15
16 fn finalize_error(&self) -> Self::Error;
19}
20
21#[derive(Debug, Clone)]
25pub enum WalkState<T: WalkStateUnit + Debug + Clone> {
26 Begin,
27 Intermediate(T),
28 Finished(T),
29}
30
31impl<T: WalkStateUnit + Debug + Clone> WalkState<T> {
32 pub fn new() -> Self {
34 WalkState::Begin
35 }
36
37 pub fn at(state: T) -> Self {
41 WalkState::Intermediate(state)
42 }
43
44 pub fn advance(&self, next_state: Option<T>) -> Result<Self, RoutingError> {
46 match (&self, &next_state) {
47 (WalkState::Finished(_), _) => {
48 panic!("Attempting to advance a finished WalkState");
49 }
50 (WalkState::Begin, Some(proposed_state)) => {
51 Ok(WalkState::Intermediate(proposed_state.clone()))
52 }
53 (WalkState::Intermediate(last_seen_state), Some(proposed_state)) => {
54 last_seen_state.validate_next(proposed_state).map_err(|e| e.into())?;
55 Ok(WalkState::Intermediate(proposed_state.clone()))
56 }
57 (_, None) => Ok(self.clone()),
58 }
59 }
60
61 pub fn is_finished(&self) -> bool {
63 match self {
64 WalkState::Finished(_) => true,
65 _ => false,
66 }
67 }
68
69 pub fn finalize(&self, state: Option<T>) -> Result<Self, RoutingError> {
73 if self.is_finished() {
74 panic!("Attempted to finalized a finished walk state.");
75 }
76 if state.is_none() {
77 match self {
78 WalkState::Begin => panic!("finalizing a walk state that's only just begun"),
79 WalkState::Intermediate(last_seen_state) => {
80 return Err(last_seen_state.finalize_error().into());
81 }
82 WalkState::Finished(_) => {
83 unreachable!("Captured earlier");
84 }
85 }
86 }
87 let final_state = state.unwrap();
88 match self {
89 WalkState::Begin => Ok(WalkState::Finished(final_state)),
90 WalkState::Intermediate(last_seen_state) => {
91 last_seen_state.validate_next(&final_state).map_err(|e| e.into())?;
92 Ok(WalkState::Finished(final_state))
93 }
94 WalkState::Finished(_) => {
95 unreachable!("Captured earlier");
96 }
97 }
98 }
99}