Skip to main content

stabby_abi/alloc/
string.rs

1use super::{
2    boxed::BoxedSlice,
3    sync::{ArcSlice, WeakSlice},
4    vec::Vec,
5    AllocationError, IAlloc,
6};
7use core::hash::Hash;
8
9/// A growable owned string.
10#[crate::stabby]
11#[derive(Clone)]
12pub struct String<Alloc: IAlloc = super::DefaultAllocator> {
13    pub(crate) inner: Vec<u8, Alloc>,
14}
15
16#[cfg(not(stabby_default_alloc = "disabled"))]
17impl String {
18    /// Constructs a new string using the default allocator.
19    pub const fn new() -> Self {
20        Self { inner: Vec::new() }
21    }
22}
23impl<Alloc: IAlloc> String<Alloc> {
24    /// Constructs a new string using the provided allocator.
25    pub const fn new_in(alloc: Alloc) -> Self {
26        Self {
27            inner: Vec::new_in(alloc),
28        }
29    }
30    /// Returns self as a borrowed string
31    pub const fn as_str(&self) -> &str {
32        unsafe { core::str::from_utf8_unchecked(self.inner.as_slice()) }
33    }
34    /// Returns self as a mutably borrowed string
35    #[rustversion::attr(since(1.86), const)]
36    pub fn as_str_mut(&mut self) -> &mut str {
37        unsafe { core::str::from_utf8_unchecked_mut(self.inner.as_slice_mut()) }
38    }
39    fn try_concat_str(&mut self, s: &str) -> Result<(), AllocationError> {
40        self.inner.try_copy_extend(s.as_bytes())
41    }
42    /// Attempts to concatenate `s` to `self`
43    /// # Errors
44    /// This returns an [`AllocationError`] if reallocation was needed and failed to concatenate.
45    pub fn try_concat<S: AsRef<str> + ?Sized>(&mut self, s: &S) -> Result<(), AllocationError> {
46        self.try_concat_str(s.as_ref())
47    }
48}
49impl<Alloc: IAlloc + Default> Default for String<Alloc> {
50    fn default() -> Self {
51        Self {
52            inner: Vec::default(),
53        }
54    }
55}
56impl<S: AsRef<str> + ?Sized, Alloc: IAlloc> core::ops::Add<&S> for String<Alloc> {
57    type Output = Self;
58    #[allow(clippy::arithmetic_side_effects)]
59    fn add(mut self, rhs: &S) -> Self::Output {
60        self += rhs.as_ref();
61        self
62    }
63}
64impl<S: AsRef<str> + ?Sized, Alloc: IAlloc> core::ops::AddAssign<&S> for String<Alloc> {
65    fn add_assign(&mut self, rhs: &S) {
66        self.inner.copy_extend(rhs.as_ref().as_bytes())
67    }
68}
69
70impl<Alloc: IAlloc> From<String<Alloc>> for Vec<u8, Alloc> {
71    fn from(value: String<Alloc>) -> Self {
72        value.inner
73    }
74}
75
76impl<Alloc: IAlloc> TryFrom<Vec<u8, Alloc>> for String<Alloc> {
77    type Error = core::str::Utf8Error;
78    fn try_from(value: Vec<u8, Alloc>) -> Result<Self, Self::Error> {
79        core::str::from_utf8(value.as_slice())?;
80        Ok(Self { inner: value })
81    }
82}
83
84impl<Alloc: IAlloc> core::ops::Deref for String<Alloc> {
85    type Target = str;
86    fn deref(&self) -> &Self::Target {
87        self.as_str()
88    }
89}
90
91impl<Alloc: IAlloc> core::convert::AsRef<str> for String<Alloc> {
92    fn as_ref(&self) -> &str {
93        self.as_str()
94    }
95}
96impl<Alloc: IAlloc> core::ops::DerefMut for String<Alloc> {
97    fn deref_mut(&mut self) -> &mut Self::Target {
98        self.as_str_mut()
99    }
100}
101
102impl<Alloc: IAlloc> core::fmt::Debug for String<Alloc> {
103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104        core::fmt::Debug::fmt(self.as_str(), f)
105    }
106}
107impl<Alloc: IAlloc> core::fmt::Display for String<Alloc> {
108    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
109        core::fmt::Display::fmt(self.as_str(), f)
110    }
111}
112impl<Alloc: IAlloc> Hash for String<Alloc> {
113    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
114        self.as_str().hash(state)
115    }
116}
117impl<Alloc: IAlloc, Rhs: AsRef<str>> PartialEq<Rhs> for String<Alloc> {
118    fn eq(&self, other: &Rhs) -> bool {
119        self.as_str() == other.as_ref()
120    }
121}
122impl<Alloc: IAlloc> Eq for String<Alloc> {}
123impl<Alloc: IAlloc, Rhs: AsRef<str>> PartialOrd<Rhs> for String<Alloc> {
124    fn partial_cmp(&self, other: &Rhs) -> Option<core::cmp::Ordering> {
125        self.as_str().partial_cmp(other.as_ref())
126    }
127}
128impl<Alloc: IAlloc> Ord for String<Alloc> {
129    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
130        self.as_str().cmp(other.as_str())
131    }
132}
133
134impl<Alloc: IAlloc + Default> From<&str> for String<Alloc> {
135    #[allow(clippy::arithmetic_side_effects)]
136    fn from(value: &str) -> Self {
137        Self::default() + value
138    }
139}
140
141impl<Alloc: IAlloc + Default> From<crate::str::Str<'_>> for String<Alloc> {
142    #[allow(clippy::arithmetic_side_effects)]
143    fn from(value: crate::str::Str<'_>) -> Self {
144        Self::default() + value.as_ref()
145    }
146}
147
148/// A reference counted boxed string.
149#[crate::stabby]
150pub struct ArcStr<Alloc: IAlloc = super::DefaultAllocator> {
151    inner: ArcSlice<u8, Alloc>,
152}
153impl<Alloc: IAlloc> ArcStr<Alloc> {
154    /// Returns a borrow to the inner string.
155    pub const fn as_str(&self) -> &str {
156        unsafe { core::str::from_utf8_unchecked(self.inner.as_slice()) }
157    }
158    /// Returns a mutably borrow to the inner str.
159    /// # Safety
160    /// [`Self::is_unique`] must be true.
161    #[rustversion::attr(since(1.86), const)]
162    pub unsafe fn as_str_mut_unchecked(&mut self) -> &mut str {
163        unsafe { core::str::from_utf8_unchecked_mut(self.inner.as_slice_mut_unchecked()) }
164    }
165    /// Returns a mutably borrow to the inner str if no other borrows of it can exist.
166    pub fn as_str_mut(&mut self) -> Option<&mut str> {
167        Self::is_unique(self).then(|| unsafe { self.as_str_mut_unchecked() })
168    }
169    /// Whether or not `this` is the sole owner of its data, including weak owners.
170    pub fn is_unique(this: &Self) -> bool {
171        ArcSlice::is_unique(&this.inner)
172    }
173}
174impl<Alloc: IAlloc> AsRef<str> for ArcStr<Alloc> {
175    fn as_ref(&self) -> &str {
176        self.as_str()
177    }
178}
179
180impl<Alloc: IAlloc> core::fmt::Debug for ArcStr<Alloc> {
181    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
182        core::fmt::Debug::fmt(self.as_str(), f)
183    }
184}
185impl<Alloc: IAlloc> core::fmt::Display for ArcStr<Alloc> {
186    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
187        core::fmt::Display::fmt(self.as_str(), f)
188    }
189}
190impl<Alloc: IAlloc> core::ops::Deref for ArcStr<Alloc> {
191    type Target = str;
192    fn deref(&self) -> &Self::Target {
193        self.as_str()
194    }
195}
196impl<Alloc: IAlloc> From<String<Alloc>> for ArcStr<Alloc> {
197    fn from(value: String<Alloc>) -> Self {
198        Self {
199            inner: value.inner.into(),
200        }
201    }
202}
203impl<Alloc: IAlloc> TryFrom<ArcStr<Alloc>> for String<Alloc> {
204    type Error = ArcStr<Alloc>;
205    fn try_from(value: ArcStr<Alloc>) -> Result<Self, ArcStr<Alloc>> {
206        match value.inner.try_into() {
207            Ok(vec) => Ok(String { inner: vec }),
208            Err(slice) => Err(ArcStr { inner: slice }),
209        }
210    }
211}
212impl<Alloc: IAlloc> Clone for ArcStr<Alloc> {
213    fn clone(&self) -> Self {
214        Self {
215            inner: self.inner.clone(),
216        }
217    }
218}
219impl<Alloc: IAlloc> Eq for ArcStr<Alloc> {}
220impl<Alloc: IAlloc> PartialEq for ArcStr<Alloc> {
221    fn eq(&self, other: &Self) -> bool {
222        self.as_str() == other.as_str()
223    }
224}
225impl<Alloc: IAlloc> Ord for ArcStr<Alloc> {
226    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
227        self.as_str().cmp(other.as_str())
228    }
229}
230impl<Alloc: IAlloc> PartialOrd for ArcStr<Alloc> {
231    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
232        Some(self.cmp(other))
233    }
234}
235impl<Alloc: IAlloc> Hash for ArcStr<Alloc> {
236    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
237        self.as_str().hash(state)
238    }
239}
240
241/// A weak reference counted boxed string.
242#[crate::stabby]
243pub struct WeakStr<Alloc: IAlloc = super::DefaultAllocator> {
244    inner: WeakSlice<u8, Alloc>,
245}
246impl<Alloc: IAlloc> WeakStr<Alloc> {
247    /// Returns a strong reference if the strong count hasn't reached 0 yet.
248    pub fn upgrade(&self) -> Option<ArcStr<Alloc>> {
249        self.inner.upgrade().map(|inner| ArcStr { inner })
250    }
251    /// Returns a strong reference to the string.
252    ///
253    /// If you're using this, there are probably design issues in your program...
254    pub fn force_upgrade(&self) -> ArcStr<Alloc> {
255        ArcStr {
256            inner: self.inner.force_upgrade(),
257        }
258    }
259}
260impl<Alloc: IAlloc> From<&ArcStr<Alloc>> for WeakStr<Alloc> {
261    fn from(value: &ArcStr<Alloc>) -> Self {
262        Self {
263            inner: (&value.inner).into(),
264        }
265    }
266}
267impl<Alloc: IAlloc> Clone for WeakStr<Alloc> {
268    fn clone(&self) -> Self {
269        Self {
270            inner: self.inner.clone(),
271        }
272    }
273}
274
275/// A boxed string.
276#[crate::stabby]
277pub struct BoxedStr<Alloc: IAlloc = super::DefaultAllocator> {
278    inner: BoxedSlice<u8, Alloc>,
279}
280impl<Alloc: IAlloc> BoxedStr<Alloc> {
281    /// Returns a borrow to the inner string.
282    pub const fn as_str(&self) -> &str {
283        unsafe { core::str::from_utf8_unchecked(self.inner.as_slice()) }
284    }
285    /// Returns a mutable borrow to the inner string.
286    #[rustversion::attr(since(1.86), const)]
287    pub fn as_str_mut(&mut self) -> &mut str {
288        unsafe { core::str::from_utf8_unchecked_mut(self.inner.as_slice_mut()) }
289    }
290}
291impl<Alloc: IAlloc> AsRef<str> for BoxedStr<Alloc> {
292    fn as_ref(&self) -> &str {
293        self.as_str()
294    }
295}
296impl<Alloc: IAlloc> core::ops::Deref for BoxedStr<Alloc> {
297    type Target = str;
298    fn deref(&self) -> &Self::Target {
299        self.as_str()
300    }
301}
302impl<Alloc: IAlloc> From<String<Alloc>> for BoxedStr<Alloc> {
303    fn from(value: String<Alloc>) -> Self {
304        Self {
305            inner: value.inner.into(),
306        }
307    }
308}
309impl<Alloc: IAlloc> From<BoxedStr<Alloc>> for String<Alloc> {
310    fn from(value: BoxedStr<Alloc>) -> Self {
311        String {
312            inner: value.inner.into(),
313        }
314    }
315}
316impl<Alloc: IAlloc> Eq for BoxedStr<Alloc> {}
317impl<Alloc: IAlloc> PartialEq for BoxedStr<Alloc> {
318    fn eq(&self, other: &Self) -> bool {
319        self.as_str() == other.as_str()
320    }
321}
322impl<Alloc: IAlloc> Ord for BoxedStr<Alloc> {
323    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
324        self.as_str().cmp(other.as_str())
325    }
326}
327impl<Alloc: IAlloc> PartialOrd for BoxedStr<Alloc> {
328    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
329        Some(self.cmp(other))
330    }
331}
332impl<Alloc: IAlloc> core::hash::Hash for BoxedStr<Alloc> {
333    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
334        self.as_str().hash(state)
335    }
336}
337
338impl<Alloc: IAlloc> core::fmt::Debug for BoxedStr<Alloc> {
339    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
340        core::fmt::Debug::fmt(self.as_str(), f)
341    }
342}
343impl<Alloc: IAlloc> core::fmt::Display for BoxedStr<Alloc> {
344    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
345        core::fmt::Display::fmt(self.as_str(), f)
346    }
347}
348
349impl core::fmt::Write for String {
350    fn write_str(&mut self, s: &str) -> core::fmt::Result {
351        self.try_concat(s).map_err(|_| core::fmt::Error)
352    }
353}
354
355#[cfg(feature = "std")]
356mod std_impl {
357    use crate::alloc::IAlloc;
358    impl<Alloc: IAlloc + Default> From<std::string::String> for crate::alloc::string::String<Alloc> {
359        fn from(value: std::string::String) -> Self {
360            Self::from(value.as_ref())
361        }
362    }
363    impl<Alloc: IAlloc + Default> From<crate::alloc::string::String<Alloc>> for std::string::String {
364        fn from(value: crate::alloc::string::String<Alloc>) -> Self {
365            Self::from(value.as_ref())
366        }
367    }
368}
369
370#[cfg(feature = "serde")]
371mod serde_impl {
372    use super::*;
373    use crate::alloc::IAlloc;
374    use serde::de::{Error, Unexpected, Visitor};
375    use serde::{Deserialize, Serialize};
376    impl<Alloc: IAlloc> Serialize for String<Alloc> {
377        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
378        where
379            S: serde::Serializer,
380        {
381            serializer.serialize_str(self)
382        }
383    }
384    impl<'a, Alloc: IAlloc + Default> Deserialize<'a> for String<Alloc> {
385        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
386        where
387            D: serde::Deserializer<'a>,
388        {
389            deserializer.deserialize_string(StringVisitor(core::marker::PhantomData))
390        }
391    }
392    struct StringVisitor<A: IAlloc>(core::marker::PhantomData<crate::alloc::string::String<A>>);
393    impl<'a, Alloc: IAlloc + Default> Visitor<'a> for StringVisitor<Alloc> {
394        type Value = String<Alloc>;
395        fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
396            Ok(v.into())
397        }
398
399        #[cfg(feature = "std")]
400        fn visit_string<E: Error>(self, v: std::string::String) -> Result<Self::Value, E> {
401            Ok(v.into())
402        }
403
404        fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> {
405            core::str::from_utf8(v)
406                .map_err(|_| Error::invalid_value(Unexpected::Bytes(v), &self))
407                .map(Into::into)
408        }
409
410        #[cfg(feature = "std")]
411        fn visit_byte_buf<E: Error>(self, v: std::vec::Vec<u8>) -> Result<Self::Value, E> {
412            std::string::String::from_utf8(v)
413                .map_err(|e| Error::invalid_value(Unexpected::Bytes(&e.into_bytes()), &self))
414                .map(Into::into)
415        }
416
417        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
418            write!(formatter, "a string")
419        }
420    }
421}