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