DEVELOPMENT ENVIRONMENT

~liljamo/emerwen

ref: ef2b830165b22e9bcc235221875b81e359a8bb7d emerwen/emerwen-protocol/src/payload.rs -rw-r--r-- 3.5 KiB
ef2b8301Jonni Liljamo feat: 2nd protocol iteration 15 days ago
                                                                                
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
use byteorder::{BigEndian, ReadBytesExt};

use crate::DecodeError;

/// Message Payload
///
/// Byte representation:
/// | [u8]                                     |
/// | content bytes, structure varies per type |
#[derive(Debug, PartialEq)]
pub enum MessagePayload {
    /// Master -> Worker authentication.
    Authentication { key: String },
    /// Master -> Worker target configuration.
    /*ConfigureTarget {
        target_id: u16,
        method: TargetMethod,
        addr: String
    },*/
    /// Worker -> Master target state change.
    TargetStateChange {
        /// ID of the target.
        target_id: u16,
        /// State the target changed to.
        ///
        /// [`true`] means up.
        /// [`false`] means down.
        state: bool,
    },
}

impl From<&MessagePayload> for u8 {
    fn from(message: &MessagePayload) -> u8 {
        match message {
            MessagePayload::Authentication { .. } => 0,
            MessagePayload::TargetStateChange { .. } => 1,
        }
    }
}

impl MessagePayload {
    pub fn encode(&self) -> Vec<u8> {
        let mut buf: Vec<u8> = Vec::new();

        match self {
            MessagePayload::Authentication { key } => {
                // Key length
                buf.extend((key.len() as u32).to_be_bytes());
                // Key
                buf.extend(key.as_bytes());
            }
            MessagePayload::TargetStateChange { target_id, state } => {
                buf.extend(target_id.to_be_bytes());
                buf.push((*state).into());
            }
        }

        buf
    }

    pub fn decode(
        payload_type: u8,
        buf: &mut impl std::io::Read,
    ) -> Result<MessagePayload, Box<dyn std::error::Error>> {
        let payload = match payload_type {
            0 => {
                let key_length = buf.read_u32::<BigEndian>()?;
                let mut key_bytes = vec![0; key_length as usize];
                buf.read_exact(&mut key_bytes)?;
                MessagePayload::Authentication {
                    key: String::from_utf8(key_bytes)?,
                }
            }
            1 => {
                let target_id = buf.read_u16::<BigEndian>()?;
                let state = u8_to_bool(buf.read_u8()?)?;

                MessagePayload::TargetStateChange { target_id, state }
            }
            _ => return Err(Box::new(DecodeError::InvalidPayloadType(payload_type))),
        };

        Ok(payload)
    }
}

fn u8_to_bool(n: u8) -> Result<bool, DecodeError> {
    match n {
        0 => Ok(false),
        1 => Ok(true),
        _ => Err(DecodeError::InvalidBoolean(n)),
    }
}

#[test]
fn test_round_trip_payload_authentication() {
    let sent_payload = MessagePayload::Authentication {
        key: "this_is_a_key".to_owned(),
    };

    let sent_payload_bytes = sent_payload.encode();
    let sent_payload_type = (&sent_payload).into();

    let mut reader = std::io::Cursor::new(sent_payload_bytes);
    let received_payload = MessagePayload::decode(sent_payload_type, &mut reader).unwrap();

    assert_eq!(sent_payload, received_payload);
}

#[test]
fn test_round_trip_payload_targetstatechange() {
    let sent_payload = MessagePayload::TargetStateChange {
        target_id: 42,
        state: true,
    };

    let sent_payload_bytes = sent_payload.encode();
    let sent_payload_type = (&sent_payload).into();

    let mut reader = std::io::Cursor::new(sent_payload_bytes);
    let received_payload = MessagePayload::decode(sent_payload_type, &mut reader).unwrap();

    assert_eq!(sent_payload, received_payload);
}