Skip to main content

osom_lib_strings/immutable/
immutable_string_builder.rs

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