1use core::fmt;
15use core::ops::Deref;
16
17#[derive(Clone, Copy)]
26pub struct Inline<T: Copy, const N: usize> {
27 items: [T; N],
28 len: usize,
29}
30
31impl<T: Copy, const N: usize> Inline<T, N> {
32 #[must_use]
34 pub const fn new(fill: T) -> Self {
35 Self {
36 items: [fill; N],
37 len: 0,
38 }
39 }
40
41 pub fn push(&mut self, item: T) -> Result<(), Full> {
47 let len = self.len;
51 if len >= N {
52 return Err(Full { capacity: N });
53 }
54 let slot = self.items.get_mut(len).ok_or(Full { capacity: N })?;
55 *slot = item;
56 self.len = len + 1;
57 Ok(())
58 }
59
60 pub fn insert(&mut self, index: usize, item: T) -> Result<(), Full> {
66 let len = self.len;
68 if len >= N {
69 return Err(Full { capacity: N });
70 }
71 let index = index.min(len);
72 let mut source = len;
74 while source > index {
75 source -= 1;
76 let value = *self.items.get(source).ok_or(Full { capacity: N })?;
77 *self.items.get_mut(source + 1).ok_or(Full { capacity: N })? = value;
78 }
79 *self.items.get_mut(index).ok_or(Full { capacity: N })? = item;
80 self.len = len + 1;
81 Ok(())
82 }
83
84 pub fn remove(&mut self, index: usize) -> Option<T> {
87 let len = self.len;
88 if index >= len {
89 return None;
90 }
91 let removed = *self.items.get(index)?;
92 let mut at = index;
94 while at + 1 < len {
95 let next = *self.items.get(at + 1)?;
96 *self.items.get_mut(at)? = next;
97 at += 1;
98 }
99 self.len = len - 1;
100 Some(removed)
101 }
102
103 #[must_use]
105 pub fn as_slice(&self) -> &[T] {
106 self.items.get(..self.len).unwrap_or(&[])
107 }
108
109 #[must_use]
111 pub fn as_mut_slice(&mut self) -> &mut [T] {
112 self.items.get_mut(..self.len).unwrap_or(&mut [])
113 }
114
115 #[must_use]
117 pub const fn len(&self) -> usize {
118 self.len
119 }
120
121 #[must_use]
123 pub const fn is_empty(&self) -> bool {
124 self.len == 0
125 }
126
127 #[must_use]
129 pub const fn capacity() -> usize {
130 N
131 }
132}
133
134impl<T: Copy, const N: usize> Deref for Inline<T, N> {
135 type Target = [T];
136
137 fn deref(&self) -> &[T] {
138 self.as_slice()
139 }
140}
141
142impl<T: Copy + fmt::Debug, const N: usize> fmt::Debug for Inline<T, N> {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 f.debug_list().entries(self.as_slice()).finish()
145 }
146}
147
148impl<T: Copy + PartialEq, const N: usize> PartialEq for Inline<T, N> {
149 fn eq(&self, other: &Self) -> bool {
151 self.as_slice() == other.as_slice()
152 }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub struct Full {
158 pub capacity: usize,
160}
161
162impl fmt::Display for Full {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 write!(f, "no room left in a store of {} items", self.capacity)
165 }
166}
167
168impl core::error::Error for Full {}
169
170#[derive(Clone, Copy, PartialEq, Eq)]
175pub struct InlineStr<const N: usize> {
176 bytes: [u8; N],
177 len: usize,
178}
179
180impl<const N: usize> InlineStr<N> {
181 #[must_use]
183 pub fn new(text: &str) -> Self {
184 let mut bytes = [0_u8; N];
185 let mut len = 0;
186 for (index, character) in text.char_indices() {
187 let end = index.saturating_add(character.len_utf8());
190 if end > N {
191 break;
192 }
193 len = end;
194 }
195 for (slot, byte) in bytes.iter_mut().zip(text.as_bytes().iter().take(len)) {
196 *slot = *byte;
197 }
198 Self { bytes, len }
199 }
200
201 #[must_use]
203 pub fn as_str(&self) -> &str {
204 self.bytes
205 .get(..self.len)
206 .and_then(|bytes| core::str::from_utf8(bytes).ok())
207 .unwrap_or("")
208 }
209
210 #[must_use]
212 pub const fn capacity() -> usize {
213 N
214 }
215}
216
217impl<const N: usize> fmt::Display for InlineStr<N> {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 f.write_str(self.as_str())
220 }
221}
222
223impl<const N: usize> fmt::Debug for InlineStr<N> {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 fmt::Debug::fmt(self.as_str(), f)
226 }
227}
228
229impl<const N: usize> PartialEq<str> for InlineStr<N> {
230 fn eq(&self, other: &str) -> bool {
231 self.as_str() == other
232 }
233}
234
235impl<const N: usize> PartialEq<&str> for InlineStr<N> {
236 fn eq(&self, other: &&str) -> bool {
237 self.as_str() == *other
238 }
239}
240
241impl<const N: usize> From<&str> for InlineStr<N> {
242 fn from(text: &str) -> Self {
243 Self::new(text)
244 }
245}
246
247#[cfg(feature = "serde")]
248impl<const N: usize> serde::Serialize for InlineStr<N> {
249 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
250 serializer.serialize_str(self.as_str())
251 }
252}
253
254#[cfg(feature = "serde")]
255impl<'de, const N: usize> serde::Deserialize<'de> for InlineStr<N> {
256 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
257 struct Visitor<const N: usize>;
258
259 impl<const N: usize> serde::de::Visitor<'_> for Visitor<N> {
260 type Value = InlineStr<N>;
261
262 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 write!(f, "a string of at most {N} bytes")
264 }
265
266 fn visit_str<E: serde::de::Error>(self, text: &str) -> Result<Self::Value, E> {
267 Ok(InlineStr::new(text))
268 }
269 }
270
271 deserializer.deserialize_str(Visitor::<N>)
272 }
273}
274
275#[cfg(test)]
276#[allow(clippy::unwrap_used)]
277mod tests {
278 use super::*;
279
280 #[test]
281 fn an_inline_store_fills_and_then_refuses() {
282 let mut store = Inline::<u8, 3>::new(0);
283 assert!(store.is_empty());
284 for value in 1..=3 {
285 store.push(value).unwrap();
286 }
287 assert_eq!(store.as_slice(), &[1, 2, 3]);
288 assert_eq!(store.push(4), Err(Full { capacity: 3 }));
289 assert_eq!(store.as_slice(), &[1, 2, 3]);
290 }
291
292 #[test]
293 fn insert_shifts_the_tail_along() {
294 let mut store = Inline::<u8, 4>::new(0);
295 store.push(1).unwrap();
296 store.push(3).unwrap();
297 store.insert(1, 2).unwrap();
298 assert_eq!(store.as_slice(), &[1, 2, 3]);
299 store.insert(99, 4).unwrap();
301 assert_eq!(store.as_slice(), &[1, 2, 3, 4]);
302 assert!(store.insert(0, 5).is_err());
303 }
304
305 #[test]
306 fn two_stores_are_equal_when_their_live_items_are() {
307 let mut first = Inline::<u8, 8>::new(0);
308 let mut second = Inline::<u8, 8>::new(9);
309 first.push(1).unwrap();
310 second.push(1).unwrap();
311 assert_eq!(first, second);
312 }
313
314 #[test]
315 fn an_inline_string_truncates_on_a_character_boundary() {
316 let short = InlineStr::<8>::new("north");
317 assert_eq!(short.as_str(), "north");
318 assert_eq!(short, "north");
319
320 let long = InlineStr::<4>::new("°°°");
322 assert_eq!(long.as_str(), "°°");
323
324 assert_eq!(InlineStr::<1>::new("°").as_str(), "");
327 }
328}