1use core::fmt::{self};
2use core::str;
3
4#[derive(Clone, Copy, PartialEq, Eq)]
23pub struct LiteStr<const N: usize> {
24 pub bytes: [u8; N],
25}
26
27impl<const N: usize> Default for LiteStr<N> {
28 #[inline(always)]
29 fn default() -> Self {
30 Self { bytes: [0; N] }
31 }
32}
33
34impl<const N: usize> LiteStr<N> {
35 pub const SIZE: usize = N;
36
37 #[inline(always)]
42 pub fn new(s: &str) -> Self {
43 let mut bytes = [0u8; N];
44 copy_valid_utf8_prefix(&mut bytes, s.as_bytes(), N);
45 Self { bytes }
46 }
47
48 #[inline(always)]
53 pub fn as_str(&self) -> Result<&str, LiteStrErr> {
54 let end = find_first_nul(&self.bytes);
55 str::from_utf8(&self.bytes[..end]).map_err(|_| LiteStrErr::CorruptedData)
56 }
57
58 #[inline(always)]
65 pub fn from_bytes(bytes: &[u8]) -> Self {
66 let mut arr = [0u8; N];
67 let len = bytes.len().min(N);
68 arr[..len].copy_from_slice(&bytes[..len]);
69 Self { bytes: arr }
70 }
71
72 #[inline(always)]
74 pub fn as_bytes(&self) -> &[u8] {
75 &self.bytes[..find_first_nul(&self.bytes)]
76 }
77}
78
79impl<const N: usize> fmt::Write for LiteStr<N> {
80 #[inline(never)]
81 fn write_str(&mut self, s: &str) -> fmt::Result {
82 let current = self.as_bytes().len();
83 let remaining = N.saturating_sub(current);
84 if remaining == 0 {
85 return Ok(());
86 }
87
88 copy_valid_utf8_prefix(&mut self.bytes[current..], s.as_bytes(), remaining);
89 Ok(())
90 }
91}
92
93impl<const N: usize> fmt::Debug for LiteStr<N> {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 match self.as_str() {
96 Ok(s) => write!(f, "{:?}", s),
97 Err(_) => f.write_str("LiteStr(<invalid utf-8>)"),
98 }
99 }
100}
101
102#[cfg(feature = "serde")]
103impl<const N: usize> serde::Serialize for LiteStr<N> {
104 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
105 where
106 S: serde::Serializer,
107 {
108 self.as_str()
109 .map_err(serde::ser::Error::custom)?
110 .serialize(serializer)
111 }
112}
113
114#[cfg(feature = "serde")]
115impl<'de, const N: usize> serde::Deserialize<'de> for LiteStr<N> {
116 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117 where
118 D: serde::Deserializer<'de>,
119 {
120 let s: &str = serde::Deserialize::deserialize(deserializer)?;
121 Ok(LiteStr::new(s))
122 }
123}
124
125#[inline(never)]
126fn find_first_nul(bytes: &[u8]) -> usize {
127 bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len())
128}
129
130#[inline(never)]
131fn copy_valid_utf8_prefix(dst: &mut [u8], src: &[u8], max_len: usize) -> usize {
132 let len = src.len().min(max_len);
133 match str::from_utf8(&src[..len]) {
134 Ok(_) => {
135 dst[..len].copy_from_slice(&src[..len]);
136 len
137 }
138 Err(e) => {
139 let valid = e.valid_up_to();
140 dst[..valid].copy_from_slice(&src[..valid]);
141 valid
142 }
143 }
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148pub enum LiteStrErr {
149 CorruptedData,
151}
152
153impl fmt::Display for LiteStrErr {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 match self {
156 LiteStrErr::CorruptedData => f.write_str("content is not valid UTF-8"),
157 }
158 }
159}
160
161impl core::error::Error for LiteStrErr {}