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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
use std::fmt::Debug;

/// The error type for parsing a header which was transferred via TCP.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ParseHeaderError {
    /// Parsing of the command failed. This typically indicates that the protocol implementation is incomplete.
    CommandParseFailed,
    /// Parsing of the length failed, possibly because the length is too large (>=2^32)
    LengthParseFailed,
}
/// This trait represents the TCP-Protocol to be used.
///
/// Messages are assumed to be given as u8-slice, consisting of a header and a payload.
///
/// The header combines a command (like Start, Stop, Pause, ...) and the lenght of the payload.
///
/// Many of the implementations show as examples should work for all cases, I'm just unable to define them generically (possible due to missing integer generics).
///
/// Since the protocol trait is only used to bundle some functions & types together, a trivial enum is ok:
/// # Example
/// ```
/// enum ProtocolExample {}
/// ```

pub trait Protocol: 'static {
    /// This type models the possible commands, like Start, Stop, Pause. It typical is represented by an enum.
    /// # Example
    /// ```
    /// enum ExampleCommands {Start, Stop, Pause}
    /// ```
    type Commands: Clone + Copy + Debug + PartialEq + Send + Sync + 'static;
    /// This type models possible busy_states like Idle, Working, Failure.
    /// # Example
    /// ```
    /// enum ExampleBusyStates {Idle, Working, Failure}
    /// ```
    type BusyStates: Clone + Copy + Debug + PartialEq + Send + 'static;
    /// This type represents the commands' underlying u8-array. (Currently, Rust supports no integer generics.)
    /// # Example
    /// ```
    /// type CommandAsArray = [u8;3];
    /// ```
    type CommandAsArray: Debug;
    /// This type represents the payload-length' underlying u8-array. (Currently, Rust supports no integer generics.)
    /// # Example
    /// ```
    /// type LengthAsArray = [u8;2];
    /// ```
    type LengthAsArray: Debug;
    /// This type represents the header' underlying u8-array.
    /// The array size is usually the sum of the command-array size & the length-array size.
    /// (Currently, Rust supports no integer generics.)
    /// # Example
    /// ```
    /// type HeaderAsArray = [u8;5];
    /// ```
    type HeaderAsArray: Debug;
    /// This function returns a default BusyState "Idle".
    /// # Example
    /// ```
    /// fn idle() -> Self::BusyStates {ExampleBusyStates::Idle}
    /// ```
    fn idle() -> Self::BusyStates;
    /// This function checks if a message has to be answered immediately and not be forwarded to the user.
    /// If the message is to be answered immediately, a command and a message must be constructed.
    /// If the message should be forwarded to the user, answer None.
    /// A possible application is for "heartbeat" checks while the user is doing a computation.
    /// # Example
    /// ```
    /// fn message_is_answered_via_immediate_route(
    ///      command: &Self::Commands,
    ///      message: &[u8],
    ///      busy_state: &Self::BusyStates,
    ///  ) -> Option<(Self::Commands, Vec<u8>)> {
    ///     None
    /// }
    /// ```
    fn message_is_answered_via_immediate_route(
        command: &Self::Commands,
        message: &[u8],
        busy_state: &Self::BusyStates,
    ) -> Option<(Self::Commands, Vec<u8>)>;
    /// This function parses a command-array into a command (enum-variant). If this fails, None is return.
    /// # Example
    /// ```
    /// fn parse_command(command: &Self::CommandAsArray) -> Option<Self::Commands> {
    ///     use self::ExampleCommands::*;
    ///     match command {
    ///         [0,0,0]=>Start,
    ///         [1,2,3]=>Stop,
    ///         [255,255,255]=>Failure,
    ///         _ => None,
    ///     }
    /// }
    /// ```
    fn parse_command(command: &Self::CommandAsArray) -> Option<Self::Commands>;
    /// This function parses a length-array into a payload-length. If this fails, None is return.
    /// It is to be used only internally.
    /// # Example
    /// ```
    /// fn parse_length(length: &Self::LengthAsArray) -> Option<Self::usize> {
    ///     length[0] as usize +length[1] as usize * 256
    /// }
    /// ```
    fn parse_length(length: &Self::LengthAsArray) -> Option<usize>;
    /// This function splits an incoming message into header-array & payload-slice. If this fails (because the message is too short), None is returned.
    /// It is to be used only internally.
    /// # Example
    /// ```
    /// fn message_slice_to_header_array(input: &[u8]) -> Option<(&Self::HeaderAsArray, &[u8])> {
    ///     const HEADER_SIZE_EXAMPLE:usize = 5;
    ///     if input.len() >= HEADER_SIZE_EXAMPLE {
    ///         Some((
    ///             unsafe {
    ///             &*(input[0..HEADER_SIZE_EXAMPLE].as_ptr() as *const [_; HEADER_SIZE_EXAMPLE])
    ///         },
    ///         &input[HEADER_SIZE_EXAMPLE..],
    ///        ))
    ///     } else {
    ///         None
    ///     }
    /// }
    /// ```
    fn message_slice_to_header_array(input: &[u8]) -> Option<(&Self::HeaderAsArray, &[u8])>;
    /// This function splits header-array into a command-array and a length-array.
    /// It is to be used only internally.
    /// # Example
    /// The following example is "length first", so the payload length takes the first (two) bytes from the incoming header. The remaining bytes encode the command.
    /// ```
    /// fn split_header_array(header: &Self::HeaderAsArray) -> (&Self::CommandAsArray, &Self::LengthAsArray) {
    ///     const LENGTH_SIZE_EXAMPLE : usize = 2;
    ///     const HEADER_SIZE_EXAMPLE : usize = 5;
    ///     (
    ///         unsafe {
    ///             &*(header[LENGTH_SIZE_EXAMPLE..HEADER_SIZE_EXAMPLE].as_ptr()
    ///                     as *const [_; COMMAND_SIZE_EXAMPLE])
    ///         },
    ///         unsafe {
    ///             &*(header[0..LENGTH_SIZE_EXAMPLE].as_ptr() as *const [_; LENGTH_SIZE_EXAMPLE])
    ///         },
    ///     )
    /// }
    /// ```
    fn split_header_array(
        header: &Self::HeaderAsArray,
    ) -> (&Self::CommandAsArray, &Self::LengthAsArray);
    /// This function converts a command (enum-variant) to an array. This has to be the inverse of "parse_command".
    /// It is to be used only internally.
    /// # Example
    /// ```
    /// fn command_to_array(command: Self::Commands) -> Self::CommandAsArray {
    ///     use self::ExampleCommands::*;
    ///     match command {
    ///         Start=>[0,0,0],
    ///         Stop=>[1,2,3],
    ///         Failure=>[255,255,255],
    ///     }
    /// }
    /// ```
    fn command_to_array(command: Self::Commands) -> Self::CommandAsArray;
    /// This function computes a length (as array-representation) from a command and a message.
    /// If this fails (for example, if the message is too long), None is return.
    /// It is to be used only internally.
    /// # Example
    /// ```
    /// fn get_length_as_array(command: Self::Commands, message: &[u8]) -> Option<Self::LengthAsArray> {
    ///     let length = message.len() as u64;
    ///     if length >= 256u64.pow(3) {
    ///         return None;
    ///     }
    ///     let mut length_array = [0; 3];
    ///     for i in 0u32..3 {
    ///         length_array[(2 - i) as usize] = (length / 256u64.pow(i) % 256) as u8;
    ///     }
    ///     Some(length_array)
    /// }
    /// ```
    fn get_length_as_array(command: Self::Commands, message: &[u8]) -> Option<Self::LengthAsArray>;
    /// This function constructs the message header from a command and a length.
    /// The implementation below should work (I'm just unable to get it to work generically).
    /// # Example
    /// ```
    /// fn construct_header(command: Self::CommandAsArray, length: Self::LengthAsArray) -> Vec<u8> {
    ///     let mut header = Vec::new();
    ///     header.extend_from_slice(&length);
    ///     header.extend_from_slice(&command);
    ///     header
    /// }
    /// ```
    fn construct_header(command: Self::CommandAsArray, length: Self::LengthAsArray) -> Vec<u8>;

    /// This function parses a header into a command & a message length.
    /// The default implementation is fine.
    fn parse_header(
        header: &Self::HeaderAsArray,
    ) -> Result<(Self::Commands, usize), (ParseHeaderError, &Self::HeaderAsArray)> {
        let (command, length) = Self::split_header_array(header);
        if let Some(command) = Self::parse_command(command) {
            if let Some(length) = Self::parse_length(length) {
                Ok((command, length))
            } else {
                Err((ParseHeaderError::LengthParseFailed, header))
            }
        } else {
            Err((ParseHeaderError::CommandParseFailed, header))
        }
    }
    /// This function construct a message from a command & a payloay/message.
    /// The default implementation is fine.
    fn construct_message(command: Self::Commands, message: &[u8]) -> Option<Vec<u8>> {
        if let Some(length) = Self::get_length_as_array(command, message) {
            let command = Self::command_to_array(command);
            let mut new_message = Self::construct_header(command, length);
            new_message.extend_from_slice(message);
            Some(new_message)
        } else {
            None
        }
    }
}

/// A type alias combining a command (as enum-variant) & a message (as byte-vector).
pub type Message<P> = (<P as Protocol>::Commands, Vec<u8>);