autocore_std/
fixed_string.rs1use core::fmt;
27
28#[derive(Clone, Copy, Eq, Hash)]
37#[repr(transparent)]
38pub struct FixedString<const N: usize>(pub [u8; N]);
39
40impl<const N: usize> FixedString<N> {
41 pub const fn new() -> Self {
43 Self([0u8; N])
44 }
45
46 pub fn set(&mut self, s: &str) {
48 self.0 = [0u8; N];
49 let bytes = s.as_bytes();
50 let mut copy_len = bytes.len().min(N);
51 while copy_len > 0 && !s.is_char_boundary(copy_len) {
53 copy_len -= 1;
54 }
55 self.0[..copy_len].copy_from_slice(&bytes[..copy_len]);
56 }
57
58 pub fn as_str(&self) -> &str {
63 let end = self.0.iter().position(|&b| b == 0).unwrap_or(N);
64 core::str::from_utf8(&self.0[..end]).unwrap_or("")
65 }
66
67 pub fn len(&self) -> usize {
69 self.0.iter().position(|&b| b == 0).unwrap_or(N)
70 }
71
72 pub fn is_empty(&self) -> bool {
74 self.0[0] == 0
75 }
76
77 pub fn clear(&mut self) {
79 self.0 = [0u8; N];
80 }
81
82 pub const fn capacity(&self) -> usize {
84 N
85 }
86}
87
88impl<const N: usize> Default for FixedString<N> {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94impl<const N: usize> PartialEq for FixedString<N> {
95 fn eq(&self, other: &Self) -> bool {
96 self.0 == other.0
97 }
98}
99
100impl<const N: usize> fmt::Debug for FixedString<N> {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 write!(f, "FixedString<{}>({:?})", N, self.as_str())
103 }
104}
105
106impl<const N: usize> fmt::Display for FixedString<N> {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.write_str(self.as_str())
109 }
110}
111
112impl<const N: usize> serde::Serialize for FixedString<N> {
117 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
118 serializer.serialize_str(self.as_str())
119 }
120}
121
122impl<'de, const N: usize> serde::Deserialize<'de> for FixedString<N> {
123 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
124 struct Visitor<const M: usize>;
125
126 impl<'de, const M: usize> serde::de::Visitor<'de> for Visitor<M> {
127 type Value = FixedString<M>;
128
129 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 write!(f, "a string of at most {} bytes", M)
131 }
132
133 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
134 let mut fs = FixedString::<M>::new();
135 fs.set(v);
136 Ok(fs)
137 }
138 }
139
140 deserializer.deserialize_str(Visitor::<N>)
141 }
142}
143
144impl<const N: usize> From<&str> for FixedString<N> {
149 fn from(s: &str) -> Self {
150 let mut fs = Self::new();
151 fs.set(s);
152 fs
153 }
154}
155
156#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn new_is_empty() {
166 let s = FixedString::<64>::new();
167 assert!(s.is_empty());
168 assert_eq!(s.len(), 0);
169 assert_eq!(s.as_str(), "");
170 }
171
172 #[test]
173 fn set_and_read() {
174 let mut s = FixedString::<64>::new();
175 s.set("Hello, world!");
176 assert_eq!(s.as_str(), "Hello, world!");
177 assert_eq!(s.len(), 13);
178 assert!(!s.is_empty());
179 }
180
181 #[test]
182 fn truncation_at_capacity() {
183 let mut s = FixedString::<4>::new();
184 s.set("Hello");
185 assert_eq!(s.as_str(), "Hell");
186 assert_eq!(s.len(), 4);
187 }
188
189 #[test]
190 fn truncation_at_utf8_boundary() {
191 let mut s = FixedString::<5>::new();
193 s.set("ab€x");
194 assert_eq!(s.as_str(), "ab€");
196
197 let mut s = FixedString::<4>::new();
198 s.set("ab€");
199 assert_eq!(s.as_str(), "ab");
201 }
202
203 #[test]
204 fn clear() {
205 let mut s = FixedString::<64>::new();
206 s.set("test");
207 assert!(!s.is_empty());
208 s.clear();
209 assert!(s.is_empty());
210 assert_eq!(s.as_str(), "");
211 }
212
213 #[test]
214 fn exact_capacity_fill() {
215 let mut s = FixedString::<5>::new();
216 s.set("abcde");
217 assert_eq!(s.as_str(), "abcde");
218 assert_eq!(s.len(), 5);
219 }
220
221 #[test]
222 fn overwrite_shorter() {
223 let mut s = FixedString::<64>::new();
224 s.set("Hello, world!");
225 s.set("Hi");
226 assert_eq!(s.as_str(), "Hi");
227 assert_eq!(s.len(), 2);
228 }
229
230 #[test]
231 fn equality() {
232 let a = FixedString::<64>::from("test");
233 let b = FixedString::<64>::from("test");
234 let c = FixedString::<64>::from("other");
235 assert_eq!(a, b);
236 assert_ne!(a, c);
237 }
238
239 #[test]
240 fn display() {
241 let s = FixedString::<64>::from("display me");
242 assert_eq!(format!("{}", s), "display me");
243 }
244
245 #[test]
246 fn debug() {
247 let s = FixedString::<32>::from("debug me");
248 let dbg = format!("{:?}", s);
249 assert!(dbg.contains("FixedString<32>"));
250 assert!(dbg.contains("debug me"));
251 }
252
253 #[test]
254 fn serde_roundtrip() {
255 let original = FixedString::<64>::from("serde test");
256 let json = serde_json::to_string(&original).unwrap();
257 assert_eq!(json, "\"serde test\"");
258
259 let restored: FixedString<64> = serde_json::from_str(&json).unwrap();
260 assert_eq!(original, restored);
261 }
262
263 #[test]
264 fn serde_truncation() {
265 let json = "\"this string is longer than eight bytes\"";
266 let s: FixedString<8> = serde_json::from_str(json).unwrap();
267 assert_eq!(s.as_str(), "this str");
268 }
269
270 #[test]
271 fn default_is_empty() {
272 let s = FixedString::<64>::default();
273 assert!(s.is_empty());
274 }
275
276 #[test]
277 fn copy_semantics() {
278 let a = FixedString::<64>::from("copy me");
279 let b = a; assert_eq!(a.as_str(), "copy me");
281 assert_eq!(b.as_str(), "copy me");
282 }
283
284 #[test]
285 fn capacity() {
286 let s = FixedString::<128>::new();
287 assert_eq!(s.capacity(), 128);
288 }
289}