rusmpp_core/framez/
mod.rs

1//! Framez [`Encoder`] and [`Decoder`] implementations.
2
3use core::num::TryFromIntError;
4
5use framez::{decode::Decoder, encode::Encoder};
6
7use crate::{
8    command::borrowed::Command,
9    decode::borrowed::DecodeWithLength,
10    encode::{Encode, Length},
11    logging::{debug, error, trace},
12};
13
14#[cfg(test)]
15mod tests;
16
17/// Codec for encoding and decoding `SMPP` PDUs using [`Encoder`] and [`Decoder`] traits.
18#[derive(Debug)]
19#[non_exhaustive]
20pub struct CommandCodec<const N: usize> {}
21
22impl<const N: usize> CommandCodec<N> {
23    pub const fn new() -> Self {
24        Self {}
25    }
26}
27
28impl<const N: usize> Default for CommandCodec<N> {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34/// An error that can occur when encoding a [`Command`].
35#[derive(Debug)]
36#[non_exhaustive]
37pub enum EncodeError {
38    /// The input buffer is too small to fit the encoded [`Command`].
39    BufferTooSmall,
40}
41
42impl core::fmt::Display for EncodeError {
43    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44        match self {
45            Self::BufferTooSmall => write!(f, "Buffer too small"),
46        }
47    }
48}
49
50impl core::error::Error for EncodeError {}
51
52impl<'buf, const N: usize> Encoder<Command<'buf, N>> for CommandCodec<N> {
53    type Error = EncodeError;
54
55    fn encode(&mut self, item: Command<'buf, N>, dst: &mut [u8]) -> Result<usize, Self::Error> {
56        let command_length = 4 + item.length();
57
58        if dst.len() < command_length {
59            return Err(EncodeError::BufferTooSmall);
60        }
61
62        dst[..4].copy_from_slice(&(command_length as u32).to_be_bytes());
63        let _ = item.encode(&mut dst[4..command_length]);
64
65        debug!(target: "rusmpp::codec::encode", command=?item, "Encoding");
66        debug!(target: "rusmpp::codec::encode", encoded=?crate::formatter::Formatter(&dst[..command_length]), encoded_length=item.length(), command_length, "Encoded");
67
68        Ok(command_length)
69    }
70}
71
72/// An error that can occur when decoding a [`Command`].
73#[derive(Debug)]
74#[non_exhaustive]
75pub enum DecodeError {
76    /// Decode error.
77    Decode(crate::decode::DecodeError),
78    /// Minimum command length not met.
79    MinLength { actual: usize, min: usize },
80    /// Integral type conversion failed.
81    InvalidLength(TryFromIntError),
82}
83
84impl core::fmt::Display for DecodeError {
85    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86        match self {
87            DecodeError::Decode(e) => write!(f, "Decode error: {e}"),
88            DecodeError::MinLength { actual, min } => {
89                write!(
90                    f,
91                    "Minimum command length not met. actual: {actual}, min: {min}"
92                )
93            }
94
95            DecodeError::InvalidLength(e) => {
96                write!(f, "Integral type conversion failed: {e}")
97            }
98        }
99    }
100}
101
102impl core::error::Error for DecodeError {}
103
104impl<const N: usize> framez::decode::DecodeError for CommandCodec<N> {
105    type Error = DecodeError;
106}
107
108impl<'buf, const N: usize> Decoder<'buf> for CommandCodec<N> {
109    type Item = Command<'buf, N>;
110
111    fn decode(&mut self, src: &'buf mut [u8]) -> Result<Option<(Self::Item, usize)>, Self::Error> {
112        const HEADER_LENGTH: usize = 16;
113
114        if src.len() < HEADER_LENGTH {
115            trace!(target: "rusmpp::codec::decode", source_length=src.len(), "Not enough bytes to read the header");
116
117            return Ok(None);
118        }
119
120        let command_length =
121                usize::try_from(u32::from_be_bytes([src[0], src[1], src[2], src[3]]))
122                    .inspect_err(|_err| {
123                        error!(target: "rusmpp::codec::decode", err=?_err, "Failed to convert command length to usize");
124                    })
125                    .map_err(DecodeError::InvalidLength)?;
126
127        trace!(target: "rusmpp::codec::decode", command_length);
128
129        if command_length < HEADER_LENGTH {
130            error!(target: "rusmpp::codec::decode", command_length, min_command_length=HEADER_LENGTH, "Minimum command length not met");
131
132            return Err(DecodeError::MinLength {
133                actual: command_length,
134                min: HEADER_LENGTH,
135            });
136        }
137
138        if src.len() < command_length {
139            trace!(target: "rusmpp::codec::decode", command_length, "Not enough bytes to read the entire command");
140
141            return Ok(None);
142        }
143
144        // command_length is at least 16 bytes
145        let pdu_len = command_length - 4;
146
147        debug!(target: "rusmpp::codec::decode", decoding=?crate::formatter::Formatter(&src[..command_length]), "Decoding");
148
149        let (command, _size) = match Command::decode(&src[4..command_length], pdu_len) {
150            Ok((command, size)) => {
151                debug!(target: "rusmpp::codec::decode", command=?command, command_length, decoded_length=size, "Decoded");
152
153                (command, size)
154            }
155            Err(err) => {
156                error!(target: "rusmpp::codec::decode", ?err);
157
158                return Err(DecodeError::Decode(err));
159            }
160        };
161
162        Ok(Some((command, command_length)))
163    }
164}