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}