Skip to main content

osom_lib_strings/immutable/
immutable_string_builder.rs

1use osom_lib_alloc::traits::Allocator;
2use osom_lib_arc::carc_array::CArcArrayBuilder;
3use osom_lib_primitives::length::Length;
4use osom_lib_reprc::macros::reprc;
5
6use crate::immutable::{ImmutableString, ImmutableStringError};
7
8/// The builder for [`ImmutableString`].
9///
10/// It allows direct writing to the underlying buffer, before the data
11/// gets sealed as immutable.
12///
13/// # Examples
14///
15/// ```rust
16/// # cfg_select! {
17/// #    feature="std" => {
18/// use osom_lib_strings::immutable::ImmutableStringError;
19/// use osom_lib_strings::immutable::std::StdImmutableStringBuilder;
20///
21/// fn main() -> Result<(), ImmutableStringError> {
22///     let mut builder = StdImmutableStringBuilder::new()?;
23///     builder.try_push("Name: ")?;
24///     builder.try_push("John")?;
25///     let string = builder.build()?;
26///     assert_eq!(string.as_str(), "Name: John");
27///     Ok(())
28/// }
29/// # },
30/// # _ => {}
31/// # }
32/// ```
33#[reprc]
34#[repr(transparent)]
35#[must_use]
36pub struct ImmutableStringBuilder<TAllocator: Allocator> {
37    internal: CArcArrayBuilder<u8, TAllocator>,
38}
39
40impl<TAllocator: Allocator> ImmutableStringBuilder<TAllocator> {
41    /// Creates a new [`ImmutableStringBuilder`]. This allocates an initial buffer
42    /// under the hood.
43    ///
44    /// # Errors
45    ///
46    /// For details see [`ImmutableStringError`].
47    #[inline(always)]
48    pub fn new() -> Result<Self, ImmutableStringError>
49    where
50        TAllocator: Default,
51    {
52        Self::with_capacity_and_allocator(Length::ZERO, TAllocator::default())
53    }
54
55    /// Creates a new [`ImmutableStringBuilder`] with given capacity. This allocates an initial buffer
56    /// under the hood.
57    ///
58    /// # Errors
59    ///
60    /// For details see [`ImmutableStringError`].
61    #[inline(always)]
62    pub fn with_capacity(capacity: Length) -> Result<Self, ImmutableStringError>
63    where
64        TAllocator: Default,
65    {
66        Self::with_capacity_and_allocator(capacity, TAllocator::default())
67    }
68
69    /// Creates a new [`ImmutableStringBuilder`] with given allocator. This allocates an initial buffer
70    /// under the hood.
71    ///
72    /// # Errors
73    ///
74    /// For details see [`ImmutableStringError`].
75    #[inline(always)]
76    pub fn with_allocator(allocator: TAllocator) -> Result<Self, ImmutableStringError> {
77        Self::with_capacity_and_allocator(Length::ZERO, allocator)
78    }
79
80    /// Creates a new [`ImmutableStringBuilder`] with given capacity and allocator.
81    /// This allocates an initial buffer under the hood.
82    ///
83    /// # Errors
84    ///
85    /// For details see [`ImmutableStringError`].
86    #[inline]
87    pub fn with_capacity_and_allocator(capacity: Length, allocator: TAllocator) -> Result<Self, ImmutableStringError> {
88        // NOTE: we allocate capacity+1 to make room for the last \0 byte, for C-string compatibility.
89        let internal = CArcArrayBuilder::with_capacity_and_allocator(capacity + 1, allocator)?;
90        Ok(Self { internal })
91    }
92
93    /// Pushes a new string slice to [`ImmutableStringBuilder`]. This copies the passed string to the underlying
94    /// buffer. It will also resize the underlying buffer on demand.
95    ///
96    /// # Errors
97    ///
98    /// For details see [`ImmutableStringError`].
99    #[inline]
100    pub fn try_push(&mut self, text: &str) -> Result<(), ImmutableStringError> {
101        self.internal
102            .try_push_slice(text.as_bytes())
103            .map_err(ImmutableStringError::from)
104    }
105
106    /// Shrinks the underlying buffer to match the length of the string exactly.
107    /// This will likely reallocate the underlying buffer.
108    ///
109    /// # Errors
110    ///
111    /// For details see [`ImmutableStringError`].
112    pub fn shrink_to_fit(&mut self) -> Result<(), ImmutableStringError> {
113        self.internal.shrink_to_fit().map_err(ImmutableStringError::from)
114    }
115
116    /// Builds a new instance of [`ImmutableString`] out of the builder.
117    ///
118    /// # Errors
119    ///
120    /// For details see [`ImmutableStringError`].
121    pub fn build(self) -> Result<ImmutableString<TAllocator>, ImmutableStringError> {
122        let mut internal = unsafe { (&raw const self.internal).read() };
123        core::mem::forget(self);
124        internal.try_push_slice(b"\0")?;
125        internal.shrink_to_fit()?;
126        let result = ImmutableString::from_internal(internal.build());
127        Ok(result)
128    }
129}