glib/
gstring_builder.rs

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::{ffi, 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`](crate::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("")
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(s);
306        Ok(())
307    }
308
309    #[inline]
310    fn write_char(&mut self, c: char) -> fmt::Result {
311        self.append_c(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}