1#![no_std]
2#![warn(missing_docs)]
3
4use core::{borrow, cmp, error, fmt, hash, mem::MaybeUninit, ops, ptr, slice, str::FromStr};
9
10pub struct Line<const CAPACITY: usize> {
14 content: [MaybeUninit<u8>; CAPACITY],
15 len: u8,
16}
17
18pub type Line15 = Line<15>;
20pub type Line31 = Line<31>;
22pub type Line63 = Line<63>;
24pub type Line127 = Line<127>;
26pub type Line255 = Line<255>;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum Error {
32 NoSpace,
34}
35
36impl<const N: usize> Line<N> {
37 pub const fn new() -> Self {
39 assert!(N < 256);
40 Self {
41 content: [MaybeUninit::uninit(); N],
42 len: 0,
43 }
44 }
45 pub fn clear(&mut self) {
47 self.len = 0;
48 }
49 pub const fn capacity(&self) -> usize {
51 N
52 }
53 pub fn as_str(&self) -> &str {
55 unsafe {
57 core::str::from_utf8_unchecked(slice::from_raw_parts(
58 self.content.as_ptr().cast(),
59 self.len as usize,
60 ))
61 }
62 }
63 pub fn as_mut_str(&mut self) -> &mut str {
65 unsafe {
67 core::str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(
68 self.content.as_mut_ptr().cast(),
69 self.len as usize,
70 ))
71 }
72 }
73 pub fn push_str(&mut self, s: &str) -> Result<(), Error> {
75 assert!(N < 256);
76 let l = self.len as usize;
77 if l + s.len() <= N {
78 unsafe {
80 ptr::copy_nonoverlapping(
81 s.as_ptr(),
82 self.content[l..l + s.len()].as_mut_ptr().cast(),
83 s.len(),
84 );
85 }
86 self.len += s.len() as u8;
87 Ok(())
88 } else {
89 Err(Error::NoSpace)
90 }
91 }
92}
93impl<const N: usize> Clone for Line<N> {
94 fn clone(&self) -> Self {
95 let mut content = [MaybeUninit::uninit(); N];
96 unsafe {
98 ptr::copy_nonoverlapping(
99 self.content.as_ptr(),
100 content.as_mut_ptr(),
101 self.len as usize,
102 );
103 }
104 Self {
105 content,
106 len: self.len,
107 }
108 }
109}
110impl<const N: usize> FromStr for Line<N> {
111 type Err = Error;
112 fn from_str(s: &str) -> Result<Self, Error> {
113 let mut line = Self::new();
114 line.push_str(s)?;
115 Ok(line)
116 }
117}
118impl<const N: usize> ops::Deref for Line<N> {
119 type Target = str;
120 fn deref(&self) -> &Self::Target {
121 self.as_str()
122 }
123}
124impl<const N: usize> AsRef<str> for Line<N> {
125 fn as_ref(&self) -> &str {
126 self.as_str()
127 }
128}
129impl<const N: usize> AsMut<str> for Line<N> {
130 fn as_mut(&mut self) -> &mut str {
131 self.as_mut_str()
132 }
133}
134impl<const N: usize> borrow::Borrow<str> for Line<N> {
135 fn borrow(&self) -> &str {
136 self.as_str()
137 }
138}
139impl<const N: usize> borrow::BorrowMut<str> for Line<N> {
140 fn borrow_mut(&mut self) -> &mut str {
141 self.as_mut_str()
142 }
143}
144impl<const N: usize> TryFrom<&str> for Line<N> {
145 type Error = Error;
146 fn try_from(value: &str) -> Result<Self, Self::Error> {
147 Self::from_str(value)
148 }
149}
150impl<const N: usize> fmt::Write for Line<N> {
151 fn write_str(&mut self, s: &str) -> fmt::Result {
152 self.push_str(s).map_err(|_| fmt::Error)
153 }
154}
155impl<const N: usize> fmt::Debug for Line<N> {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 self.as_str().fmt(f)
158 }
159}
160impl<const N: usize> fmt::Display for Line<N> {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 self.as_str().fmt(f)
163 }
164}
165impl<const N: usize> Default for Line<N> {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170impl<const N: usize, const M: usize> PartialEq<Line<M>> for Line<N> {
171 fn eq(&self, other: &Line<M>) -> bool {
172 self.as_str() == other.as_str()
173 }
174}
175impl<const N: usize> Eq for Line<N> {}
176impl<const N: usize> PartialEq<str> for Line<N> {
177 fn eq(&self, other: &str) -> bool {
178 self.as_str() == other
179 }
180}
181impl<const N: usize> PartialEq<Line<N>> for str {
182 fn eq(&self, other: &Line<N>) -> bool {
183 self == other.as_str()
184 }
185}
186impl<'a, const N: usize> PartialEq<&'a str> for Line<N> {
187 fn eq(&self, other: &&'a str) -> bool {
188 self.as_str() == *other
189 }
190}
191impl<const N: usize> PartialEq<Line<N>> for &str {
192 fn eq(&self, other: &Line<N>) -> bool {
193 *self == other.as_str()
194 }
195}
196impl<const N: usize, const M: usize> PartialOrd<Line<M>> for Line<N> {
197 fn partial_cmp(&self, other: &Line<M>) -> Option<cmp::Ordering> {
198 Some(self.as_str().cmp(other.as_str()))
199 }
200}
201impl<const N: usize> Ord for Line<N> {
202 fn cmp(&self, other: &Self) -> cmp::Ordering {
203 self.as_str().cmp(other.as_str())
204 }
205}
206impl<const N: usize> PartialOrd<str> for Line<N> {
207 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
208 Some(self.as_str().cmp(other))
209 }
210}
211impl<const N: usize> PartialOrd<Line<N>> for str {
212 fn partial_cmp(&self, other: &Line<N>) -> Option<cmp::Ordering> {
213 Some(self.cmp(other.as_str()))
214 }
215}
216impl<'a, const N: usize> PartialOrd<&'a str> for Line<N> {
217 fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
218 Some(self.as_str().cmp(other))
219 }
220}
221impl<const N: usize> PartialOrd<Line<N>> for &str {
222 fn partial_cmp(&self, other: &Line<N>) -> Option<cmp::Ordering> {
223 Some(self.cmp(&other.as_str()))
224 }
225}
226impl<const N: usize> hash::Hash for Line<N> {
227 fn hash<H: hash::Hasher>(&self, state: &mut H) {
228 self.as_str().hash(state)
229 }
230}
231
232impl fmt::Display for Error {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 match self {
235 Self::NoSpace => f.write_str("not enough capacity"),
236 }
237 }
238}
239impl error::Error for Error {}
240
241#[cfg(test)]
242mod tests {
243 use core::fmt::Write;
244
245 use super::*;
246
247 #[test]
248 fn basics() {
249 let mut line = Line15::new();
250 write!(line, "{}", 1.5).unwrap();
251 assert_eq!("1.5", line);
252 assert!("1.6" > line);
253 let mut line2 = Line255::new();
254 write!(line2, "{line:?}").unwrap();
255 assert_eq!(line2, "\"1.5\"");
256 assert_eq!(line.clone(), line);
257 let line3 = Line31::from_str(&line2).unwrap();
258 assert_eq!(line2, line3);
259 assert!(Line15::from_str("b").unwrap() > Line63::from_str("a").unwrap());
260 assert_eq!(Line15::from_str("a").unwrap().as_bytes(), &[b'a']);
261 }
262
263 #[test]
264 fn maps() {
265 extern crate std;
266 use std::collections::{BTreeMap, HashMap};
267 let mut m = HashMap::new();
268 m.insert(Line15::from_str("test").unwrap(), 1234);
269 m.insert("elite".try_into().unwrap(), 1337);
270 assert_eq!(m.get("test"), Some(&1234));
271 let mut m = BTreeMap::new();
272 m.insert(Line15::from_str("test").unwrap(), 1234);
273 assert_eq!(m.get("test"), Some(&1234));
274 }
275
276 #[test]
277 #[should_panic]
278 fn invalid_capacity() {
279 let _: Line<256> = Line::new();
280 }
281}