1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use super::messages::{MessageToClient, MessageToServer, StateVersionNumber};
use crate::StateMachine;

#[derive(Default)]
pub struct StateServer<S: StateMachine> {
    pub version: StateVersionNumber,
    state: S,
}

pub struct StateServerMessageResponse<S: StateMachine> {
    pub reply_message: MessageToClient<S>,
    pub broadcast_message: Option<MessageToClient<S>>,
}

impl<S: StateMachine> StateServer<S> {
    pub fn new(state: S) -> Self {
        StateServer {
            version: StateVersionNumber::default(),
            state,
        }
    }

    pub fn state(&self) -> &S {
        &self.state
    }

    pub fn receive_message(
        &mut self,
        message: MessageToServer<S>,
    ) -> StateServerMessageResponse<S> {
        match message {
            MessageToServer::DoTransition {
                transition_number,
                transition,
            } => match self.state.apply(&transition) {
                Ok(state) => {
                    self.state = state;
                    self.version.0 += 1;

                    StateServerMessageResponse {
                        reply_message: MessageToClient::ConfirmTransition {
                            transition_number,
                            version: self.version,
                        },
                        broadcast_message: Some(MessageToClient::PeerTransition {
                            transition,
                            version: self.version,
                        }),
                    }
                }
                Err(_) => todo!(),
            },
            MessageToServer::RequestState => StateServerMessageResponse {
                reply_message: MessageToClient::SetState {
                    state: self.state.clone(),
                    version: self.version,
                },
                broadcast_message: None,
            },
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{data_structures::Counter, sync::messages::ClientTransitionNumber};

    #[test]
    pub fn basic_messages() {
        let counter = Counter::new(110);
        let mut server: StateServer<Counter> = StateServer::new(counter);

        let result = server.receive_message(MessageToServer::RequestState);

        if let StateServerMessageResponse {
            reply_message: MessageToClient::SetState { state, version },
            broadcast_message: None,
        } = result
        {
            assert_eq!(0, version.0);
            assert_eq!(110, state.value());
        } else {
            panic!("Response did not match pattern.");
        }

        let result = server.receive_message(MessageToServer::DoTransition {
            transition_number: ClientTransitionNumber(1),
            transition: Counter::increment(3),
        });

        if let StateServerMessageResponse {
            reply_message:
                MessageToClient::ConfirmTransition {
                    transition_number: ClientTransitionNumber(1),
                    version: StateVersionNumber(1),
                },
            broadcast_message:
                Some(MessageToClient::PeerTransition {
                    transition,
                    version: StateVersionNumber(1),
                }),
        } = result
        {
            assert_eq!(Counter::increment(3), transition);
        } else {
            panic!("Response did not match pattern.");
        }

        assert_eq!(113, server.state.value());
        assert_eq!(1, server.version.0);
    }

    // TODO: test conflict case.
}