mut_str/
char_owned.rs

1use core::{
2    borrow::{Borrow, BorrowMut},
3    fmt,
4    ops::{Deref, DerefMut},
5    ptr,
6};
7
8use crate::{
9    errors::{TryFromBytesError, TryFromStrError},
10    Char,
11};
12
13#[derive(Clone)]
14#[repr(transparent)]
15/// An owned [`Char`].
16///
17/// Use [`Char::to_owned()`] to obtain this.
18///
19/// ```
20/// use mut_str::get_char;
21///
22/// let s = "Hello, World!";
23/// let c = get_char(s, 1).unwrap();
24/// let owned_c = c.to_owned();
25///
26/// assert_eq!(owned_c, 'e');
27/// ```
28pub struct OwnedChar {
29    b: [u8; 4],
30}
31
32impl OwnedChar {
33    #[must_use]
34    #[inline]
35    /// Create a new [`OwnedChar`] without checking the validity of the buffer.
36    ///
37    /// For a safe version, see [`OwnedChar::try_from()`].
38    ///
39    /// # Safety
40    /// There must be one valid UTF-8 encoded character at the start of the `b`.
41    pub const unsafe fn new_unchecked(b: [u8; 4]) -> Self {
42        Self { b }
43    }
44
45    #[must_use]
46    #[inline]
47    /// Create a new [`OwnedChar`] without checking the validity of the bytes.
48    ///
49    /// For a safe version, see [`OwnedChar::try_from()`].
50    ///
51    /// # Safety
52    /// There must be one valid UTF-8 encoded character at the start of `bytes`, which must be no longer than 4 bytes long.
53    pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
54        let mut c = [0; 4];
55        c[..bytes.len()].copy_from_slice(bytes);
56
57        Self { b: c }
58    }
59
60    #[must_use]
61    #[inline]
62    /// Get the underlying buffer as a mutable array.
63    ///
64    /// # Safety
65    /// The caller must ensure that when the mutable reference returned is dropped, there is a valid UTF-8 encoded character at the start of the buffer.
66    pub unsafe fn buffer_mut(&mut self) -> &mut [u8; 4] {
67        &mut self.b
68    }
69
70    #[must_use]
71    #[inline]
72    /// Get the underlying buffer. **This is not guaranteed to be valid UTF-8!**
73    ///
74    /// The first `self.len()` bytes will be valid UTF-8.
75    ///
76    /// ```
77    /// use mut_str::OwnedChar;
78    ///
79    /// let c = OwnedChar::from('🌑');
80    /// assert_eq!(c.into_bytes(), [240, 159, 140, 145]);
81    /// ```
82    pub const fn into_bytes(self) -> [u8; 4] {
83        self.b
84    }
85}
86
87impl AsRef<Char> for OwnedChar {
88    #[inline]
89    fn as_ref(&self) -> &Char {
90        // SAFETY:
91        // `self` is transparent bytes and contains a utf8 encoded character at
92        // the start, so this cast is valid, as the pointer will be to the
93        // start of a utf8 character.
94        unsafe { &*ptr::from_ref(self).cast() }
95    }
96}
97
98impl AsMut<Char> for OwnedChar {
99    #[inline]
100    fn as_mut(&mut self) -> &mut Char {
101        // SAFETY:
102        // `self` is transparent bytes and contains a utf8 encoded character at
103        // the start, so this cast is valid, as the pointer will be to the
104        // start of a utf8 character.
105        unsafe { &mut *ptr::from_mut(self).cast() }
106    }
107}
108
109impl Borrow<Char> for OwnedChar {
110    #[inline]
111    fn borrow(&self) -> &Char {
112        self.as_ref()
113    }
114}
115
116impl BorrowMut<Char> for OwnedChar {
117    #[inline]
118    fn borrow_mut(&mut self) -> &mut Char {
119        self.as_mut()
120    }
121}
122
123impl Deref for OwnedChar {
124    type Target = Char;
125
126    #[inline]
127    fn deref(&self) -> &Self::Target {
128        self.as_ref()
129    }
130}
131
132impl DerefMut for OwnedChar {
133    #[inline]
134    fn deref_mut(&mut self) -> &mut Self::Target {
135        self.as_mut()
136    }
137}
138
139impl fmt::Debug for OwnedChar {
140    #[inline]
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        self.as_ref().fmt(f)
143    }
144}
145
146impl fmt::Display for OwnedChar {
147    #[inline]
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        self.as_ref().fmt(f)
150    }
151}
152
153impl From<char> for OwnedChar {
154    #[inline]
155    fn from(value: char) -> Self {
156        let mut c = [0; 4];
157        value.encode_utf8(&mut c);
158        Self { b: c }
159    }
160}
161
162impl From<&Char> for OwnedChar {
163    #[inline]
164    fn from(value: &Char) -> Self {
165        value.as_owned()
166    }
167}
168
169impl TryFrom<&str> for OwnedChar {
170    type Error = TryFromStrError;
171
172    #[inline]
173    fn try_from(value: &str) -> Result<Self, Self::Error> {
174        <&Char>::try_from(value)?;
175
176        let mut c = [0; 4];
177        c[..value.len()].copy_from_slice(value.as_bytes());
178
179        Ok(Self { b: c })
180    }
181}
182
183impl TryFrom<&[u8]> for OwnedChar {
184    type Error = TryFromBytesError;
185
186    #[inline]
187    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
188        <&Char>::try_from(value)?;
189
190        let mut c = [0; 4];
191        c[..value.len()].copy_from_slice(value);
192
193        Ok(Self { b: c })
194    }
195}
196
197impl TryFrom<[u8; 4]> for OwnedChar {
198    type Error = TryFromBytesError;
199
200    #[inline]
201    fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
202        <&Char>::try_from(&value[..]).map(|_| Self { b: value })
203    }
204}
205
206impl<T> PartialEq<T> for OwnedChar
207where
208    Char: PartialEq<T>,
209{
210    #[inline]
211    fn eq(&self, other: &T) -> bool {
212        self.as_ref().eq(other)
213    }
214}
215
216impl Eq for OwnedChar {}
217
218impl<T> PartialOrd<T> for OwnedChar
219where
220    Char: PartialOrd<T>,
221{
222    #[inline]
223    fn partial_cmp(&self, other: &T) -> Option<core::cmp::Ordering> {
224        self.as_ref().partial_cmp(other)
225    }
226}
227
228impl Ord for OwnedChar {
229    #[inline]
230    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
231        self.as_ref().cmp(other)
232    }
233}
234
235#[cfg(test)]
236mod test {
237    use crate::{OwnedChar, StrExt};
238
239    #[test]
240    fn test_from_char_ref() {
241        let s = "abc";
242        let c = s.get_char(1).unwrap().as_owned();
243        assert_eq!(c.as_bytes(), &[98]);
244        assert_eq!(c, 'b');
245    }
246
247    #[test]
248    fn test_from_char() {
249        let c = OwnedChar::from('b');
250        assert_eq!(c.as_bytes(), &[98]);
251        assert_eq!(c, 'b');
252    }
253}