1use std::fmt;
23use std::fmt::{Debug, Display};
24use std::marker::PhantomData;
25
26#[derive(Clone, Copy, Hash, Eq, Ord, PartialEq, PartialOrd)]
27pub struct FixStr<const N: usize> {
28 inline: [u8; N],
29 len: u8,
30 _marker: PhantomData<[u8; N]>,
31}
32
33impl<const N: usize> Debug for FixStr<N> {
34 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35 write!(f, "FixStr(\"{}\")", self.as_str())
36 }
37}
38
39impl<const N: usize> Default for FixStr<N> {
40 fn default() -> Self {
41 Self {
42 inline: [0; N],
43 len: 0,
44 _marker: PhantomData,
45 }
46 }
47}
48
49impl<const N: usize> FixStr<N> {
50 #[must_use]
54 pub fn new(s: &str) -> Option<Self> {
55 if s.len() > N || s.len() > u8::MAX as usize {
56 return None;
57 }
58
59 let mut buffer = [0u8; N];
63 buffer[..s.len()].copy_from_slice(s.as_bytes());
64
65 u8::try_from(s.len()).ok().map(|len| Self {
66 inline: buffer,
67 len,
68 _marker: PhantomData,
69 })
70 }
71
72 #[must_use]
77 pub fn new_unchecked(s: &str) -> Self {
78 Self::new(s)
79 .unwrap_or_else(|| panic!("String '{s}' (len={}) exceeds capacity {N}", s.len()))
80 }
81
82 #[must_use]
87 pub fn as_str(&self) -> &str {
88 unsafe { std::str::from_utf8_unchecked(&self.inline[..self.len as usize]) }
90 }
91
92 #[must_use]
96 pub fn char_len(&self) -> usize {
97 self.as_str().chars().count()
98 }
99
100 #[must_use]
102 pub fn len(&self) -> usize {
103 self.len as usize
104 }
105
106 #[must_use]
108 pub fn is_empty(&self) -> bool {
109 self.len() == 0
110 }
111
112 #[must_use]
114 pub fn capacity(&self) -> usize {
115 N
116 }
117}
118
119impl<const N: usize> TryFrom<&str> for FixStr<N> {
120 type Error = String;
121
122 fn try_from(s: &str) -> Result<Self, Self::Error> {
123 Self::new(s).ok_or(format!(
124 "String '{s}' (len={}) exceeds capacity {N}",
125 s.len()
126 ))
127 }
128}
129
130impl<const N: usize> TryFrom<String> for FixStr<N> {
131 type Error = String;
132
133 fn try_from(s: String) -> Result<Self, Self::Error> {
134 Self::try_from(s.as_str())
135 }
136}
137
138impl<const N: usize> From<FixStr<N>> for String {
139 fn from(s: FixStr<N>) -> Self {
140 String::from(s.as_str())
141 }
142}
143
144impl<const N: usize> AsRef<str> for FixStr<N> {
145 fn as_ref(&self) -> &str {
146 self.as_str()
147 }
148}
149
150impl<const N: usize> fmt::Display for FixStr<N> {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 write!(f, "{}", self.as_str())
153 }
154}