mstr/lib.rs
1#![doc = include_str!("../README.md")]
2#![no_std]
3#![deny(unsafe_op_in_unsafe_fn)]
4#![deny(elided_lifetimes_in_paths)]
5
6extern crate alloc;
7
8use alloc::borrow::Cow;
9use alloc::boxed::Box;
10use alloc::string::String;
11use core::borrow::Borrow;
12use core::cmp::Ordering;
13use core::fmt::{Debug, Display, Formatter, Pointer};
14use core::hash::{Hash, Hasher};
15use core::marker::PhantomData;
16use core::ops::Deref;
17use core::ptr::NonNull;
18use core::{fmt, mem, ptr, str};
19
20// the high bit of usize
21// if set (1), MStr is owned
22// if not (0), MStr is borrowed
23const TAG: usize = 1 << (usize::BITS - 1);
24// every bit except the tag bit
25const MASK: usize = !TAG;
26
27/// `MStr` is a 2-word, immutable version of `Cow<str>`.
28///
29/// See the [crate docs](crate) for more info.
30pub struct MStr<'a> {
31 ptr: NonNull<u8>,
32
33 // if high bit (TAG) is set, we are owned
34 // rust requires all allocations to be less than isize::MAX bytes,
35 // so the top bit is never used and thus available for tagging
36 len: usize,
37
38 // use the lifetime (also makes it covariant)
39 _marker: PhantomData<&'a str>,
40}
41
42unsafe impl Send for MStr<'_> {}
43unsafe impl Sync for MStr<'_> {}
44
45impl<'a> MStr<'a> {
46 // -- Constructors --
47
48 /// Creates a new `MStr<'a>` from an `&'a str`.
49 ///
50 /// The returned `MStr` is borrowed for the same lifetime as the input data.
51 ///
52 /// # Examples
53 ///
54 /// ```rust
55 /// # use mstr::MStr;
56 /// # use std::borrow::Cow;
57 /// let s = String::from("foo");
58 /// let mstr = MStr::new_borrowed(&s);
59 ///
60 /// assert!(mstr.is_borrowed());
61 /// assert_eq!(mstr, s);
62 /// assert_eq!(mstr.as_str(), "foo");
63 /// assert_eq!(mstr.as_ptr(), s.as_ptr());
64 /// assert!(matches!(mstr.into_cow(), Cow::Borrowed(_)));
65 /// ```
66 #[inline]
67 #[must_use]
68 pub const fn new_borrowed(s: &'a str) -> MStr<'a> {
69 MStr::_new(s.as_ptr(), s.len(), false)
70 }
71
72 /// Creates a new `MStr` from owned data.
73 /// The input type is anything that can be converted into a `Box<str>` (String, &str, etc).
74 ///
75 /// The returned `MStr` is owned.
76 /// The lifetime can be chosen to be anything, including `'static`.
77 ///
78 /// If `s` is `Box<str>`, it will not reallocate.
79 /// If `s` is `String`, it [may reallocate](String::into_boxed_str) if there is excess capacity.
80 /// If `s` is `&str`, it will be copied to a new heap allocation.
81 ///
82 /// # Examples
83 ///
84 /// ```rust
85 /// # use mstr::MStr;
86 /// # use std::borrow::Cow;
87 /// let s = Box::<str>::from("foo");
88 /// let ptr = s.as_ptr();
89 /// let mstr = MStr::new_owned(s);
90 ///
91 /// assert!(mstr.is_owned());
92 /// assert_eq!(mstr, "foo");
93 /// assert_eq!(mstr.as_ptr(), ptr); // the allocation is reused
94 /// assert!(matches!(mstr.into_cow(), Cow::Owned(_)));
95 /// ```
96 ///
97 /// Passing a string slice makes an owned copy:
98 /// ```rust
99 /// # use mstr::MStr;
100 /// let s = "bar";
101 /// let ptr = s.as_ptr();
102 /// let mstr = MStr::new_owned(s);
103 ///
104 /// assert!(mstr.is_owned());
105 /// assert_eq!(mstr, s);
106 /// assert_eq!(mstr, "bar");
107 ///
108 /// // a new allocation was created, and so the pointer are different
109 /// assert_ne!(mstr.as_ptr(), s.as_ptr());
110 /// ```
111 #[must_use]
112 pub fn new_owned(s: impl Into<Box<str>>) -> MStr<'a> {
113 let s = s.into();
114
115 let len = s.len();
116 let ptr = Box::into_raw(s).cast::<u8>();
117
118 MStr::_new(ptr, len, true)
119 }
120
121 /// Creates a new `MStr<'a>` from a `Cow<'a, str>`.
122 ///
123 /// The returned `MStr` will be borrowed if the cow is borrowed,
124 /// and owned if the cow is owned.
125 /// If the cow is owned, and has excess capacity, it [may reallocate](String::into_boxed_str).
126 ///
127 /// # Examples
128 ///
129 /// Owned:
130 /// ```rust
131 /// # use mstr::MStr;
132 /// # use std::borrow::Cow;
133 /// let cow = Cow::Owned(String::from("foo"));
134 /// let mstr = MStr::new_cow(cow);
135 ///
136 /// assert!(mstr.is_owned());
137 /// assert_eq!(mstr, "foo");
138 /// assert!(matches!(mstr.into_cow(), Cow::Owned(_)));
139 /// ```
140 ///
141 /// Borrowed:
142 /// ```rust
143 /// # use mstr::MStr;
144 /// # type Cow<'a> = std::borrow::Cow<'a, str>; // fix inference
145 /// let s = String::from("bar");
146 /// let cow = Cow::Borrowed(&s);
147 /// let mstr = MStr::new_cow(cow);
148 ///
149 /// assert!(mstr.is_borrowed());
150 /// assert_eq!(mstr, s);
151 /// assert_eq!(mstr.as_ptr(), s.as_ptr());
152 /// assert!(matches!(mstr.into_cow(), Cow::Borrowed(_)));
153 /// ```
154 ///
155 /// Borrowed (static):
156 /// ```rust
157 /// # use mstr::MStr;
158 /// # use std::borrow::Cow;
159 /// let cow = Cow::Borrowed("qux");
160 /// let mstr = MStr::new_cow(cow);
161 ///
162 /// assert!(mstr.is_borrowed());
163 /// assert_eq!(mstr, "qux");
164 /// assert!(matches!(mstr.into_cow(), Cow::Borrowed("qux")));
165 /// ```
166 #[inline]
167 #[must_use]
168 pub fn new_cow(s: Cow<'a, str>) -> MStr<'a> {
169 match s {
170 Cow::Borrowed(s) => MStr::new_borrowed(s),
171 Cow::Owned(s) => MStr::new_owned(s),
172 }
173 }
174
175 #[inline]
176 #[must_use]
177 const fn _new(ptr: *const u8, len: usize, tag: bool) -> MStr<'a> {
178 MStr {
179 // SAFETY: always comes from a valid string
180 ptr: unsafe { NonNull::new_unchecked(ptr.cast_mut()) },
181 len: if tag { len | TAG } else { len },
182 _marker: PhantomData,
183 }
184 }
185
186 // -- Accessors --
187
188 /// Converts this `MStr` to a string slice.
189 ///
190 /// # Examples
191 ///
192 /// ```rust
193 /// # use mstr::MStr;
194 /// let mstr = MStr::new_borrowed("foo");
195 ///
196 /// assert_eq!(mstr.as_str(), "foo");
197 /// ```
198 #[inline]
199 #[must_use]
200 pub const fn as_str(&self) -> &str {
201 unsafe { &*self.as_str_ptr() }
202 }
203
204 /// Converts this `MStr` to a UTF-8 byte slice.
205 ///
206 /// # Examples
207 ///
208 /// ```rust
209 /// # use mstr::MStr;
210 /// let mstr = MStr::new_borrowed("foo");
211 ///
212 /// assert_eq!(mstr.as_bytes(), b"foo");
213 /// ```
214 #[inline]
215 #[must_use]
216 pub const fn as_bytes(&self) -> &[u8] {
217 self.as_str().as_bytes()
218 }
219
220 /// Converts this `MStr` into an owned `String`.
221 /// This will consume `self`.
222 ///
223 /// If `self` is owned, the allocation will be reused.
224 /// If `self` is borrowed, it will be copied to the heap.
225 ///
226 /// # Examples
227 ///
228 /// ```rust
229 /// # use mstr::MStr;
230 /// let mstr = MStr::new_borrowed("foo");
231 /// let s: String = mstr.into_string();
232 ///
233 /// assert_eq!(s, "foo");
234 /// ```
235 ///
236 /// Reuses owned allocation:
237 /// ```rust
238 /// # use mstr::MStr;
239 /// let owned = Box::<str>::from("bar");
240 /// let ptr = owned.as_ptr();
241 /// let mstr = MStr::new_owned(owned);
242 /// let s = mstr.into_string();
243 ///
244 /// assert_eq!(s, "bar");
245 /// assert_eq!(s.as_ptr(), ptr);
246 /// ```
247 #[inline]
248 #[must_use]
249 pub fn into_string(self) -> String {
250 self.into_cow().into_owned()
251 }
252
253 /// Converts this `MStr` into an owned `Box<str>`.
254 /// This will consume `self`.
255 ///
256 /// If `self` is owned, the allocation will be reused.
257 /// If `self` is borrowed, it will be copied to the heap.
258 ///
259 /// # Examples
260 ///
261 /// ```rust
262 /// # use mstr::MStr;
263 /// let mstr = MStr::new_borrowed("foo");
264 /// let s: Box<str> = mstr.into_boxed();
265 ///
266 /// assert_eq!(&*s, "foo");
267 /// ```
268 ///
269 /// Reuses owned allocation:
270 /// ```rust
271 /// # use mstr::MStr;
272 /// let owned = Box::<str>::from("bar");
273 /// let ptr = owned.as_ptr();
274 /// let mstr = MStr::new_owned(owned);
275 /// let s = mstr.into_boxed();
276 ///
277 /// assert_eq!(&*s, "bar");
278 /// assert_eq!(s.as_ptr(), ptr);
279 /// ```
280 #[inline]
281 #[must_use]
282 pub fn into_boxed(self) -> Box<str> {
283 self.into_string().into_boxed_str()
284 }
285
286 /// Converts this `MStr<'a>` into a `Cow<'a, str>`.
287 /// This will consume `self`.
288 ///
289 /// The returned cow will be owned if `self` is owned, and borrowed if `self` is borrowed.
290 ///
291 /// # Examples
292 ///
293 /// ```rust
294 /// # use mstr::MStr;
295 /// # use std::borrow::Cow;
296 /// let borrowed = MStr::new_borrowed("foo");
297 /// let owned = MStr::new_owned("bar");
298 ///
299 /// assert!(matches!(borrowed.into_cow(), Cow::Borrowed("foo")));
300 /// assert!(matches!(owned.into_cow(), Cow::Owned(_)));
301 /// ```
302 #[must_use]
303 pub fn into_cow(self) -> Cow<'a, str> {
304 let ptr = self.as_str_ptr();
305 let is_owned = self.is_owned();
306 mem::forget(self);
307
308 if is_owned {
309 let b = unsafe { Box::from_raw(ptr.cast_mut()) };
310 Cow::Owned(b.into_string())
311 } else {
312 Cow::Borrowed(unsafe { &*ptr })
313 }
314 }
315
316 /// Checks if this `MStr` is owned.
317 ///
318 /// The result of this function is mutually exclusive with [`is_borrowed`](MStr::is_borrowed).
319 /// Exactly one of `is_borrowed` and `is_owned` will be true for every `MStr`.
320 ///
321 /// # Examples
322 ///
323 /// ```rust
324 /// # use mstr::MStr;
325 /// let mstr = MStr::new_owned("bar");
326 ///
327 /// assert!(mstr.is_owned());
328 /// assert!(!mstr.is_borrowed());
329 /// ```
330 #[inline]
331 #[must_use]
332 pub const fn is_owned(&self) -> bool {
333 self.len & TAG == TAG
334 }
335
336 /// Checks if this `MStr` is borrowed.
337 ///
338 /// The result of this function is mutually exclusive with [`is_owned`](MStr::is_owned).
339 /// Exactly one of `is_borrowed` and `is_owned` will be true for every `MStr`.
340 ///
341 /// # Examples
342 ///
343 /// ```rust
344 /// # use mstr::MStr;
345 /// let mstr = MStr::new_borrowed("bar");
346 ///
347 /// assert!(mstr.is_borrowed());
348 /// assert!(!mstr.is_owned());
349 /// ```
350 #[inline]
351 #[must_use]
352 pub const fn is_borrowed(&self) -> bool {
353 self.len & TAG == 0
354 }
355
356 /// If this `MStr<'a>` is borrowed, get the underlying `&'a str`.
357 /// Useful if you want access to the borrowed data for longer than the borrow of `&self`.
358 ///
359 /// This will return `Some` if `self` is borrowed, and `None` if `self` is owned.
360 ///
361 /// # Examples
362 ///
363 /// ```rust
364 /// # use mstr::MStr;
365 /// let mstr: MStr<'static> = MStr::new_borrowed("abc");
366 /// let s: Option<&'static str> = mstr.as_borrowed();
367 ///
368 /// assert_eq!(s, Some("abc"));
369 /// ```
370 ///
371 /// If `self` is owned:
372 /// ```
373 /// # use mstr::MStr;
374 /// assert_eq!(MStr::new_owned("abc").as_borrowed(), None);
375 /// ```
376 #[inline]
377 #[must_use]
378 pub const fn as_borrowed(&self) -> Option<&'a str> {
379 if self.is_borrowed() {
380 // SAFETY: self is borrowed which means it is an &'a str
381 Some(unsafe { &*self.as_str_ptr() })
382 } else {
383 None
384 }
385 }
386
387 /// Gets the length of the underlying string slice.
388 ///
389 /// # Examples
390 ///
391 /// ```rust
392 /// # use mstr::MStr;
393 /// let mstr = MStr::new_borrowed("12345");
394 ///
395 /// assert_eq!(mstr.len(), 5);
396 /// ```
397 #[inline]
398 #[must_use]
399 pub const fn len(&self) -> usize {
400 self.len & MASK
401 }
402
403 /// Checks if the underlying string slice is empty (length of 0)
404 ///
405 /// # Examples
406 ///
407 /// ```rust
408 /// # use mstr::MStr;
409 /// let empty = MStr::new_borrowed("");
410 /// let mstr = MStr::new_borrowed("foo");
411 ///
412 /// assert!(empty.is_empty());
413 /// assert!(!mstr.is_empty());
414 /// ```
415 #[inline]
416 #[must_use]
417 pub const fn is_empty(&self) -> bool {
418 self.len() == 0
419 }
420
421 /// Gets a pointer (`*const u8`) to the underlying slice's buffer.
422 ///
423 /// Do **NOT** use the returned pointer mutably, as `self` may be borrowed.
424 ///
425 /// Use [`as_str_ptr`](MStr::as_str_ptr) if you want a `*const str` instead.
426 ///
427 /// # Examples
428 ///
429 /// ```rust
430 /// # use mstr::MStr;
431 /// let s = "foo";
432 /// let mstr = MStr::new_borrowed(s);
433 ///
434 /// assert_eq!(mstr.as_ptr(), s.as_ptr());
435 /// ```
436 #[inline]
437 #[must_use]
438 pub const fn as_ptr(&self) -> *const u8 {
439 self.ptr.as_ptr()
440 }
441
442 /// Gets a pointer (`*const str`) to the underlying slice's buffer.
443 ///
444 /// Do **NOT** use the returned pointer mutably, as `self` may be borrowed.
445 ///
446 /// Use [`as_ptr`](MStr::as_ptr) if you want a `*const u8` instead.
447 ///
448 /// # Examples
449 ///
450 /// ```rust
451 /// # use mstr::MStr;
452 /// let s = "foo";
453 /// let mstr = MStr::new_borrowed(s);
454 ///
455 /// assert_eq!(mstr.as_str_ptr(), s as *const str);
456 /// ```
457 #[inline]
458 #[must_use]
459 pub const fn as_str_ptr(&self) -> *const str {
460 ptr::slice_from_raw_parts::<u8>(self.as_ptr(), self.len()) as *const str
461 }
462}
463
464// ===== Trait Impls =====
465
466impl Clone for MStr<'_> {
467 /// Clones this `MStr`.
468 ///
469 /// The returned `MStr` will be owned if `self` is owned, and borrowed if `self` is borrowed.
470 ///
471 /// # Examples
472 ///
473 /// ```rust
474 /// # use mstr::MStr;
475 /// let mstr = MStr::new_owned("foo");
476 /// let mstr2 = mstr.clone();
477 ///
478 /// assert_eq!(mstr, mstr2);
479 /// ```
480 ///
481 /// Borrowed/Owned is preserved:
482 /// ```rust
483 /// # use mstr::MStr;
484 /// let borrowed = MStr::new_borrowed("bar");
485 /// let owned = MStr::new_owned("qux");
486 ///
487 /// assert!(borrowed.clone().is_borrowed());
488 /// assert!(owned.clone().is_owned());
489 /// ```
490 fn clone(&self) -> Self {
491 if self.is_borrowed() {
492 MStr::_new(self.as_ptr(), self.len(), false)
493 } else {
494 MStr::new_owned(self.as_str())
495 }
496 }
497}
498
499impl Drop for MStr<'_> {
500 fn drop(&mut self) {
501 if self.is_owned() {
502 let b = unsafe { Box::from_raw(self.as_str_ptr().cast_mut()) };
503 drop(b);
504 }
505 }
506}
507
508// -- Default --
509
510impl Default for MStr<'_> {
511 /// Creates a new, empty, borrowed `MStr`.
512 ///
513 /// The returned `MStr` can have any lifetime.
514 ///
515 /// # Examples
516 ///
517 /// ```rust
518 /// # use mstr::MStr;
519 /// let default = MStr::default();
520 ///
521 /// assert_eq!(default, "");
522 /// assert!(default.is_empty());
523 /// assert!(default.is_borrowed());
524 /// ```
525 fn default() -> Self {
526 // a dangling (suitably aligned) slice of length 0 is always valid
527 MStr::_new(NonNull::<u8>::dangling().as_ptr(), 0, false)
528 }
529}
530
531// -- Format --
532
533impl Debug for MStr<'_> {
534 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
535 Debug::fmt(self.as_str(), f)
536 }
537}
538
539impl Display for MStr<'_> {
540 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
541 Display::fmt(self.as_str(), f)
542 }
543}
544
545impl Pointer for MStr<'_> {
546 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
547 Pointer::fmt(&self.as_str_ptr(), f)
548 }
549}
550
551// -- Convert From --
552
553impl<'a> From<&'a str> for MStr<'a> {
554 fn from(value: &'a str) -> Self {
555 MStr::new_borrowed(value)
556 }
557}
558
559impl<'a> From<&'a mut str> for MStr<'a> {
560 fn from(value: &'a mut str) -> Self {
561 MStr::new_borrowed(value)
562 }
563}
564
565impl<'a> From<Cow<'a, str>> for MStr<'a> {
566 fn from(value: Cow<'a, str>) -> Self {
567 MStr::new_cow(value)
568 }
569}
570
571impl From<String> for MStr<'_> {
572 fn from(value: String) -> Self {
573 MStr::new_owned(value)
574 }
575}
576
577impl From<Box<str>> for MStr<'_> {
578 fn from(value: Box<str>) -> Self {
579 MStr::new_owned(value)
580 }
581}
582
583// -- Convert To --
584
585impl<'a> From<MStr<'a>> for Cow<'a, str> {
586 fn from(value: MStr<'a>) -> Self {
587 value.into_cow()
588 }
589}
590
591impl From<MStr<'_>> for String {
592 fn from(value: MStr<'_>) -> Self {
593 value.into_string()
594 }
595}
596
597impl From<MStr<'_>> for Box<str> {
598 fn from(value: MStr<'_>) -> Self {
599 value.into_boxed()
600 }
601}
602
603// -- Convert Ref --
604
605impl Deref for MStr<'_> {
606 type Target = str;
607
608 fn deref(&self) -> &Self::Target {
609 self.as_str()
610 }
611}
612
613impl AsRef<str> for MStr<'_> {
614 fn as_ref(&self) -> &str {
615 self.as_str()
616 }
617}
618
619impl AsRef<[u8]> for MStr<'_> {
620 fn as_ref(&self) -> &[u8] {
621 self.as_bytes()
622 }
623}
624
625impl Borrow<str> for MStr<'_> {
626 fn borrow(&self) -> &str {
627 self.as_str()
628 }
629}
630
631// no Borrow<[u8]> because str/String don't implement it
632// (because the Hash impls are different)
633
634// -- Hash --
635
636impl Hash for MStr<'_> {
637 fn hash<H: Hasher>(&self, state: &mut H) {
638 Hash::hash(self.as_str(), state)
639 }
640}
641
642// -- [Partial]Eq --
643
644impl Eq for MStr<'_> {}
645
646impl PartialEq for MStr<'_> {
647 fn eq(&self, other: &Self) -> bool {
648 self.as_str() == other.as_str()
649 }
650}
651
652// str
653
654impl PartialEq<str> for MStr<'_> {
655 fn eq(&self, other: &str) -> bool {
656 self.as_str() == other
657 }
658}
659
660impl PartialEq<MStr<'_>> for str {
661 fn eq(&self, other: &MStr<'_>) -> bool {
662 self == other.as_str()
663 }
664}
665
666// &str
667
668impl PartialEq<&str> for MStr<'_> {
669 fn eq(&self, other: &&str) -> bool {
670 self.as_str() == *other
671 }
672}
673
674impl PartialEq<MStr<'_>> for &str {
675 fn eq(&self, other: &MStr<'_>) -> bool {
676 *self == other.as_str()
677 }
678}
679
680// String
681
682impl PartialEq<String> for MStr<'_> {
683 fn eq(&self, other: &String) -> bool {
684 self.as_str() == other
685 }
686}
687
688impl PartialEq<MStr<'_>> for String {
689 fn eq(&self, other: &MStr<'_>) -> bool {
690 self == other.as_str()
691 }
692}
693
694// Box<str>
695
696impl PartialEq<Box<str>> for MStr<'_> {
697 fn eq(&self, other: &Box<str>) -> bool {
698 self.as_str() == &**other
699 }
700}
701
702impl PartialEq<MStr<'_>> for Box<str> {
703 fn eq(&self, other: &MStr<'_>) -> bool {
704 &**self == other.as_str()
705 }
706}
707
708// -- [Partial]Ord --
709
710impl Ord for MStr<'_> {
711 fn cmp(&self, other: &Self) -> Ordering {
712 self.as_str().cmp(other.as_str())
713 }
714}
715
716impl PartialOrd for MStr<'_> {
717 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
718 Some(self.cmp(other))
719 }
720}
721
722impl PartialOrd<str> for MStr<'_> {
723 fn partial_cmp(&self, other: &str) -> Option<Ordering> {
724 self.as_str().partial_cmp(other)
725 }
726}
727
728impl PartialOrd<MStr<'_>> for str {
729 fn partial_cmp(&self, other: &MStr<'_>) -> Option<Ordering> {
730 self.partial_cmp(other.as_str())
731 }
732}
733
734// ===== serde =====
735
736#[cfg(feature = "serde")]
737mod serde_impls {
738 use super::*;
739 use serde::de::{Deserialize, Deserializer, Error, Visitor};
740 use serde::ser::{Serialize, Serializer};
741
742 // -- Serialize --
743
744 impl Serialize for MStr<'_> {
745 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
746 s.serialize_str(self.as_str())
747 }
748 }
749
750 // -- Deserialize --
751
752 struct MStrVisitor;
753
754 impl Visitor<'_> for MStrVisitor {
755 type Value = MStr<'static>;
756
757 fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
758 f.write_str("a string")
759 }
760
761 fn visit_str<E: Error>(self, s: &str) -> Result<Self::Value, E> {
762 Ok(MStr::new_owned(s))
763 }
764
765 fn visit_string<E: Error>(self, s: String) -> Result<Self::Value, E> {
766 Ok(MStr::new_owned(s))
767 }
768 }
769
770 impl<'de> Deserialize<'de> for MStr<'_> {
771 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
772 d.deserialize_string(MStrVisitor)
773 }
774 }
775
776 // -- Unit Tests --
777
778 #[cfg(test)]
779 mod tests {
780 use super::*;
781 use serde::de::DeserializeOwned;
782 use serde_json::json;
783 use serde_test::{assert_tokens, Token};
784
785 #[test]
786 fn basic() {
787 assert_tokens(&MStr::from("roar"), &[Token::BorrowedStr("roar")]);
788 assert_tokens(&MStr::from("honk"), &[Token::Str("honk")]);
789 assert_tokens(&MStr::from("quack"), &[Token::String("quack")]);
790 }
791
792 #[test]
793 fn always_de_owned() {
794 let not_static = String::from("\"frogs <3\"");
795
796 let s: MStr<'static> = serde_json::from_str(¬_static).unwrap();
797
798 assert_eq!(s, "frogs <3");
799 assert!(s.is_owned());
800 }
801
802 #[test]
803 fn de_value() {
804 let s: MStr<'static> =
805 serde_json::from_value(json!("i like frogs can you tell")).unwrap();
806
807 assert_eq!(s, "i like frogs can you tell");
808 assert!(s.is_owned());
809 }
810
811 #[test]
812 fn assert_deserialize_owned() {
813 fn assert_deserialize_owned<T: DeserializeOwned>() {}
814
815 assert_deserialize_owned::<MStr<'_>>();
816 assert_deserialize_owned::<MStr<'static>>();
817 }
818 }
819}
820
821// ===== Unit Tests =====
822
823#[cfg(test)]
824mod tests {
825 use super::*;
826
827 // fix inference
828 type Cow<'a> = alloc::borrow::Cow<'a, str>;
829
830 #[test]
831 fn correct_repr() {
832 assert!(MStr::new_borrowed("abc").is_borrowed());
833 assert!(!MStr::new_borrowed("abc").is_owned());
834
835 assert!(MStr::new_owned("123").is_owned());
836 assert!(!MStr::new_owned("123").is_borrowed());
837 }
838
839 #[test]
840 fn empty() {
841 assert!(MStr::new_borrowed("").is_empty());
842 assert!(MStr::new_owned("").is_empty());
843 assert!(MStr::default().is_empty());
844
845 assert_eq!(MStr::new_borrowed("").len(), 0);
846 assert_eq!(MStr::new_owned("").len(), 0);
847 assert_eq!(MStr::default().len(), 0);
848 }
849
850 #[test]
851 fn owned_empty() {
852 // even though its dangling it should still be marked as owned
853 // behaviour has to be consistent
854 assert!(MStr::new_owned("").is_owned());
855 assert!(MStr::new_owned(Box::<str>::default()).is_owned());
856 }
857
858 #[test]
859 fn len() {
860 assert_eq!(MStr::new_borrowed("12345").len(), 5);
861 assert_eq!(MStr::new_owned("12345").len(), 5);
862 }
863
864 #[test]
865 fn borrowed_stays_borrowed() {
866 let s = "1234";
867 let mstr = MStr::new_borrowed(s);
868
869 assert_eq!(mstr, s);
870 assert_eq!(mstr.as_str(), s);
871
872 assert_eq!(mstr.as_ptr(), s.as_ptr());
873 assert_eq!(mstr.as_str().as_ptr(), s.as_ptr());
874 assert_eq!(mstr.as_str_ptr(), s as *const str);
875
876 let clone = mstr.clone();
877
878 assert!(clone.is_borrowed());
879 assert!(!clone.is_owned());
880
881 assert_eq!(mstr, clone);
882 assert_eq!(mstr.as_ptr(), clone.as_ptr());
883 assert_eq!(mstr.as_str_ptr(), clone.as_str_ptr());
884 }
885
886 #[test]
887 fn into_cow() {
888 assert!(matches!(
889 MStr::new_borrowed("meow").into_cow(),
890 Cow::Borrowed("meow")
891 ));
892 assert!(matches!(
893 MStr::new_cow(Cow::Borrowed("purr")).into_cow(),
894 Cow::Borrowed("purr")
895 ));
896
897 assert!(matches!(MStr::new_owned("woof").into_cow(), Cow::Owned(_)));
898 assert!(matches!(
899 MStr::new_cow(Cow::Owned("bark".into())).into_cow(),
900 Cow::Owned(_)
901 ));
902
903 assert_eq!(MStr::new_owned("woof").into_cow(), "woof");
904 assert_eq!(MStr::new_cow(Cow::Owned("bark".into())).into_cow(), "bark");
905 }
906
907 #[test]
908 fn roundtrip() {
909 assert_eq!(MStr::new_borrowed("foo").into_string(), String::from("foo"));
910 assert_eq!(MStr::new_owned("bar").into_string(), String::from("bar"));
911 }
912
913 #[test]
914 fn roundtrip_string_ptr() {
915 let s = String::from("quack");
916 let ptr = s.as_ptr();
917 let mstr = MStr::new_owned(s);
918
919 assert_eq!(mstr, "quack");
920 assert_eq!(mstr.as_ptr(), ptr);
921
922 let s2 = mstr.into_string();
923 assert_eq!(s2.as_ptr(), ptr);
924 }
925
926 #[test]
927 fn owned_clone() {
928 let mstr = MStr::new_owned("quack");
929 let mstr2 = mstr.clone();
930
931 assert!(mstr.is_owned());
932 assert!(mstr2.is_owned());
933 assert!(!mstr2.is_borrowed());
934
935 assert_eq!(mstr, mstr2);
936 assert_ne!(mstr.as_ptr(), mstr2.as_ptr());
937 assert_ne!(mstr.as_str_ptr(), mstr2.as_str_ptr());
938 }
939
940 #[test]
941 fn as_borrowed() {
942 let s = String::from("meow");
943 let mstr = MStr::new_borrowed(&s);
944 let s2 = mstr.as_borrowed();
945 drop(mstr);
946 assert_eq!(s2, Some("meow"));
947 assert_eq!(s2.unwrap(), s);
948 }
949
950 #[test]
951 fn static_lt() {
952 let owned: MStr<'static> = MStr::new_owned("abc");
953 let borrowed: MStr<'static> = MStr::new_borrowed("abc");
954
955 assert_eq!(owned, borrowed);
956 }
957
958 #[test]
959 fn covariant_lt() {
960 fn same_lt<'a>(a: &MStr<'a>, b: &MStr<'a>, s: &'a str) {
961 assert_eq!(a, b);
962 assert_eq!(a, s);
963 assert_eq!(b, s);
964 }
965
966 let st1: MStr<'static> = MStr::new_borrowed("oink");
967 let st2: MStr<'static> = MStr::new_owned("oink");
968
969 same_lt(&st1, &st2, "oink");
970
971 let s = String::from("oink");
972 let ms = MStr::new_borrowed(&s);
973
974 same_lt(&st1, &ms, &s);
975
976 // --
977
978 fn coerce_any_lt_owned<'a>() -> MStr<'a> {
979 MStr::new_owned("abc")
980 }
981 assert_eq!(coerce_any_lt_owned(), "abc");
982
983 fn coerce_any_lt_borrowed<'a>() -> MStr<'a> {
984 MStr::new_borrowed("123")
985 }
986 assert_eq!(coerce_any_lt_borrowed(), "123");
987 }
988
989 #[test]
990 fn assert_send_sync() {
991 fn assert_send_sync<T: Send + Sync>() {}
992
993 assert_send_sync::<MStr<'_>>();
994 assert_send_sync::<MStr<'static>>();
995 }
996}