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}