1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{cmp, fmt, hash, mem, ops, ptr, slice, str};
4
5use crate::{translate::*, GStr};
6
7wrapper! {
8 // rustdoc-stripper-ignore-next
9 /// A mutable text buffer that grows automatically.
10 #[doc(alias = "GString")]
11 #[must_use = "The builder must be built to be used"]
12 pub struct GStringBuilder(BoxedInline<ffi::GString>);
13
14 match fn {
15 copy => |ptr| ffi::g_string_new_len((*ptr).str, (*ptr).len as isize),
16 free => |ptr| ffi::g_string_free(ptr, ffi::GTRUE),
17 init => |ptr| unsafe {
18 let inner = ffi::GString {
19 str: ffi::g_malloc(64) as *mut _,
20 len: 0,
21 allocated_len: 64,
22 };
23 ptr::write(inner.str, 0);
24
25 *ptr = inner;
26 },
27 copy_into => |dest, src| {
28 debug_assert!((*src).allocated_len > (*src).len);
29 let allocated_len = (*src).allocated_len;
30 let inner = ffi::GString {
31 str: ffi::g_malloc(allocated_len) as *mut _,
32 len: 0,
33 allocated_len,
34 };
35 // +1 to also copy the NUL-terminator
36 ptr::copy_nonoverlapping((*src).str, inner.str, (*src).len + 1);
37 *dest = inner;
38 },
39 clear => |ptr| {
40 ffi::g_free((*ptr).str as *mut _);
41 },
42 type_ => || ffi::g_gstring_get_type(),
43 }
44}
45
46unsafe impl Send for GStringBuilder {}
47unsafe impl Sync for GStringBuilder {}
48
49impl GStringBuilder {
50 #[doc(alias = "g_string_new_len")]
51 #[inline]
52 pub fn new<T: AsRef<str>>(data: T) -> GStringBuilder {
53 let data = data.as_ref();
54 assert!(data.len() < usize::MAX - 1);
55 unsafe {
56 let allocated_len = usize::next_power_of_two(std::cmp::max(data.len(), 64) + 1);
57 assert_ne!(allocated_len, 0);
58
59 let inner = ffi::GString {
60 str: ffi::g_malloc(allocated_len) as *mut _,
61 len: data.len(),
62 allocated_len,
63 };
64 if data.is_empty() {
65 ptr::write(inner.str, 0);
66 } else {
67 ptr::copy_nonoverlapping(data.as_ptr() as *const _, inner.str, data.len());
68 ptr::write(inner.str.add(data.len()), 0);
69 }
70 Self { inner }
71 }
72 }
73
74 #[doc(alias = "g_string_append")]
75 #[doc(alias = "g_string_append_len")]
76 #[inline]
77 pub fn append(&mut self, val: &str) {
78 unsafe {
79 ffi::g_string_append_len(
80 self.to_glib_none_mut().0,
81 val.as_ptr() as *const _,
82 val.len() as isize,
83 );
84 }
85 }
86
87 #[doc(alias = "g_string_prepend")]
88 #[doc(alias = "g_string_prepend_len")]
89 #[inline]
90 pub fn prepend(&mut self, val: &str) {
91 unsafe {
92 ffi::g_string_prepend_len(
93 self.to_glib_none_mut().0,
94 val.as_ptr() as *const _,
95 val.len() as isize,
96 );
97 }
98 }
99
100 #[doc(alias = "g_string_append_c")]
101 #[doc(alias = "g_string_append_unichar")]
102 #[inline]
103 pub fn append_c(&mut self, val: char) {
104 unsafe {
105 ffi::g_string_append_unichar(self.to_glib_none_mut().0, val.into_glib());
106 }
107 }
108
109 #[doc(alias = "g_string_prepend_c")]
110 #[doc(alias = "g_string_prepend_unichar")]
111 #[inline]
112 pub fn prepend_c(&mut self, val: char) {
113 unsafe {
114 ffi::g_string_prepend_unichar(self.to_glib_none_mut().0, val.into_glib());
115 }
116 }
117
118 // rustdoc-stripper-ignore-next
119 /// Returns `&[str]` slice.
120 #[inline]
121 pub fn as_str(&self) -> &str {
122 unsafe {
123 let ptr: *const u8 = self.inner.str as _;
124 let len: usize = self.inner.len;
125 if len == 0 {
126 return "";
127 }
128 let slice = slice::from_raw_parts(ptr, len);
129 std::str::from_utf8_unchecked(slice)
130 }
131 }
132
133 // rustdoc-stripper-ignore-next
134 /// Returns <code>&[GStr]</code> slice.
135 #[inline]
136 pub fn as_gstr(&self) -> &GStr {
137 unsafe {
138 let ptr: *const u8 = self.inner.str as _;
139 let len: usize = self.inner.len;
140 if len == 0 {
141 return Default::default();
142 }
143 let slice = slice::from_raw_parts(ptr, len + 1);
144 GStr::from_utf8_with_nul_unchecked(slice)
145 }
146 }
147
148 // rustdoc-stripper-ignore-next
149 /// Finalizes the builder, converting it to a [`GString`].
150 #[must_use = "String returned from the builder should probably be used"]
151 #[inline]
152 pub fn into_string(self) -> crate::GString {
153 unsafe {
154 let s = mem::ManuallyDrop::new(self);
155 crate::GString::from_ptr_and_len_unchecked(s.inner.str, s.inner.len)
156 }
157 }
158}
159
160impl Default for GStringBuilder {
161 // rustdoc-stripper-ignore-next
162 /// Creates a new empty string.
163 #[inline]
164 fn default() -> Self {
165 Self::new(data:"")
166 }
167}
168
169impl fmt::Debug for GStringBuilder {
170 #[inline]
171 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172 f.write_str(self.as_str())
173 }
174}
175
176impl fmt::Display for GStringBuilder {
177 #[inline]
178 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179 f.write_str(self.as_str())
180 }
181}
182
183impl PartialEq for GStringBuilder {
184 #[inline]
185 fn eq(&self, other: &Self) -> bool {
186 self.as_str() == other.as_str()
187 }
188}
189
190impl PartialEq<str> for GStringBuilder {
191 #[inline]
192 fn eq(&self, other: &str) -> bool {
193 self.as_str() == other
194 }
195}
196
197impl PartialEq<GStringBuilder> for str {
198 #[inline]
199 fn eq(&self, other: &GStringBuilder) -> bool {
200 self == other.as_str()
201 }
202}
203
204impl PartialEq<GStr> for GStringBuilder {
205 #[inline]
206 fn eq(&self, other: &GStr) -> bool {
207 self.as_gstr() == other
208 }
209}
210
211impl PartialEq<GStringBuilder> for GStr {
212 #[inline]
213 fn eq(&self, other: &GStringBuilder) -> bool {
214 self == other.as_gstr()
215 }
216}
217
218impl Eq for GStringBuilder {}
219
220impl cmp::PartialOrd for GStringBuilder {
221 #[inline]
222 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
223 Some(self.cmp(other))
224 }
225}
226
227impl cmp::PartialOrd<str> for GStringBuilder {
228 #[inline]
229 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
230 Some(self.as_str().cmp(other))
231 }
232}
233
234impl cmp::PartialOrd<GStringBuilder> for str {
235 #[inline]
236 fn partial_cmp(&self, other: &GStringBuilder) -> Option<cmp::Ordering> {
237 Some(self.cmp(other.as_str()))
238 }
239}
240
241impl cmp::PartialOrd<GStr> for GStringBuilder {
242 #[inline]
243 fn partial_cmp(&self, other: &GStr) -> Option<cmp::Ordering> {
244 Some(self.as_gstr().cmp(other))
245 }
246}
247
248impl cmp::PartialOrd<GStringBuilder> for GStr {
249 #[inline]
250 fn partial_cmp(&self, other: &GStringBuilder) -> Option<cmp::Ordering> {
251 Some(self.cmp(other.as_gstr()))
252 }
253}
254
255impl cmp::Ord for GStringBuilder {
256 #[inline]
257 fn cmp(&self, other: &Self) -> cmp::Ordering {
258 self.as_str().cmp(other.as_str())
259 }
260}
261
262impl hash::Hash for GStringBuilder {
263 #[inline]
264 fn hash<H>(&self, state: &mut H)
265 where
266 H: hash::Hasher,
267 {
268 self.as_str().hash(state)
269 }
270}
271
272impl AsRef<[u8]> for GStringBuilder {
273 #[inline]
274 fn as_ref(&self) -> &[u8] {
275 self.as_bytes()
276 }
277}
278
279impl AsRef<str> for GStringBuilder {
280 #[inline]
281 fn as_ref(&self) -> &str {
282 self.as_str()
283 }
284}
285
286impl AsRef<GStr> for GStringBuilder {
287 #[inline]
288 fn as_ref(&self) -> &GStr {
289 self.as_gstr()
290 }
291}
292
293impl ops::Deref for GStringBuilder {
294 type Target = str;
295
296 #[inline]
297 fn deref(&self) -> &str {
298 self.as_str()
299 }
300}
301
302impl fmt::Write for GStringBuilder {
303 #[inline]
304 fn write_str(&mut self, s: &str) -> fmt::Result {
305 self.append(val:s);
306 Ok(())
307 }
308
309 #[inline]
310 fn write_char(&mut self, c: char) -> fmt::Result {
311 self.append_c(val:c);
312 Ok(())
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 #[test]
319 fn append() {
320 let mut s = crate::GStringBuilder::new("");
321 assert_eq!(&*s, "");
322 s.append("Hello");
323 s.append(" ");
324 s.append("there!");
325 assert_eq!(&*s, "Hello there!");
326 assert_eq!(s.into_string().as_str(), "Hello there!");
327 }
328
329 #[test]
330 fn prepend() {
331 let mut s = crate::GStringBuilder::new("456");
332 assert_eq!(&*s, "456");
333 s.prepend("123");
334 assert_eq!(&*s, "123456");
335 }
336
337 #[test]
338 fn default() {
339 let s1: crate::GStringBuilder = Default::default();
340 assert_eq!(&*s1, "");
341 }
342
343 #[test]
344 fn display() {
345 let s: crate::GStringBuilder = crate::GStringBuilder::new("This is a string.");
346 assert_eq!(&format!("{s}"), "This is a string.");
347 }
348
349 #[test]
350 fn eq() {
351 let a1 = crate::GStringBuilder::new("a");
352 let a2 = crate::GStringBuilder::new("a");
353 let b = crate::GStringBuilder::new("b");
354 assert_eq!(a1, a2);
355 assert_ne!(a1, b);
356 assert_ne!(a2, b);
357 }
358
359 #[test]
360 fn write() {
361 use std::fmt::Write;
362
363 let mut s = crate::GStringBuilder::default();
364 write!(&mut s, "bla bla {} bla", 123).unwrap();
365 assert_eq!(&*s, "bla bla 123 bla");
366 }
367}
368