Skip to main content

c_its_parser/transport/
decode.rs

1use etherparse::PacketHeaders;
2use nom::bytes::streaming::take;
3use nom::combinator::{into, map_res};
4use nom::error::{ErrorKind, FromExternalError, ParseError};
5use nom::sequence::pair;
6
7use crate::transport::{BasicTransportAHeader, BasicTransportBHeader, IPv6Header};
8
9#[derive(Debug, PartialEq)]
10pub enum DecodeError<I> {
11    IntegerError(alloc::string::String),
12    IPv6Parsing(alloc::string::String),
13    Nom(I, ErrorKind),
14    #[cfg(feature = "json")]
15    Json(alloc::string::String),
16}
17
18impl<I> ParseError<I> for DecodeError<I> {
19    fn from_error_kind(input: I, kind: ErrorKind) -> Self {
20        DecodeError::Nom(input, kind)
21    }
22
23    fn append(_: I, _: ErrorKind, other: Self) -> Self {
24        other
25    }
26}
27
28impl<I, E> FromExternalError<I, E> for DecodeError<I> {
29    fn from_external_error(input: I, kind: ErrorKind, _: E) -> Self {
30        DecodeError::Nom(input, kind)
31    }
32}
33
34pub type IResult<I, T> = nom::IResult<I, T, DecodeError<I>>;
35
36pub trait Decode: Sized {
37    /// Decoder trait for decoding the individual fields of the transport header
38    /// Takes byte slice as input.
39    /// ### Usage
40    /// ```ignore
41    /// # use c_its_parser::transport::*;
42    /// let input: &'static [u8] = &[0,1,0,2];
43    /// let (_remaining_input, decoded) = BasicTransportAHeader::decode(input).unwrap();
44    /// assert_eq!(
45    ///     decoded,
46    ///     BasicTransportAHeader {
47    ///         destination_port: 1,
48    ///         source_port: 2,
49    ///     }
50    /// );
51    /// ```
52    fn decode(input: &[u8]) -> IResult<&[u8], Self>;
53}
54
55impl Decode for BasicTransportAHeader {
56    fn decode(input: &[u8]) -> IResult<&[u8], Self> {
57        into(pair(u16_from_be_bytes, u16_from_be_bytes))(input)
58    }
59}
60
61impl From<(u16, u16)> for BasicTransportAHeader {
62    fn from(value: (u16, u16)) -> Self {
63        Self {
64            destination_port: value.0,
65            source_port: value.1,
66        }
67    }
68}
69
70impl BasicTransportAHeader {
71    #[cfg(feature = "json")]
72    /// Decodes a BTP-A header from JSON
73    ///
74    /// # Errors
75    /// Returns an error when parsing failed
76    pub fn decode_from_json(input: &str) -> Result<Self, DecodeError<&str>> {
77        serde_json::from_str(input)
78            .map_err(|e| DecodeError::Json(alloc::format!("Error encoding to JSON: {e:?}")))
79    }
80}
81
82impl Decode for BasicTransportBHeader {
83    fn decode(input: &[u8]) -> IResult<&[u8], Self> {
84        into(pair(u16_from_be_bytes, u16_from_be_bytes))(input)
85    }
86}
87
88impl From<(u16, u16)> for BasicTransportBHeader {
89    fn from(value: (u16, u16)) -> Self {
90        Self {
91            destination_port: value.0,
92            destination_port_info: value.1,
93        }
94    }
95}
96
97impl BasicTransportBHeader {
98    #[cfg(feature = "json")]
99    /// Decodes a BTP-B header from JSON
100    ///
101    /// # Errors
102    /// Returns an error when parsing failed
103    pub fn decode_from_json(input: &str) -> Result<Self, DecodeError<&str>> {
104        serde_json::from_str(input)
105            .map_err(|e| DecodeError::Json(alloc::format!("Error encoding to JSON: {e:?}")))
106    }
107}
108
109fn u16_from_be_bytes(input: &[u8]) -> IResult<&[u8], u16> {
110    map_res(take(2usize), |slice: &[u8]| {
111        slice.try_into().map(u16::from_be_bytes).map_err(|e| {
112            DecodeError::IntegerError::<&[u8]>(alloc::format!(
113                "Failed to construct integer from bytes: {e:?}"
114            ))
115        })
116    })(input)
117}
118
119impl Decode for IPv6Header {
120    fn decode(input: &[u8]) -> IResult<&[u8], Self> {
121        etherparse::PacketHeaders::from_ip_slice(input)
122            .map(|headers| {
123                let first_after_headers = headers
124                    .net
125                    .as_ref()
126                    .map_or(0, etherparse::NetHeaders::header_len)
127                    + headers
128                        .link
129                        .as_ref()
130                        .map_or(0, etherparse::LinkHeader::header_len)
131                    + headers
132                        .transport
133                        .as_ref()
134                        .map_or(0, etherparse::TransportHeader::header_len)
135                    + headers
136                        .link_exts
137                        .first()
138                        .map_or(0, etherparse::LinkExtHeader::header_len);
139                (&input[first_after_headers..], IPv6Header::from(headers))
140            })
141            .map_err(|e| {
142                nom::Err::Error(DecodeError::IPv6Parsing(alloc::format!(
143                    "Error parsing IPv6 Header: {e:?}"
144                )))
145            })
146    }
147}
148
149impl From<PacketHeaders<'_>> for IPv6Header {
150    fn from(value: PacketHeaders<'_>) -> Self {
151        Self {
152            ip: value.net,
153            link: value.link,
154            transport: value.transport,
155            link_ext: value.link_exts.first().cloned(),
156        }
157    }
158}
159
160#[cfg(test)]
161mod tests {}