1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{cmp, fmt, hash, mem, ops, ptr, slice, str}; |
4 | |
5 | use crate::{translate::*, GStr}; |
6 | |
7 | wrapper! { |
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 | |
46 | unsafe impl Send for GStringBuilder {} |
47 | unsafe impl Sync for GStringBuilder {} |
48 | |
49 | impl 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 | |
160 | impl 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 | |
169 | impl 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 | |
176 | impl 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 | |
183 | impl PartialEq for GStringBuilder { |
184 | #[inline ] |
185 | fn eq(&self, other: &Self) -> bool { |
186 | self.as_str() == other.as_str() |
187 | } |
188 | } |
189 | |
190 | impl PartialEq<str> for GStringBuilder { |
191 | #[inline ] |
192 | fn eq(&self, other: &str) -> bool { |
193 | self.as_str() == other |
194 | } |
195 | } |
196 | |
197 | impl PartialEq<GStringBuilder> for str { |
198 | #[inline ] |
199 | fn eq(&self, other: &GStringBuilder) -> bool { |
200 | self == other.as_str() |
201 | } |
202 | } |
203 | |
204 | impl PartialEq<GStr> for GStringBuilder { |
205 | #[inline ] |
206 | fn eq(&self, other: &GStr) -> bool { |
207 | self.as_gstr() == other |
208 | } |
209 | } |
210 | |
211 | impl PartialEq<GStringBuilder> for GStr { |
212 | #[inline ] |
213 | fn eq(&self, other: &GStringBuilder) -> bool { |
214 | self == other.as_gstr() |
215 | } |
216 | } |
217 | |
218 | impl Eq for GStringBuilder {} |
219 | |
220 | impl cmp::PartialOrd for GStringBuilder { |
221 | #[inline ] |
222 | fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { |
223 | Some(self.cmp(other)) |
224 | } |
225 | } |
226 | |
227 | impl 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 | |
234 | impl 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 | |
241 | impl 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 | |
248 | impl 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 | |
255 | impl 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 | |
262 | impl 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 | |
272 | impl AsRef<[u8]> for GStringBuilder { |
273 | #[inline ] |
274 | fn as_ref(&self) -> &[u8] { |
275 | self.as_bytes() |
276 | } |
277 | } |
278 | |
279 | impl AsRef<str> for GStringBuilder { |
280 | #[inline ] |
281 | fn as_ref(&self) -> &str { |
282 | self.as_str() |
283 | } |
284 | } |
285 | |
286 | impl AsRef<GStr> for GStringBuilder { |
287 | #[inline ] |
288 | fn as_ref(&self) -> &GStr { |
289 | self.as_gstr() |
290 | } |
291 | } |
292 | |
293 | impl ops::Deref for GStringBuilder { |
294 | type Target = str; |
295 | |
296 | #[inline ] |
297 | fn deref(&self) -> &str { |
298 | self.as_str() |
299 | } |
300 | } |
301 | |
302 | impl 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)] |
317 | mod 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 | |