musli_common/buf/
buf_string.rs

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