hadris_core/
str.rs

1/// A no-std compatible string type
2///
3/// This is a wrapper around a fixed size array of bytes
4/// The string is not null terminated, and the length is stored in the struct
5#[repr(C)]
6#[derive(Clone, Copy)]
7pub struct FixedByteStr<const N: usize> {
8    pub raw: [u8; N],
9    pub len: usize,
10}
11
12impl<const N: usize> FixedByteStr<N> {
13    pub fn new() -> Self {
14        Self {
15            raw: [0; N],
16            len: 0,
17        }
18    }
19    pub fn from_str(s: &str) -> Self {
20        assert!(s.len() <= N, "String length exceeds maximum length");
21        let mut str = Self {
22            raw: [0; N],
23            len: s.len(),
24        };
25        str.raw[..s.len()].copy_from_slice(s.as_bytes());
26        str
27    }
28
29    pub fn as_str(&self) -> &str {
30        core::str::from_utf8(&self.raw[..self.len]).unwrap()
31    }
32
33    pub fn as_slice(&self) -> &[u8; N] {
34        &self.raw
35    }
36}
37
38impl<const N: usize> core::fmt::Debug for FixedByteStr<N> {
39    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40        f.debug_tuple("FixedByteStr")
41            .field(&self.as_str())
42            .finish()
43    }
44}
45
46impl<const N: usize> core::fmt::Display for FixedByteStr<N> {
47    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48        f.write_str(self.as_str())
49    }
50}
51
52impl<const N: usize> core::fmt::Write for FixedByteStr<N> {
53    fn write_str(&mut self, s: &str) -> core::fmt::Result {
54        let len = s.len();
55        let remaining = N.saturating_sub(self.len); 
56
57        if len > remaining {
58            return Err(core::fmt::Error); 
59        }
60
61        self.raw[self.len..self.len + len].copy_from_slice(s.as_bytes());
62
63        self.len += len;
64        Ok(())
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use std::fmt::Write;
72
73    #[test]
74    fn test_str() {
75        let mut str = FixedByteStr::<11>::new();
76        str.write_str("Hello World").unwrap();
77        assert_eq!(str.as_str(), "Hello World");
78    }
79
80    #[test]
81    fn test_from_str() {
82        let str = FixedByteStr::<11>::from_str("Hello World");
83        assert_eq!(str.as_str(), "Hello World");
84    }
85
86    #[test]
87    fn test_str_overflow() {
88        let mut str = FixedByteStr::<11>::new();
89        str.write_str("Hello World").unwrap();
90        assert!(str.write_str("Hello World").is_err());
91    }
92
93    #[test]
94    fn test_str_display() {
95        let str = FixedByteStr::<11>::from_str("Hello World");
96        assert_eq!(format!("{}", str), "Hello World");
97    }
98
99    #[test]
100    #[should_panic]
101    fn test_str_from_str_overflow() {
102        _ = FixedByteStr::<11>::from_str("Hello World!");
103    }
104}