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
use super::*;
use std::convert::TryFrom;

const PROTOCOL_VERSION_INDEX: usize = 0;
const IDENTIFIER_INDEX: usize = 3;
const PACKET_PAYLOAD_START: usize = 8;

fn random_token(buffer: &[u8]) -> u16 {
    (buffer[1] as u16) << 8 | buffer[2] as u16
}

pub fn gateway_mac(buffer: &[u8]) -> MacAddress {
    MacAddress::new(array_ref![buffer, 0, 8])
}

pub trait Parser {
    fn parse(buffer: &[u8]) -> std::result::Result<Packet, Error>;
}

impl Parser for Packet {
    fn parse(buffer: &[u8]) -> std::result::Result<Packet, Error> {
        if buffer[PROTOCOL_VERSION_INDEX] != PROTOCOL_VERSION {
            return Err(Error::InvalidProtocolVersion);
        };

        match Identifier::try_from(buffer[IDENTIFIER_INDEX]) {
            Err(_) => Err(Error::InvalidIdentifier),
            Ok(id) => {
                let random_token = random_token(buffer);
                let buffer = &buffer[4..];
                Ok(match id {
                    // up packets
                    Identifier::PullData => {
                        let gateway_mac = gateway_mac(&buffer[..PACKET_PAYLOAD_START]);
                        pull_data::Packet {
                            random_token,
                            gateway_mac,
                        }
                        .into()
                    }
                    Identifier::PushData => {
                        let gateway_mac = gateway_mac(&buffer[..PACKET_PAYLOAD_START]);
                        let json_str = std::str::from_utf8(&buffer[PACKET_PAYLOAD_START..])?;
                        let data = serde_json::from_str(json_str)?;

                        push_data::Packet {
                            random_token,
                            gateway_mac,
                            data,
                        }
                        .into()
                    }
                    Identifier::TxAck => {
                        let gateway_mac = gateway_mac(&buffer[..PACKET_PAYLOAD_START]);
                        let data = if buffer.len() > PACKET_PAYLOAD_START {
                            let json_str = std::str::from_utf8(&buffer[PACKET_PAYLOAD_START..])?;
                            Some(serde_json::from_str(json_str).unwrap())
                        } else {
                            None
                        };
                        tx_ack::Packet {
                            random_token,
                            gateway_mac,
                            data,
                        }
                        .into()
                    }
                    // down packets
                    Identifier::PushAck => push_ack::Packet { random_token }.into(),
                    Identifier::PullAck => pull_ack::Packet { random_token }.into(),
                    Identifier::PullResp => {
                        let json_str = std::str::from_utf8(&buffer)?;
                        let data = serde_json::from_str(json_str)?;
                        pull_resp::Packet { random_token, data }.into()
                    }
                })
            }
        }
    }
}

use std::{fmt, str};

#[derive(Debug, Clone)]
pub enum Error {
    InvalidProtocolVersion,
    InvalidIdentifier,
    Utf8(std::str::Utf8Error),
    Json,
}

impl From<std::str::Utf8Error> for Error {
    fn from(err: std::str::Utf8Error) -> Error {
        Error::Utf8(err)
    }
}

impl From<serde_json::error::Error> for Error {
    fn from(_: serde_json::error::Error) -> Error {
        Error::Json
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::InvalidProtocolVersion => {
                write!(f, "Invalid protocol version (byte 0 in UDP frame)")
            }
            Error::InvalidIdentifier => {
                write!(f, "Invalid message identifier (byte 3 in UDP frame)")
            }
            Error::Utf8(err) => write!(f, "UTF-8 from bytes parsing error: {}", err),
            Error::Json => write!(f, "Json Deserialization Error"),
        }
    }
}

impl stdError for Error {
    fn description(&self) -> &str {
        match self {
            Error::InvalidProtocolVersion => "Invalid protocol version (byte 0 in UDP frame)",
            Error::InvalidIdentifier => "Invalid message identifier (byte 3 in UDP frame)",
            Error::Utf8(_err) => "UTF-8 from bytes parsing error",
            Error::Json => "Json Deserialization Error",
        }
    }

    fn cause(&self) -> Option<&dyn stdError> {
        // Generic error, underlying cause isn't tracked.
        None
    }
}