1use core::ops::{Deref, DerefMut};
2
3#[cfg(feature = "serde")]
4use crate::utils::nulstr;
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8#[cfg(feature = "arbitrary")]
9use arbitrary::{Arbitrary, Unstructured};
10
11#[cfg_attr(feature = "serde", derive(Serialize))]
30#[cfg_attr(feature = "serde", serde(transparent))]
31#[derive(Debug, PartialEq, Clone, Copy)]
32pub struct CharArray<const N: usize> {
33    #[cfg_attr(
34        feature = "serde",
35        serde(serialize_with = "nulstr::serialize::<_, N>",)
36    )]
37    data: [u8; N],
38
39    #[cfg_attr(feature = "serde", serde(skip))]
40    str_len: usize,
41}
42
43impl<const N: usize> CharArray<N> {
44    pub const fn new(data: [u8; N]) -> Self {
45        let mut first_null = N;
48        let mut i = 0;
49        loop {
50            if i >= N {
51                break;
52            }
53            if data[i] == 0 {
54                first_null = i;
55                break;
56            }
57            i += 1;
58        }
59        Self {
60            data,
61            str_len: first_null,
62        }
63    }
64
65    pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
69        core::str::from_utf8(&self.data[..self.str_len])
70    }
71}
72
73impl<const N: usize> Deref for CharArray<N> {
74    type Target = [u8; N];
75
76    fn deref(&self) -> &Self::Target {
77        &self.data
78    }
79}
80
81impl<const N: usize> DerefMut for CharArray<N> {
82    fn deref_mut(&mut self) -> &mut Self::Target {
83        &mut self.data
84    }
85}
86
87impl<'a, const N: usize> IntoIterator for &'a CharArray<N> {
88    type Item = &'a u8;
89    type IntoIter = core::slice::Iter<'a, u8>;
90
91    fn into_iter(self) -> Self::IntoIter {
92        self.data.iter()
93    }
94}
95
96impl<const N: usize> From<[u8; N]> for CharArray<N> {
97    fn from(data: [u8; N]) -> Self {
98        Self::new(data)
99    }
100}
101
102impl<const N: usize> From<CharArray<N>> for [u8; N] {
103    fn from(value: CharArray<N>) -> Self {
104        value.data
105    }
106}
107
108impl<const N: usize> From<&str> for CharArray<N> {
109    fn from(s: &str) -> Self {
110        let mut data = [0u8; N];
111        let bytes = s.as_bytes();
112        let len = bytes.len().min(N);
113        data[..len].copy_from_slice(&bytes[..len]);
114        Self::new(data)
115    }
116}
117
118impl<const N: usize> crate::utils::RustDefault for CharArray<N> {
119    #[inline(always)]
120    fn rust_default() -> Self {
121        Self::new([0u8; N])
122    }
123}
124
125#[cfg(feature = "serde")]
126impl<'de, const N: usize> Deserialize<'de> for CharArray<N> {
127    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
128    where
129        D: serde::Deserializer<'de>,
130    {
131        let data: [u8; N] = nulstr::deserialize(deserializer)?;
132        Ok(Self::new(data))
133    }
134}
135
136#[cfg(feature = "arbitrary")]
137impl<'a, const N: usize> Arbitrary<'a> for CharArray<N> {
138    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
139        let mut data = [0u8; N];
140        u.fill_buffer(&mut data)?;
141        Ok(CharArray::new(data))
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::CharArray;
148
149    #[test]
150    fn char_array_to_str_handles_no_nulls() {
151        let data = *b"HELLOWORLD";
152        let ca = CharArray::new(data);
153        assert_eq!(ca.len(), 10);
154        assert_eq!(ca.to_str().unwrap(), "HELLOWORLD");
155    }
156
157    #[test]
158    fn char_array_to_str_trims_after_first_null() {
159        let mut data = [0u8; 10];
160        data[..3].copy_from_slice(b"abc");
161        let ca = CharArray::new(data);
163        assert_eq!(ca.len(), 10);
164        assert_eq!(ca.to_str().unwrap(), "abc");
165    }
166
167    #[test]
168    fn char_array_from_str_into_str() {
169        let ca: CharArray<10> = "HELLOWORLD".into();
170        assert_eq!(ca.len(), 10);
171        assert_eq!(ca.to_str().unwrap(), "HELLOWORLD");
172
173        let ca: CharArray<10> = "abc".into();
174        assert_eq!(ca.len(), 10);
175        assert_eq!(ca.to_str().unwrap(), "abc");
176    }
177}