our_string/
string.rs

1use core::cmp::{Ordering, PartialEq, Eq, PartialOrd, Ord};
2use core::fmt::{self, Debug, Display};
3use core::borrow::Borrow;
4use core::ops::Deref;
5use core::hash::Hash;
6
7use crate::Comrade;
8
9#[derive(Default, Clone)]
10struct ItsUtf8ISwear;
11
12/// A customizable immutable shared string.
13///
14/// Data is backed inline up to `N` bytes (max 254), or stored dynamically by (shared) [`Comrade`] `T`.
15///
16/// This type can be constructed via the [`From`] trait given a `&str` (in which case inlining is attempted but may result in a shared `T` allocation)
17/// or via [`OurString::from_utf8`] given the underlying shared [`OurBytes`](crate::OurBytes) container (in which case the shared handle is simply wrapped after checking UTF-8 compliance).
18#[derive(Default, Clone)]
19pub struct OurString<T: Comrade, const N: usize>(crate::OurBytes<T, N>, ItsUtf8ISwear);
20
21impl<T: Comrade, const N: usize> OurString<T, N> {
22    /// Creates a new empty instance of [`OurString`] with inlined data.
23    pub const fn new() -> Self {
24        Self(crate::OurBytes::new(), ItsUtf8ISwear)
25    }
26    /// Converts this [`OurString`] instance into another [`OurString`] type which uses the same shared type `T`.
27    ///
28    /// If the content of this instance is already allocated via shared handle `T`, that handle will simply be reused without inlining.
29    /// Otherwise, re-inlining will be attempted, but may fail if `M < N` and result in a new shared `T` allocation.
30    ///
31    /// Because of this, it is advised to minimize the use of this function (e.g., by only using one [`OurString`] type throughout your codebase).
32    pub fn convert<const M: usize>(self) -> OurString<T, M> {
33        OurString(self.0.convert(), ItsUtf8ISwear)
34    }
35    /// Gets a shared reference to the content.
36    pub fn as_str(&self) -> &str {
37        self
38    }
39    /// Extracts the underlying shared bytes container.
40    pub fn into_bytes(self) -> crate::OurBytes<T, N> {
41        self.0
42    }
43    /// Attempts to construct a new [`OurString`] instance from the underlying shared bytes container.
44    pub fn from_utf8(value: crate::OurBytes<T, N>) -> Result<Self, core::str::Utf8Error> {
45        core::str::from_utf8(&value)?;
46        Ok(Self(value, ItsUtf8ISwear))
47    }
48}
49
50impl<T: Comrade, const N: usize> Deref for OurString<T, N> {
51    type Target = str;
52    fn deref(&self) -> &Self::Target {
53        unsafe { core::str::from_utf8_unchecked(&self.0) }
54    }
55}
56
57impl<T: Comrade, const N: usize> From<&str> for OurString<T, N> {
58    fn from(value: &str) -> Self {
59        Self(value.as_bytes().into(), ItsUtf8ISwear)
60    }
61}
62
63impl<T: Comrade, const N: usize> AsRef<str> for OurString<T, N> {
64    fn as_ref(&self) -> &str {
65        self
66    }
67}
68
69impl<T: Comrade, const N: usize> Borrow<str> for OurString<T, N> {
70    fn borrow(&self) -> &str {
71        self
72    }
73}
74
75impl<T: Comrade, const N: usize> Debug for OurString<T, N> {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        <str as Debug>::fmt(&**self, f)
78    }
79}
80
81impl<T: Comrade, const N: usize> Display for OurString<T, N> {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        <str as Display>::fmt(&**self, f)
84    }
85}
86
87impl<T: Comrade, const N: usize> Hash for OurString<T, N> {
88    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
89        (**self).hash(state)
90    }
91}
92
93impl<U: Deref<Target = str>, T: Comrade, const N: usize> PartialEq<U> for OurString<T, N> {
94    fn eq(&self, other: &U) -> bool {
95        (**self).eq(&**other)
96    }
97}
98
99impl<T: Comrade, const N: usize> PartialEq<OurString<T, N>> for &str {
100    fn eq(&self, other: &OurString<T, N>) -> bool {
101        (**self).eq(&**other)
102    }
103}
104
105impl<T: Comrade, const N: usize> Eq for OurString<T, N> {}
106
107impl<U: Deref<Target = str>, T: Comrade, const N: usize> PartialOrd<U> for OurString<T, N> {
108    fn partial_cmp(&self, other: &U) -> Option<Ordering> {
109        (**self).partial_cmp(&**other)
110    }
111}
112
113impl<T: Comrade, const N: usize> PartialOrd<OurString<T, N>> for &str {
114    fn partial_cmp(&self, other: &OurString<T, N>) -> Option<Ordering> {
115        (**self).partial_cmp(&**other)
116    }
117}
118
119impl<T: Comrade, const N: usize> Ord for OurString<T, N> {
120    fn cmp(&self, other: &Self) -> Ordering {
121        (**self).cmp(&**other)
122    }
123}