musli/alloc/
string.rs

1use core::fmt;
2use core::ops::Deref;
3use core::str;
4
5use crate::fixed::CapacityError;
6use crate::Context;
7
8use super::{Allocator, Vec};
9
10/// Wrapper around a buffer that is guaranteed to be a valid utf-8 string.
11pub struct String<'a, A>
12where
13    A: 'a + ?Sized + Allocator,
14{
15    buf: Vec<'a, u8, A>,
16}
17
18/// Collect a string into a string buffer.
19pub(crate) fn collect_string<C, T>(cx: &C, value: T) -> Result<String<'_, C::Allocator>, C::Error>
20where
21    C: ?Sized + Context,
22    T: fmt::Display,
23{
24    use core::fmt::Write;
25
26    let mut string = String::new_in(cx.alloc());
27
28    if write!(string, "{value}").is_err() {
29        return Err(cx.message("Failed to write to string"));
30    }
31
32    Ok(string)
33}
34
35impl<'a, A> String<'a, A>
36where
37    A: 'a + ?Sized + Allocator,
38{
39    /// Construct a new string buffer in the provided allocator.
40    pub fn new_in(alloc: &'a A) -> Self {
41        Self::new(alloc.new_raw_vec::<u8>())
42    }
43
44    /// Construct a new fixed string.
45    pub(crate) const fn new(buf: A::RawVec<'a, u8>) -> Self {
46        Self { buf: Vec::new(buf) }
47    }
48
49    fn as_str(&self) -> &str {
50        // SAFETY: Interactions ensure that data is valid utf-8.
51        unsafe { str::from_utf8_unchecked(self.buf.as_slice()) }
52    }
53
54    fn try_push(&mut self, c: char) -> Result<(), CapacityError> {
55        if !self.buf.write(c.encode_utf8(&mut [0; 4]).as_bytes()) {
56            return Err(CapacityError);
57        }
58
59        Ok(())
60    }
61
62    fn try_push_str(&mut self, s: &str) -> Result<(), CapacityError> {
63        if !self.buf.write(s.as_bytes()) {
64            return Err(CapacityError);
65        }
66
67        Ok(())
68    }
69}
70
71impl<'a, A> fmt::Write for String<'a, A>
72where
73    A: 'a + ?Sized + Allocator,
74{
75    #[inline]
76    fn write_char(&mut self, c: char) -> fmt::Result {
77        self.try_push(c).map_err(|_| fmt::Error)
78    }
79
80    #[inline]
81    fn write_str(&mut self, s: &str) -> fmt::Result {
82        self.try_push_str(s).map_err(|_| fmt::Error)
83    }
84}
85
86impl<'a, A> Deref for String<'a, A>
87where
88    A: 'a + ?Sized + Allocator,
89{
90    type Target = str;
91
92    #[inline]
93    fn deref(&self) -> &str {
94        unsafe { str::from_utf8_unchecked(self.buf.as_slice()) }
95    }
96}
97
98impl<'a, A> fmt::Display for String<'a, A>
99where
100    A: 'a + ?Sized + Allocator,
101{
102    #[inline]
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        self.as_str().fmt(f)
105    }
106}
107
108impl<'a, A> fmt::Debug for String<'a, A>
109where
110    A: 'a + ?Sized + Allocator,
111{
112    #[inline]
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        self.as_str().fmt(f)
115    }
116}
117
118impl<'a, A> AsRef<str> for String<'a, A>
119where
120    A: ?Sized + Allocator,
121{
122    #[inline]
123    fn as_ref(&self) -> &str {
124        self.as_str()
125    }
126}