rusmpp_extra/encoding/
latin1.rs

1//! Latin1 encoding/decoding support.
2
3use rusmpp_core::values::DataCoding;
4
5mod errors;
6pub use errors::{Latin1ConcatenateError, Latin1EncodeError};
7
8/// Latin1 codec.
9#[derive(Debug)]
10#[non_exhaustive]
11pub struct Latin1 {}
12
13impl Default for Latin1 {
14    fn default() -> Self {
15        Self::new()
16    }
17}
18
19impl Latin1 {
20    /// Creates a new [`Latin1`] codec.
21    pub const fn new() -> Self {
22        Self {}
23    }
24
25    /// Returns the associated [`DataCoding`].
26    pub const fn data_coding(&self) -> DataCoding {
27        DataCoding::Latin1
28    }
29}
30
31#[cfg(any(test, feature = "alloc"))]
32#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
33mod impl_owned {
34    use alloc::vec::Vec;
35
36    use crate::{
37        concatenation::{
38            MAX_PARTS,
39            owned::{Concatenation, Concatenator},
40        },
41        encoding::owned::Encoder,
42    };
43
44    use super::*;
45
46    impl Latin1 {
47        /// Encodes the given message into a vector of bytes.
48        pub fn encode_to_vec(&self, input: &str) -> Result<Vec<u8>, Latin1EncodeError> {
49            encoding_rs::mem::is_utf8_latin1(input.as_bytes())
50                .then_some(())
51                .ok_or(Latin1EncodeError::UnencodableCharacter)?;
52
53            let mut buffer = alloc::vec![0u8; input.len()];
54            /*
55            Correctness:
56
57            - The input is UTF-8 Latin1.
58            - The size of the buffer is at least as large as the encoded output.
59            */
60            let size =
61                encoding_rs::mem::convert_utf8_to_latin1_lossy(input.as_bytes(), &mut buffer);
62
63            buffer.truncate(size);
64
65            Ok(buffer)
66        }
67    }
68
69    impl Encoder for Latin1 {
70        type Error = Latin1EncodeError;
71
72        fn encode(&self, message: &str) -> Result<(Vec<u8>, DataCoding), Self::Error> {
73            self.encode_to_vec(message)
74                .map(|vec| (vec, self.data_coding()))
75        }
76    }
77
78    impl Concatenator for Latin1 {
79        type Error = Latin1ConcatenateError;
80
81        fn concatenate(
82            &self,
83            message: &str,
84            max_message_size: usize,
85            part_header_size: usize,
86        ) -> Result<(Concatenation, DataCoding), Self::Error> {
87            let encoded = self.encode_to_vec(message)?;
88
89            let total = encoded.len();
90
91            if total <= max_message_size {
92                return Ok((Concatenation::single(encoded), self.data_coding()));
93            }
94
95            let part_payload_size = max_message_size.saturating_sub(part_header_size);
96
97            if part_payload_size == 0 {
98                return Err(Latin1ConcatenateError::PartCapacityExceeded);
99            }
100
101            let parts = encoded
102                .chunks(part_payload_size)
103                .map(|chunk| chunk.to_vec())
104                .collect::<Vec<Vec<u8>>>();
105
106            if parts.len() > MAX_PARTS {
107                return Err(Latin1ConcatenateError::parts_count_exceeded(parts.len()));
108            }
109
110            Ok((Concatenation::concatenated(parts), self.data_coding()))
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests;