1use crate::ByteView;
6use std::{ops::Deref, sync::Arc};
7
8#[repr(C)]
18#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
19pub struct StrView(ByteView);
20
21impl std::fmt::Display for StrView {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 write!(f, "{}", &**self)
24 }
25}
26
27impl std::fmt::Debug for StrView {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "{:?}", &**self)
30 }
31}
32
33impl Deref for StrView {
34 type Target = str;
35
36 fn deref(&self) -> &Self::Target {
37 unsafe { std::str::from_utf8_unchecked(&self.0) }
39 }
40}
41
42impl std::hash::Hash for StrView {
43 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
44 self.deref().hash(state);
45 }
46}
47
48impl StrView {
49 #[must_use]
57 pub fn new(s: &str) -> Self {
58 Self(ByteView::new(s.as_bytes()))
59 }
60
61 #[doc(hidden)]
62 #[must_use]
63 #[allow(clippy::missing_const_for_fn)]
64 pub unsafe fn from_raw(view: ByteView) -> Self {
65 Self(view)
66 }
67
68 #[must_use]
70 pub fn to_detached(&self) -> Self {
71 Self::new(self)
72 }
73
74 #[must_use]
76 pub fn slice(&self, range: impl std::ops::RangeBounds<usize>) -> Self {
77 Self(self.0.slice(range))
78 }
79
80 #[must_use]
82 pub fn is_empty(&self) -> bool {
83 self.0.is_empty()
84 }
85
86 #[must_use]
88 pub fn len(&self) -> usize {
89 self.0.len()
90 }
91
92 #[must_use]
94 pub fn starts_with(&self, needle: &str) -> bool {
95 self.0.starts_with(needle.as_bytes())
96 }
97}
98
99impl std::borrow::Borrow<str> for StrView {
100 fn borrow(&self) -> &str {
101 self
102 }
103}
104
105impl AsRef<str> for StrView {
106 fn as_ref(&self) -> &str {
107 self
108 }
109}
110
111impl From<&str> for StrView {
112 fn from(value: &str) -> Self {
113 Self::new(value)
114 }
115}
116
117impl From<String> for StrView {
118 fn from(value: String) -> Self {
119 Self::new(&value)
120 }
121}
122
123impl From<Arc<str>> for StrView {
124 fn from(value: Arc<str>) -> Self {
125 Self::new(&value)
126 }
127}
128
129impl TryFrom<ByteView> for StrView {
130 type Error = std::str::Utf8Error;
131
132 fn try_from(value: ByteView) -> Result<Self, Self::Error> {
133 std::str::from_utf8(&value)?;
134 Ok(Self(value))
135 }
136}
137
138impl From<StrView> for ByteView {
139 fn from(val: StrView) -> Self {
140 val.0
141 }
142}
143
144#[cfg(feature = "serde")]
145mod serde {
146 use super::StrView;
147 use serde::de::{self, Visitor};
148 use serde::{Deserialize, Deserializer, Serialize, Serializer};
149 use std::fmt;
150
151 impl Serialize for StrView {
152 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
153 where
154 S: Serializer,
155 {
156 serializer.serialize_str(self.as_ref())
157 }
158 }
159
160 impl<'de> Deserialize<'de> for StrView {
161 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
162 where
163 D: Deserializer<'de>,
164 {
165 struct StrViewVisitor;
166
167 impl Visitor<'_> for StrViewVisitor {
168 type Value = StrView;
169
170 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
171 formatter.write_str("a string")
172 }
173
174 fn visit_str<E>(self, v: &str) -> Result<StrView, E>
175 where
176 E: de::Error,
177 {
178 Ok(StrView::new(v))
179 }
180 }
181
182 deserializer.deserialize_str(StrViewVisitor)
183 }
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::StrView;
190 use std::collections::HashMap;
191
192 #[cfg(feature = "serde")]
193 #[test]
194 fn serde_roundtrip() -> serde_json::Result<()> {
195 let a = StrView::from("abcdef");
196 let b: StrView = serde_json::from_slice(&serde_json::to_vec(&a)?)?;
197 assert_eq!(a, b);
198 Ok(())
199 }
200
201 #[test]
202 fn strview_hash() {
203 let a = StrView::from("abcdef");
204
205 let mut map = HashMap::new();
206 map.insert(a, 0);
207 assert!(map.contains_key("abcdef"));
208 }
209
210 #[test]
211 fn cmp_misc_1() {
212 let a = StrView::from("abcdef");
213 let b = StrView::from("abcdefhelloworldhelloworld");
214 assert!(a < b);
215 }
216
217 #[test]
218 fn nostr() {
219 let slice = StrView::from("");
220 assert_eq!(0, slice.len());
221 assert_eq!(&*slice, "");
222 }
223
224 #[test]
225 fn default_str() {
226 let slice = StrView::default();
227 assert_eq!(0, slice.len());
228 assert_eq!(&*slice, "");
229 }
230
231 #[test]
232 fn short_str() {
233 let slice = StrView::from("abcdef");
234 assert_eq!(6, slice.len());
235 assert_eq!(&*slice, "abcdef");
236 }
237
238 #[test]
239 #[cfg(target_pointer_width = "64")]
240 fn medium_str() {
241 let slice = StrView::from("abcdefabcdef");
242 assert_eq!(12, slice.len());
243 assert_eq!(&*slice, "abcdefabcdef");
244 }
245
246 #[test]
247 #[cfg(target_pointer_width = "64")]
248 fn medium_long_str() {
249 let slice = StrView::from("abcdefabcdefabcdabcd");
250 assert_eq!(20, slice.len());
251 assert_eq!(&*slice, "abcdefabcdefabcdabcd");
252 }
253
254 #[test]
255 #[cfg(target_pointer_width = "64")]
256 fn medium_str_clone() {
257 let slice = StrView::from("abcdefabcdefabcdefa");
258
259 #[allow(clippy::redundant_clone)]
260 let copy = slice.clone();
261
262 assert_eq!(slice, copy);
263 }
264
265 #[test]
266 fn long_str() {
267 let slice = StrView::from("abcdefabcdefabcdefababcd");
268 assert_eq!(24, slice.len());
269 assert_eq!(&*slice, "abcdefabcdefabcdefababcd");
270 }
271
272 #[test]
273 fn long_str_clone() {
274 let slice = StrView::from("abcdefabcdefabcdefababcd");
275
276 #[allow(clippy::redundant_clone)]
277 let copy = slice.clone();
278
279 assert_eq!(slice, copy);
280 }
281
282 #[test]
283 fn long_str_slice_full() {
284 let slice = StrView::from("helloworld_thisisalongstring");
285
286 let copy = slice.slice(..);
287 assert_eq!(copy, slice);
288 }
289
290 #[test]
291 #[cfg(target_pointer_width = "64")]
292 fn long_str_slice() {
293 let slice = StrView::from("helloworld_thisisalongstring");
294
295 let copy = slice.slice(11..);
296 assert_eq!("thisisalongstring", &*copy);
297 }
298
299 #[test]
300 #[cfg(target_pointer_width = "64")]
301 fn long_str_slice_twice() {
302 let slice = StrView::from("helloworld_thisisalongstring");
303
304 let copy = slice.slice(11..);
305 assert_eq!("thisisalongstring", &*copy);
306
307 let copycopy = copy.slice(..);
308 assert_eq!(copy, copycopy);
309 }
310
311 #[test]
312 #[cfg(target_pointer_width = "64")]
313 fn long_str_slice_downgrade() {
314 let slice = StrView::from("helloworld_thisisalongstring");
315
316 let copy = slice.slice(11..);
317 assert_eq!("thisisalongstring", &*copy);
318
319 let copycopy = copy.slice(0..4);
320 assert_eq!("this", &*copycopy);
321
322 {
323 let copycopy = copy.slice(0..=4);
324 assert_eq!("thisi", &*copycopy);
325 assert_eq!(Some('t'), copycopy.chars().next());
326 }
327 }
328
329 #[test]
330 fn short_str_clone() {
331 let slice = StrView::from("abcdef");
332 let copy = slice.clone();
333 assert_eq!(slice, copy);
334
335 drop(slice);
336 assert_eq!(&*copy, "abcdef");
337 }
338
339 #[test]
340 fn short_str_slice_full() {
341 let slice = StrView::from("abcdef");
342 let copy = slice.slice(..);
343 assert_eq!(slice, copy);
344
345 drop(slice);
346 assert_eq!(&*copy, "abcdef");
347 }
348
349 #[test]
350 fn short_str_slice_part() {
351 let slice = StrView::from("abcdef");
352 let copy = slice.slice(3..);
353
354 drop(slice);
355 assert_eq!(&*copy, "def");
356 }
357
358 #[test]
359 fn short_str_slice_empty() {
360 let slice = StrView::from("abcdef");
361 let copy = slice.slice(0..0);
362
363 drop(slice);
364 assert_eq!(&*copy, "");
365 }
366
367 #[test]
368 fn tiny_str_starts_with() {
369 let a = StrView::from("abc");
370 assert!(a.starts_with("ab"));
371 assert!(!a.starts_with("b"));
372 }
373
374 #[test]
375 fn long_str_starts_with() {
376 let a = StrView::from("abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef");
377 assert!(a.starts_with("abcdef"));
378 assert!(!a.starts_with("def"));
379 }
380
381 #[test]
382 fn tiny_str_cmp() {
383 let a = StrView::from("abc");
384 let b = StrView::from("def");
385 assert!(a < b);
386 }
387
388 #[test]
389 fn tiny_str_eq() {
390 let a = StrView::from("abc");
391 let b = StrView::from("def");
392 assert_ne!(a, b);
393 }
394
395 #[test]
396 fn long_str_eq() {
397 let a = StrView::from("abcdefabcdefabcdefabcdef");
398 let b = StrView::from("xycdefabcdefabcdefabcdef");
399 assert_ne!(a, b);
400 }
401
402 #[test]
403 fn long_str_cmp() {
404 let a = StrView::from("abcdefabcdefabcdefabcdef");
405 let b = StrView::from("xycdefabcdefabcdefabcdef");
406 assert!(a < b);
407 }
408
409 #[test]
410 fn long_str_eq_2() {
411 let a = StrView::from("abcdefabcdefabcdefabcdef");
412 let b = StrView::from("abcdefabcdefabcdefabcdef");
413 assert_eq!(a, b);
414 }
415
416 #[test]
417 fn long_str_cmp_2() {
418 let a = StrView::from("abcdefabcdefabcdefabcdef");
419 let b = StrView::from("abcdefabcdefabcdefabcdeg");
420 assert!(a < b);
421 }
422
423 #[test]
424 fn long_str_cmp_3() {
425 let a = StrView::from("abcdefabcdefabcdefabcde");
426 let b = StrView::from("abcdefabcdefabcdefabcdef");
427 assert!(a < b);
428 }
429}