rusmpp/types/
empty_or_full_c_octet_string.rs

1#![allow(path_statements)]
2
3use alloc::{string::String, string::ToString, vec::Vec};
4
5use crate::{
6    decode::{COctetStringDecodeError, Decode, DecodeError},
7    encode::{Encode, Length},
8};
9
10/// An error that can occur when creating an [`EmptyOrFullCOctetString`].
11#[derive(Debug)]
12pub enum Error {
13    TooManyBytes { actual: usize, max: usize },
14    TooFewBytes { actual: usize },
15    NotNullTerminated,
16    NotAscii,
17    NullByteFound,
18}
19
20impl core::fmt::Display for Error {
21    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22        match self {
23            Self::TooManyBytes { actual, max } => {
24                write!(f, "Too many bytes. actual: {actual}, max: {max}")
25            }
26            Self::TooFewBytes { actual } => {
27                write!(f, "Too few bytes. actual: {actual}, min: 1")
28            }
29            Self::NotNullTerminated => write!(f, "Not null terminated"),
30            Self::NotAscii => write!(f, "Not ASCII"),
31            Self::NullByteFound => write!(f, "Null byte found"),
32        }
33    }
34}
35
36impl core::error::Error for Error {}
37
38/// Empty or full [`COctetString`](struct@crate::types::COctetString).
39///
40/// `N` is the maximum length of the string, including the null terminator.
41///
42/// Possible values:
43///  - Empty: `[0x00]`
44///  - Full: `[..(N - 1), 0x00]` where `0x00` not in `..(N - 1)`
45///
46/// # Notes
47///
48/// `N` must be greater than `0`.
49/// ```rust, compile_fail
50/// use rusmpp::types::EmptyOrFullCOctetString;
51///
52/// // does not compile
53/// let string = EmptyOrFullCOctetString::<0>::new(b"Hello\0");
54/// ```
55#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
56#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
57pub struct EmptyOrFullCOctetString<const N: usize> {
58    bytes: Vec<u8>,
59}
60
61impl<const N: usize> EmptyOrFullCOctetString<N> {
62    const _ASSERT_NON_ZERO: () = assert!(N > 0, "N must be greater than 0");
63
64    /// Create a new empty [`EmptyOrFullCOctetString`].
65    ///
66    /// Equivalent to [`EmptyOrFullCOctetString::empty`].
67    #[inline]
68    pub fn null() -> Self {
69        Self::empty()
70    }
71
72    /// Create a new empty [`EmptyOrFullCOctetString`].
73    #[inline]
74    pub fn empty() -> Self {
75        Self::_ASSERT_NON_ZERO;
76
77        Self {
78            bytes: alloc::vec![0],
79        }
80    }
81
82    /// Check if an [`EmptyOrFullCOctetString`] is empty.
83    ///
84    /// An [`EmptyOrFullCOctetString`] is considered empty if it
85    /// contains only a single NULL octet `(0x00)`.
86    #[inline]
87    pub fn is_empty(&self) -> bool {
88        self.bytes.len() == 1
89    }
90
91    /// Create a new [`EmptyOrFullCOctetString`] from a sequence of bytes including a null terminator.
92    pub fn new(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
93        Self::_ASSERT_NON_ZERO;
94
95        let bytes = bytes.as_ref();
96
97        // We must have at least the null terminator
98        if bytes.is_empty() {
99            return Err(Error::TooFewBytes { actual: 0 });
100        }
101
102        // If we have at least one byte, it must be the null terminator
103        if bytes.len() == 1 {
104            if bytes[0] != 0 {
105                return Err(Error::NotNullTerminated);
106            }
107
108            return Ok(Self {
109                bytes: bytes.to_vec(),
110            });
111        }
112
113        if bytes.len() < N {
114            return Err(Error::TooFewBytes {
115                actual: bytes.len(),
116            });
117        }
118
119        if bytes.len() > N {
120            return Err(Error::TooManyBytes {
121                actual: bytes.len(),
122                max: N,
123            });
124        }
125
126        if bytes[bytes.len() - 1] != 0 {
127            return Err(Error::NotNullTerminated);
128        }
129
130        if bytes[..bytes.len() - 1].contains(&0) {
131            return Err(Error::NullByteFound);
132        }
133
134        if !bytes.is_ascii() {
135            return Err(Error::NotAscii);
136        }
137
138        let bytes = bytes.to_vec();
139
140        Ok(Self { bytes })
141    }
142
143    /// Convert an [`EmptyOrFullCOctetString`] to a &[`str`] including the null terminator.
144    #[inline]
145    pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
146        core::str::from_utf8(&self.bytes)
147    }
148
149    /// Get the bytes of an [`EmptyOrFullCOctetString`].
150    #[inline]
151    pub fn bytes(&self) -> &[u8] {
152        &self.bytes
153    }
154
155    /// Convert an [`EmptyOrFullCOctetString`] to a [`Vec`] of [`u8`].
156    #[inline]
157    pub fn into_bytes(self) -> Vec<u8> {
158        self.bytes
159    }
160}
161
162impl<const N: usize> core::fmt::Debug for EmptyOrFullCOctetString<N> {
163    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164        f.debug_struct("EmptyOrFullCOctetString")
165            .field("bytes", &crate::utils::HexFormatter(&self.bytes))
166            .field("string", &self.to_string())
167            .finish()
168    }
169}
170
171impl<const N: usize> Default for EmptyOrFullCOctetString<N> {
172    fn default() -> Self {
173        Self::empty()
174    }
175}
176
177impl<const N: usize> core::str::FromStr for EmptyOrFullCOctetString<N> {
178    type Err = Error;
179
180    /// Create a new [`EmptyOrFullCOctetString`] from an &[`str`] including without a null terminator.
181    fn from_str(s: &str) -> Result<Self, Self::Err> {
182        Self::_ASSERT_NON_ZERO;
183
184        let bytes = s.as_bytes();
185
186        // We pretend as if the string had a null terminator at the end, that is why the bytes.len() + 1
187        if bytes.len() + 1 > 1 {
188            if bytes.len() + 1 < N {
189                return Err(Error::TooFewBytes {
190                    actual: bytes.len() + 1,
191                });
192            }
193
194            if bytes.len() + 1 > N {
195                return Err(Error::TooManyBytes {
196                    actual: bytes.len() + 1,
197                    max: N,
198                });
199            }
200        }
201
202        if !bytes.is_ascii() {
203            return Err(Error::NotAscii);
204        }
205
206        if bytes[..bytes.len()].contains(&0) {
207            return Err(Error::NullByteFound);
208        }
209
210        let mut bytes = bytes.to_vec();
211
212        bytes.push(0);
213
214        Ok(Self { bytes })
215    }
216}
217
218impl<const N: usize> core::fmt::Display for EmptyOrFullCOctetString<N> {
219    /// Format an [`EmptyOrFullCOctetString`] including the null terminator.
220    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
221        f.write_str(&String::from_utf8_lossy(&self.bytes))
222    }
223}
224
225impl<const N: usize> AsRef<[u8]> for EmptyOrFullCOctetString<N> {
226    fn as_ref(&self) -> &[u8] {
227        &self.bytes
228    }
229}
230
231impl<const N: usize> Length for EmptyOrFullCOctetString<N> {
232    fn length(&self) -> usize {
233        self.bytes.len()
234    }
235}
236
237impl<const N: usize> Encode for EmptyOrFullCOctetString<N> {
238    fn encode(&self, dst: &mut [u8]) -> usize {
239        _ = &mut dst[..self.bytes.len()].copy_from_slice(&self.bytes);
240
241        self.bytes.len()
242    }
243}
244
245impl<const N: usize> Decode for EmptyOrFullCOctetString<N> {
246    fn decode(src: &[u8]) -> Result<(Self, usize), DecodeError> {
247        Self::_ASSERT_NON_ZERO;
248
249        let mut bytes = Vec::with_capacity(N);
250
251        for i in 0..N {
252            if i >= src.len() {
253                return Err(DecodeError::unexpected_eof());
254            }
255
256            let byte = src[i];
257
258            bytes.push(byte);
259
260            if byte == 0 {
261                let len = i + 1;
262
263                if bytes.len() > 1 && bytes.len() < N {
264                    return Err(DecodeError::c_octet_string_decode_error(
265                        COctetStringDecodeError::TooFewBytes {
266                            actual: bytes.len(),
267                            min: N,
268                        },
269                    ));
270                }
271
272                if !bytes.is_ascii() {
273                    return Err(DecodeError::c_octet_string_decode_error(
274                        COctetStringDecodeError::NotAscii,
275                    ));
276                }
277
278                return Ok((Self { bytes }, len));
279            }
280        }
281
282        Err(DecodeError::c_octet_string_decode_error(
283            COctetStringDecodeError::NotNullTerminated,
284        ))
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use super::*;
291
292    impl<const N: usize> crate::tests::TestInstance for EmptyOrFullCOctetString<N> {
293        fn instances() -> Vec<Self> {
294            alloc::vec![
295                Self::empty(),
296                Self::new(
297                    core::iter::repeat_n(b'1', N - 1)
298                        .chain(core::iter::once(b'\0'))
299                        .collect::<Vec<_>>(),
300                )
301                .unwrap(),
302            ]
303        }
304    }
305
306    #[test]
307    fn encode_decode() {
308        crate::tests::encode_decode_test_instances::<EmptyOrFullCOctetString<1>>();
309        crate::tests::encode_decode_test_instances::<EmptyOrFullCOctetString<2>>();
310        crate::tests::encode_decode_test_instances::<EmptyOrFullCOctetString<3>>();
311    }
312
313    mod new {
314        use super::*;
315
316        #[test]
317        fn empty_too_few_bytes() {
318            let bytes = b"";
319            let error = EmptyOrFullCOctetString::<5>::new(bytes).unwrap_err();
320            assert!(matches!(error, Error::TooFewBytes { actual: 0 }));
321        }
322
323        #[test]
324        fn too_many_bytes() {
325            let bytes = b"Hello\0";
326            let error = EmptyOrFullCOctetString::<5>::new(bytes).unwrap_err();
327            assert!(matches!(error, Error::TooManyBytes { actual: 6, max: 5 }));
328        }
329
330        #[test]
331        fn too_few_bytes() {
332            let bytes = b"Hel\0";
333            let error = EmptyOrFullCOctetString::<5>::new(bytes).unwrap_err();
334            assert!(matches!(error, Error::TooFewBytes { actual: 4 }));
335        }
336
337        #[test]
338        fn not_null_terminated() {
339            let bytes = b"Hello";
340            let error = EmptyOrFullCOctetString::<5>::new(bytes).unwrap_err();
341            assert!(matches!(error, Error::NotNullTerminated));
342        }
343
344        #[test]
345        fn not_ascii() {
346            let bytes = b"Hell\xF0\0";
347            let error = EmptyOrFullCOctetString::<6>::new(bytes).unwrap_err();
348            assert!(matches!(error, Error::NotAscii));
349        }
350
351        #[test]
352        fn null_byte_found() {
353            let bytes = b"Hel\0lo\0";
354            let error = EmptyOrFullCOctetString::<7>::new(bytes).unwrap_err();
355            assert!(matches!(error, Error::NullByteFound));
356        }
357
358        #[test]
359        fn ok() {
360            let bytes = b"Hello\0";
361            let string = EmptyOrFullCOctetString::<6>::new(bytes).unwrap();
362            assert_eq!(string.bytes, bytes);
363        }
364
365        #[test]
366        fn ok_len() {
367            let bytes = b"Hello\0";
368            let string = EmptyOrFullCOctetString::<6>::new(bytes).unwrap();
369            assert_eq!(string.bytes.len(), 6);
370            assert_eq!(string.length(), 6);
371        }
372
373        #[test]
374        fn ok_empty() {
375            let bytes = b"\0";
376            let string = EmptyOrFullCOctetString::<6>::new(bytes).unwrap();
377            assert_eq!(string.bytes, bytes);
378            assert_eq!(string.bytes.len(), 1);
379            assert_eq!(string.length(), 1);
380        }
381    }
382
383    mod from_str {
384        use core::str::FromStr;
385
386        use super::*;
387
388        #[test]
389        fn too_many_bytes() {
390            let string = "Hello";
391            let error = EmptyOrFullCOctetString::<5>::from_str(string).unwrap_err();
392            assert!(matches!(error, Error::TooManyBytes { actual: 6, .. }));
393        }
394
395        #[test]
396        fn too_few_bytes() {
397            let string = "Hel";
398            let error = EmptyOrFullCOctetString::<5>::from_str(string).unwrap_err();
399            assert!(matches!(error, Error::TooFewBytes { actual: 4 }));
400        }
401
402        #[test]
403        fn null_byte_found() {
404            let string = "Hel\0lo";
405            let error = EmptyOrFullCOctetString::<7>::from_str(string).unwrap_err();
406            assert!(matches!(error, Error::NullByteFound));
407        }
408
409        #[test]
410        fn not_ascii() {
411            let string = "Hellö"; // ö is 2 bytes. Hellö = 6 bytes, + 1 null terminator = 7 bytes
412            let error = EmptyOrFullCOctetString::<7>::from_str(string).unwrap_err();
413            assert!(matches!(error, Error::NotAscii));
414        }
415
416        #[test]
417        fn ok() {
418            let string = "Hello";
419            let bytes = b"Hello\0";
420            let string = EmptyOrFullCOctetString::<6>::from_str(string).unwrap();
421            assert_eq!(string.bytes, bytes);
422        }
423
424        #[test]
425        fn ok_len() {
426            let string = "Hello";
427            let string = EmptyOrFullCOctetString::<6>::from_str(string).unwrap();
428            assert_eq!(string.bytes.len(), 6);
429            assert_eq!(string.length(), 6);
430        }
431
432        #[test]
433        fn ok_empty() {
434            let string = "";
435            let bytes = b"\0";
436            let string = EmptyOrFullCOctetString::<6>::from_str(string).unwrap();
437            assert_eq!(string.bytes, bytes);
438        }
439
440        #[test]
441        fn ok_empty_len() {
442            let string = "";
443            let string = EmptyOrFullCOctetString::<6>::from_str(string).unwrap();
444            assert_eq!(string.bytes.len(), 1);
445            assert_eq!(string.length(), 1);
446        }
447    }
448
449    mod to_str {
450        use super::*;
451
452        #[test]
453        fn empty_ok() {
454            let bytes = b"\0";
455            let string = EmptyOrFullCOctetString::<6>::new(bytes).unwrap();
456            assert_eq!(string.to_str().unwrap(), "\0");
457            assert_eq!(string.to_string(), "\0");
458        }
459
460        #[test]
461        fn ok() {
462            let bytes = b"Hello\0";
463            let string = EmptyOrFullCOctetString::<6>::new(bytes).unwrap();
464            assert_eq!(string.to_str().unwrap(), "Hello\0");
465            assert_eq!(string.to_string(), "Hello\0");
466        }
467    }
468
469    mod decode {
470        use crate::decode::DecodeErrorKind;
471
472        use super::*;
473
474        #[test]
475        fn unexpected_eof_empty() {
476            let bytes = b"";
477            let error = EmptyOrFullCOctetString::<6>::decode(bytes).unwrap_err();
478
479            assert!(matches!(error.kind(), DecodeErrorKind::UnexpectedEof));
480        }
481
482        #[test]
483        fn not_null_terminated() {
484            let bytes = b"Hi";
485            let error = EmptyOrFullCOctetString::<2>::decode(bytes).unwrap_err();
486
487            assert!(matches!(
488                error.kind(),
489                DecodeErrorKind::COctetStringDecodeError(
490                    COctetStringDecodeError::NotNullTerminated
491                )
492            ));
493        }
494
495        #[test]
496        fn too_many_bytes() {
497            let bytes = b"Hello\0";
498            let error = EmptyOrFullCOctetString::<5>::decode(bytes).unwrap_err();
499
500            assert!(matches!(
501                error.kind(),
502                DecodeErrorKind::COctetStringDecodeError(
503                    COctetStringDecodeError::NotNullTerminated,
504                )
505            ));
506        }
507
508        #[test]
509        fn too_few_bytes() {
510            let bytes = b"Hel\0";
511            let error = EmptyOrFullCOctetString::<5>::decode(bytes).unwrap_err();
512
513            assert!(matches!(
514                error.kind(),
515                DecodeErrorKind::COctetStringDecodeError(COctetStringDecodeError::TooFewBytes {
516                    actual: 4,
517                    min: 5,
518                },)
519            ));
520        }
521
522        #[test]
523        fn not_ascii() {
524            let bytes = b"Hell\xF0\0";
525            let error = EmptyOrFullCOctetString::<6>::decode(bytes).unwrap_err();
526
527            assert!(matches!(
528                error.kind(),
529                DecodeErrorKind::COctetStringDecodeError(COctetStringDecodeError::NotAscii)
530            ));
531        }
532
533        #[test]
534        fn ok() {
535            let bytes = b"Hello\0World!";
536            let (string, size) = EmptyOrFullCOctetString::<6>::decode(bytes).unwrap();
537
538            assert_eq!(string.bytes, b"Hello\0");
539            assert_eq!(string.length(), 6);
540            assert_eq!(size, 6);
541            assert_eq!(&bytes[size..], b"World!");
542        }
543
544        #[test]
545        fn ok_empty() {
546            let bytes = b"\0World!";
547            let (string, size) = EmptyOrFullCOctetString::<6>::decode(bytes).unwrap();
548
549            assert_eq!(string.bytes, b"\0");
550            assert_eq!(string.length(), 1);
551            assert_eq!(size, 1);
552            assert_eq!(&bytes[size..], b"World!");
553        }
554    }
555}