lowlevel_types/ascii/
fixedlengthstring.rs

1use std::fmt::Display;
2
3use serde::{Deserialize, Deserializer, Serialize, de::Visitor};
4
5use crate::ascii::{char::Char, error::ASCIIError};
6
7/// A fixed length ASCII string of length N
8#[derive(Clone, Debug, Hash, PartialOrd)]
9pub struct FixedLengthString<const N: usize>(pub [Char; N]);
10
11impl<const N: usize> FixedLengthString<N> {
12	/// Constructs a new instance
13	pub fn new() -> Self {
14		Self([Char(0x00); N])
15	}
16
17	/// Returns the length of the string (in bytes)
18	pub fn len(&self) -> usize {
19		self.0.len()
20	}
21
22	/// Returns the raw bytes of the string
23	pub fn as_bytes(&self) -> [u8; N] {
24		self.0.map(|c| u8::from(c)).clone()
25	}
26}
27
28impl<const N: usize> Display for FixedLengthString<N> {
29	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30		self.0
31			.iter()
32			.for_each(|c| f.write_str(c.char().to_string().as_str()).unwrap());
33		Ok(())
34	}
35}
36
37impl<const N: usize> PartialEq<FixedLengthString<N>> for FixedLengthString<N> {
38	fn eq(&self, other: &FixedLengthString<N>) -> bool {
39		self.0 == other.0
40	}
41}
42
43impl<const N: usize> PartialEq<&str> for FixedLengthString<N> {
44	fn eq(&self, other: &&str) -> bool {
45		let s = String::from(self.clone());
46		s.as_str() == *other
47	}
48}
49
50impl<const N: usize> From<Vec<u8>> for FixedLengthString<N> {
51	fn from(value: Vec<u8>) -> Self {
52		FixedLengthString::<N>::try_from(value.as_slice()).unwrap()
53	}
54}
55
56impl<const N: usize> TryFrom<&[u8]> for FixedLengthString<N> {
57	type Error = ASCIIError;
58
59	fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
60		if value.len() > N {
61			Err(ASCIIError {
62				message: format!(
63					"array &[u8] of length {} too long for AString<{}>",
64					value.len(),
65					N
66				),
67			})
68		} else {
69			let v: &mut [Char; N] = &mut [Char(0x00); N];
70			for i in 0..value.len() {
71				v[i] = Char(value[i].clone());
72			}
73			Ok(FixedLengthString(v.clone()))
74		}
75	}
76}
77
78impl<const N: usize> From<[u8; N]> for FixedLengthString<N> {
79	fn from(value: [u8; N]) -> Self {
80		FixedLengthString(value.map(|c| Char(c)))
81	}
82}
83
84impl<const N: usize> TryFrom<&String> for FixedLengthString<N> {
85	type Error = ASCIIError;
86
87	fn try_from(value: &String) -> Result<Self, Self::Error> {
88		FixedLengthString::<N>::try_from(value.as_str())
89	}
90}
91
92impl<const N: usize> TryFrom<&str> for FixedLengthString<N> {
93	type Error = ASCIIError;
94
95	fn try_from(value: &str) -> Result<Self, Self::Error> {
96		if !value.is_ascii() {
97			Err(ASCIIError {
98				message: format!("attempt to convert an Unicode string to an AString"),
99			})
100		} else {
101			let v: &mut Vec<u8> = &mut Vec::new();
102			for c in value.chars() {
103				v.push(c as u8);
104			}
105			Ok(FixedLengthString::from(v.clone()))
106		}
107	}
108}
109
110impl<const N: usize> From<FixedLengthString<N>> for String {
111	fn from(value: FixedLengthString<N>) -> Self {
112		let s = &mut String::new();
113		for c in value.0 {
114			s.push(c.char());
115		}
116		s.clone()
117	}
118}
119
120#[cfg(feature = "serde")]
121impl<const N: usize> Serialize for FixedLengthString<N> {
122	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
123	where
124		S: serde::Serializer,
125	{
126		let bytes = &self.as_bytes() as &[u8];
127		serializer.serialize_bytes(bytes)
128	}
129}
130
131#[cfg(feature = "serde")]
132impl<'de, const N: usize> Deserialize<'de> for FixedLengthString<N> {
133	fn deserialize<D>(deserializer: D) -> Result<FixedLengthString<N>, D::Error>
134	where
135		D: Deserializer<'de>,
136	{
137		deserializer.deserialize_bytes(AStringVisitor::<N>)
138	}
139}
140
141#[cfg(feature = "serde")]
142pub struct AStringVisitor<const N: usize>;
143
144#[cfg(feature = "serde")]
145impl<'de, const N: usize> Visitor<'de> for AStringVisitor<N> {
146	type Value = FixedLengthString<N>;
147
148	fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
149		formatter.write_str(format!("an array of {} ASCII bytes", N).as_str())
150	}
151
152	fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
153	where
154		E: serde::de::Error,
155	{
156		if v.len() != N {
157			Err(serde::de::Error::invalid_length(v.len(), &self))
158		} else {
159			Ok(FixedLengthString::try_from(v).unwrap())
160		}
161	}
162}
163
164#[cfg(test)]
165mod tests {
166	use crate::ascii::{self, char::Char, fixedlengthstring::FixedLengthString};
167
168	#[test]
169	fn test_create() {
170		let s1 = FixedLengthString([Char(0x00)]);
171		assert_eq!(s1.len(), 1);
172		assert_eq!(s1, "\0");
173		let s: FixedLengthString<10> = FixedLengthString::new();
174		assert_eq!(s.len(), 10);
175	}
176
177	#[test]
178	fn test_from_vec_of_u8() {
179		let v: Vec<u8> = vec![0x41 as u8];
180		let s1: FixedLengthString<1> = FixedLengthString::from(v);
181		assert_eq!(s1, "A");
182	}
183
184	#[test]
185	fn test_from_array_of_u8() {
186		let s1: FixedLengthString<1> = FixedLengthString::try_from(&[0x41 as u8] as &[u8]).unwrap();
187		assert_eq!(s1, "A");
188		let s2: FixedLengthString<1> = FixedLengthString::from([0x41 as u8; 1]);
189		assert_eq!(s2, "A");
190		assert!(FixedLengthString::<1>::try_from(&[0x41 as u8; 2] as &[u8]).is_err());
191	}
192
193	#[test]
194	fn test_from_string() {
195		let s1: FixedLengthString<1> = FixedLengthString::try_from(&String::from("A")).unwrap();
196		assert_eq!(s1, "A");
197		assert!(FixedLengthString::<1>::try_from(&String::from("👿")).is_err());
198	}
199
200	#[test]
201	fn test_string_try_from_astring() {
202		let s4 = String::from(FixedLengthString::<1>::try_from("A").unwrap());
203		assert_eq!(s4, "A");
204	}
205
206	#[test]
207	fn test_display() {
208		let s = FixedLengthString([ascii::Char(0x41); 1]);
209		assert_eq!(format!("{}", s), "A");
210	}
211}