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
pub use crate::spec::v1_2::operand::interface_configuration::InterfaceConfigurationDecodingError;
#[cfg(test)]
use crate::test_tools::test_item;
use crate::{
    codec::{Codec, WithOffset, WithSize},
    spec::v1_2 as spec,
    wizzilab::v5_3::dash7,
};
#[cfg(test)]
use hex_literal::hex;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum InterfaceId {
    Host = 0,
    D7asp = 0xD7,
}

/// Meta data required to send a packet depending on the sending interface type
#[derive(Clone, Debug, PartialEq)]
pub enum InterfaceConfiguration {
    Host,
    D7asp(dash7::InterfaceConfiguration),
}
impl std::fmt::Display for InterfaceConfiguration {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Self::Host => write!(f, "HOST"),
            Self::D7asp(conf) => write!(f, "D7:{}", conf),
        }
    }
}
impl Codec for InterfaceConfiguration {
    type Error = InterfaceConfigurationDecodingError;
    fn encoded_size(&self) -> usize {
        1 + match self {
            InterfaceConfiguration::Host => 0,
            InterfaceConfiguration::D7asp(v) => v.encoded_size(),
        }
    }
    unsafe fn encode_in(&self, out: &mut [u8]) -> usize {
        match self {
            InterfaceConfiguration::Host => {
                out[0] = InterfaceId::Host as u8;
                1
            }
            InterfaceConfiguration::D7asp(v) => {
                out[0] = InterfaceId::D7asp as u8;
                1 + v.encode_in(&mut out[1..])
            }
        }
    }
    fn decode(out: &[u8]) -> Result<WithSize<Self>, WithOffset<Self::Error>> {
        if out.is_empty() {
            return Err(WithOffset::new_head(Self::Error::MissingBytes(1)));
        }
        const HOST: u8 = InterfaceId::Host as u8;
        const D7ASP: u8 = InterfaceId::D7asp as u8;
        Ok(match out[0] {
            HOST => WithSize {
                value: InterfaceConfiguration::Host,
                size: 1,
            },
            D7ASP => {
                let WithSize { value, size } = dash7::InterfaceConfiguration::decode(&out[1..])
                    .map_err(|e| e.map_value(InterfaceConfigurationDecodingError::D7asp))?;
                WithSize {
                    value: InterfaceConfiguration::D7asp(value),
                    size: size + 1,
                }
            }
            id => {
                return Err(WithOffset {
                    value: Self::Error::BadInterfaceId(id),
                    offset: 0,
                })
            }
        })
    }
}
#[test]
fn test_interface_configuration_d7asp() {
    test_item(
        InterfaceConfiguration::D7asp(dash7::InterfaceConfiguration {
            qos: dash7::Qos {
                retry: dash7::RetryMode::Oneshot,
                resp: dash7::RespMode::Any,
            },
            to: 0x23,
            te: 0x34,
            nls_method: dash7::NlsMethod::AesCcm32,
            access_class: 0xFF,
            address: dash7::Address::Vid([0xAB, 0xCD]),
            use_vid: false,
            group_condition: dash7::GroupCondition::Any,
        }),
        &hex!("D7   02 23 34   37 FF ABCD  000000000000"),
    )
}
#[test]
fn test_interface_configuration_host() {
    test_item(InterfaceConfiguration::Host, &hex!("00"))
}
impl From<spec::operand::InterfaceConfiguration> for InterfaceConfiguration {
    fn from(o: spec::operand::InterfaceConfiguration) -> Self {
        match o {
            spec::operand::InterfaceConfiguration::Host => Self::Host,
            spec::operand::InterfaceConfiguration::D7asp(conf) => Self::D7asp(conf.into()),
        }
    }
}
impl From<InterfaceConfiguration> for spec::operand::InterfaceConfiguration {
    fn from(o: InterfaceConfiguration) -> Self {
        match o {
            InterfaceConfiguration::Host => Self::Host,
            InterfaceConfiguration::D7asp(conf) => Self::D7asp(conf.into()),
        }
    }
}