1#![deny(clippy::doc_markdown)]
39#![deny(missing_docs)]
40
41use core::str;
42use std::{
43 borrow::{Borrow, Cow},
44 cmp::Ordering,
45 ffi::OsStr,
46 ops::Deref,
47 path::Path,
48};
49
50#[cfg(feature = "serde")]
51mod serde;
52
53use inline_array::InlineArray;
54
55#[derive(PartialEq, Eq, Clone)]
57pub struct InlineStr {
58 inner: InlineArray,
59}
60
61impl InlineStr {
62 pub fn as_str(&self) -> &str {
64 unsafe { str::from_utf8_unchecked(&self.inner) }
67 }
68
69 pub fn len(&self) -> usize {
71 self.inner.len()
72 }
73
74 pub fn is_empty(&self) -> bool {
76 self.len() == 0
77 }
78}
79
80impl std::fmt::Display for InlineStr {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 std::fmt::Display::fmt(&**self, f)
83 }
84}
85
86impl std::fmt::Debug for InlineStr {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 std::fmt::Debug::fmt(&**self, f)
89 }
90}
91
92impl std::hash::Hash for InlineStr {
93 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
94 let as_str = &**self;
95 as_str.hash(state);
96 }
97}
98
99impl From<String> for InlineStr {
100 fn from(value: String) -> Self {
101 Self {
102 inner: InlineArray::from(value.as_bytes()),
103 }
104 }
105}
106
107impl From<&String> for InlineStr {
108 fn from(value: &String) -> Self {
109 Self {
110 inner: InlineArray::from(value.as_bytes()),
111 }
112 }
113}
114
115impl From<&str> for InlineStr {
116 fn from(value: &str) -> Self {
117 Self {
118 inner: InlineArray::from(value.as_bytes()),
119 }
120 }
121}
122
123impl PartialOrd for InlineStr {
124 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
125 Some(self.cmp(other))
126 }
127}
128
129impl Ord for InlineStr {
130 fn cmp(&self, other: &Self) -> Ordering {
131 self.as_str().cmp(other.as_str())
132 }
133}
134
135impl PartialEq<String> for InlineStr {
136 fn eq(&self, other: &String) -> bool {
137 self.as_str() == other
138 }
139}
140
141impl PartialEq<InlineStr> for String {
142 fn eq(&self, other: &InlineStr) -> bool {
143 self.as_str() == other.as_str()
144 }
145}
146
147impl PartialEq<&'_ str> for InlineStr {
148 fn eq(&self, other: &&str) -> bool {
149 self.as_str() == *other
150 }
151}
152
153impl PartialEq<InlineStr> for &str {
154 fn eq(&self, other: &InlineStr) -> bool {
155 *self == other.as_str()
156 }
157}
158
159impl PartialEq<&InlineStr> for &str {
160 fn eq(&self, other: &&InlineStr) -> bool {
161 self == *other
162 }
163}
164impl PartialEq<Cow<'_, str>> for InlineStr {
165 fn eq(&self, other: &Cow<'_, str>) -> bool {
166 self.as_str() == other
167 }
168}
169
170impl PartialEq<InlineStr> for Cow<'_, str> {
171 fn eq(&self, other: &InlineStr) -> bool {
172 self.as_ref() == other.as_str()
173 }
174}
175
176impl PartialEq<InlineStr> for &InlineStr {
177 fn eq(&self, other: &InlineStr) -> bool {
178 self.as_str() == other.as_str()
179 }
180}
181
182impl Deref for InlineStr {
183 type Target = str;
184
185 fn deref(&self) -> &Self::Target {
186 self.as_str()
187 }
188}
189
190impl AsRef<str> for InlineStr {
191 fn as_ref(&self) -> &str {
192 self
193 }
194}
195
196impl AsRef<Path> for InlineStr {
197 fn as_ref(&self) -> &Path {
198 self.as_str().as_ref()
199 }
200}
201
202impl AsRef<[u8]> for InlineStr {
203 fn as_ref(&self) -> &[u8] {
204 self.inner.as_ref()
205 }
206}
207
208impl AsRef<OsStr> for InlineStr {
209 fn as_ref(&self) -> &OsStr {
210 self.as_str().as_ref()
211 }
212}
213
214impl Borrow<str> for InlineStr {
215 fn borrow(&self) -> &str {
216 self.as_ref()
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use std::{
223 collections::HashMap,
224 hash::{BuildHasher, RandomState},
225 };
226
227 use super::*;
228
229 #[test]
230 fn test_basic_eq() {
231 let words = "the quick brown fox";
232 let inline_words = InlineStr::from(words);
233
234 assert_eq!(words, &*inline_words);
235 assert_eq!(words, inline_words);
236 assert_eq!(inline_words, words);
237 }
238
239 #[test]
240 fn test_basic_hash() {
241 let hasher = RandomState::new();
242
243 let words = "the quick brown fox";
244 let inline_words = InlineStr::from(words);
245
246 let words_hash = hasher.hash_one(words);
247 let words_hash_2 = hasher.hash_one(words);
248 let inline_hash = hasher.hash_one(inline_words);
249
250 assert_eq!(words_hash, words_hash_2);
251 assert_eq!(words_hash, inline_hash);
252 }
253
254 #[test]
255 fn test_borrow() {
256 let map = [(InlineStr::from("x"), 5)]
257 .into_iter()
258 .collect::<HashMap<InlineStr, i32>>();
259
260 let v = map.get("x");
261 assert_eq!(v, Some(&5));
262 }
263
264 #[cfg(feature = "serde")]
265 #[test]
266 fn test_serde() {
267 let s = "hello world";
268 let inline_s = InlineStr::from("hello world");
269 assert_eq!(s, inline_s);
270 let serialized_s = serde_json::to_value(s).unwrap();
271 let serialized_inline = serde_json::to_value(inline_s.as_str()).unwrap();
272 assert_eq!(serialized_s, serialized_inline);
273 let deserialized: InlineStr = serde_json::from_value(serialized_s).unwrap();
274 assert_eq!(deserialized, "hello world");
275 }
276}