1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{ |
4 | borrow::{Borrow, Cow}, |
5 | cmp::Ordering, |
6 | ffi::{CStr, CString, OsStr, OsString}, |
7 | fmt, hash, |
8 | marker::PhantomData, |
9 | mem, |
10 | ops::Deref, |
11 | os::raw::{c_char, c_void}, |
12 | path::{Path, PathBuf}, |
13 | ptr, slice, |
14 | }; |
15 | |
16 | use crate::{prelude::*, translate::*, value::FromValue, Type, Value}; |
17 | |
18 | // rustdoc-stripper-ignore-next |
19 | /// Representation of a borrowed [`GString`]. |
20 | /// |
21 | /// This type is very similar to [`std::ffi::CStr`], but with one added constraint: the string |
22 | /// must also be valid UTF-8. |
23 | #[repr (transparent)] |
24 | pub struct GStr(str); |
25 | |
26 | impl GStr { |
27 | // rustdoc-stripper-ignore-next |
28 | /// Creates a GLib string wrapper from a byte slice. |
29 | /// |
30 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring that the byte |
31 | /// slice is valid UTF-8 and is nul-terminated. |
32 | #[inline ] |
33 | pub fn from_utf8_with_nul(bytes: &[u8]) -> Result<&Self, GStrError> { |
34 | Self::check_trailing_nul(bytes)?; |
35 | std::str::from_utf8(bytes)?; |
36 | Ok(unsafe { mem::transmute(bytes) }) |
37 | } |
38 | // rustdoc-stripper-ignore-next |
39 | /// Creates a GLib string wrapper from a byte slice, checking for interior nul-bytes. |
40 | /// |
41 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring that the byte |
42 | /// slice is valid UTF-8, is nul-terminated, and does not contain any interior nul-bytes. |
43 | #[inline ] |
44 | pub fn from_utf8_with_nul_checked(bytes: &[u8]) -> Result<&Self, GStrError> { |
45 | Self::check_nuls(bytes)?; |
46 | std::str::from_utf8(bytes)?; |
47 | Ok(unsafe { mem::transmute(bytes) }) |
48 | } |
49 | // rustdoc-stripper-ignore-next |
50 | /// Unsafely creates a GLib string wrapper from a byte slice. |
51 | /// |
52 | /// This function will cast the provided `bytes` to a `GStr` wrapper without performing any |
53 | /// sanity checks. |
54 | /// |
55 | /// # Safety |
56 | /// |
57 | /// The provided slice **must** be valid UTF-8 and nul-terminated. It is undefined behavior to |
58 | /// pass a slice that does not uphold those conditions. |
59 | #[inline ] |
60 | pub const unsafe fn from_utf8_with_nul_unchecked(bytes: &[u8]) -> &Self { |
61 | debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0); |
62 | debug_assert!(std::str::from_utf8(bytes).is_ok()); |
63 | mem::transmute(bytes) |
64 | } |
65 | // rustdoc-stripper-ignore-next |
66 | /// Creates a GLib string wrapper from a byte slice, truncating it at the first nul-byte. |
67 | /// |
68 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring that the byte |
69 | /// slice is valid UTF-8 and contains at least one nul-byte. |
70 | #[inline ] |
71 | pub fn from_utf8_until_nul(bytes: &[u8]) -> Result<&Self, GStrError> { |
72 | let nul_pos = memchr::memchr(0, bytes).ok_or(GStrError::NoTrailingNul)?; |
73 | let bytes = unsafe { bytes.get_unchecked(..nul_pos + 1) }; |
74 | std::str::from_utf8(bytes)?; |
75 | Ok(unsafe { mem::transmute(bytes) }) |
76 | } |
77 | // rustdoc-stripper-ignore-next |
78 | /// Creates a GLib string wrapper from a string slice. |
79 | /// |
80 | /// The string slice must be terminated with a nul-byte. |
81 | /// |
82 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring |
83 | /// that the string slice is nul-terminated. |
84 | #[inline ] |
85 | pub fn from_str_with_nul(s: &str) -> Result<&Self, GStrError> { |
86 | Self::check_trailing_nul(s)?; |
87 | Ok(unsafe { mem::transmute(s) }) |
88 | } |
89 | // rustdoc-stripper-ignore-next |
90 | /// Creates a GLib string wrapper from a string slice, checking for interior nul-bytes. |
91 | /// |
92 | /// The string slice must be terminated with a nul-byte. |
93 | /// |
94 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring |
95 | /// that the string slice is nul-terminated and does not contain any interior nul-bytes. |
96 | #[inline ] |
97 | pub fn from_str_with_nul_checked(s: &str) -> Result<&Self, GStrError> { |
98 | Self::check_nuls(s)?; |
99 | Ok(unsafe { mem::transmute(s) }) |
100 | } |
101 | // rustdoc-stripper-ignore-next |
102 | /// Unsafely creates a GLib string wrapper from a string slice. The string slice must be |
103 | /// terminated with a nul-byte. |
104 | /// |
105 | /// This function will cast the provided string slice to a `GStr` without performing any sanity |
106 | /// checks. |
107 | /// |
108 | /// # Safety |
109 | /// |
110 | /// The provided string slice **must** be nul-terminated. It is undefined behavior to pass a |
111 | /// slice that does not uphold those conditions. |
112 | #[inline ] |
113 | pub const unsafe fn from_str_with_nul_unchecked(s: &str) -> &Self { |
114 | debug_assert!(!s.is_empty() && s.as_bytes()[s.len() - 1] == 0); |
115 | mem::transmute(s) |
116 | } |
117 | // rustdoc-stripper-ignore-next |
118 | /// Creates a GLib string wrapper from a string slice, truncating it at the first nul-byte. |
119 | /// |
120 | /// The string slice must contain at least one nul-byte. |
121 | /// |
122 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring |
123 | /// that the string slice contains at least one nul-byte. |
124 | #[inline ] |
125 | pub fn from_str_until_nul(s: &str) -> Result<&Self, GStrError> { |
126 | let b = s.as_bytes(); |
127 | let nul_pos = memchr::memchr(0, b).ok_or(GStrError::NoTrailingNul)?; |
128 | let s = unsafe { std::str::from_utf8_unchecked(b.get_unchecked(..nul_pos + 1)) }; |
129 | Ok(unsafe { mem::transmute(s) }) |
130 | } |
131 | // rustdoc-stripper-ignore-next |
132 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
133 | /// valid UTF-8 and nul-terminated. All constraints from [`CStr::from_ptr`] also apply here. |
134 | /// |
135 | /// # Safety |
136 | /// |
137 | /// See [`CStr::from_ptr`](CStr::from_ptr#safety). |
138 | #[inline ] |
139 | pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self { |
140 | let cstr = CStr::from_ptr(ptr); |
141 | Self::from_utf8_with_nul_unchecked(cstr.to_bytes_with_nul()) |
142 | } |
143 | // rustdoc-stripper-ignore-next |
144 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
145 | /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here. |
146 | /// |
147 | /// If the string is valid UTF-8 then it is directly returned, otherwise `None` is returned. |
148 | #[inline ] |
149 | pub unsafe fn from_ptr_checked<'a>(ptr: *const c_char) -> Option<&'a Self> { |
150 | let mut end_ptr = ptr::null(); |
151 | if ffi::g_utf8_validate(ptr as *const _, -1, &mut end_ptr) != ffi::GFALSE { |
152 | Some(Self::from_utf8_with_nul_unchecked(slice::from_raw_parts( |
153 | ptr as *const u8, |
154 | end_ptr.offset_from(ptr) as usize + 1, |
155 | ))) |
156 | } else { |
157 | None |
158 | } |
159 | } |
160 | // rustdoc-stripper-ignore-next |
161 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
162 | /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here. |
163 | /// |
164 | /// If the string is valid UTF-8 then it is directly returned otherwise a copy is created with |
165 | /// every invalid character replaced by the Unicode replacement character (U+FFFD). |
166 | #[inline ] |
167 | pub unsafe fn from_ptr_lossy<'a>(ptr: *const c_char) -> Cow<'a, Self> { |
168 | if let Some(gs) = Self::from_ptr_checked(ptr) { |
169 | Cow::Borrowed(gs) |
170 | } else { |
171 | Cow::Owned(GString::from_glib_full(ffi::g_utf8_make_valid( |
172 | ptr as *const _, |
173 | -1, |
174 | ))) |
175 | } |
176 | } |
177 | // rustdoc-stripper-ignore-next |
178 | /// Converts this GLib string to a byte slice containing the trailing 0 byte. |
179 | /// |
180 | /// This function is the equivalent of [`GStr::to_bytes`] except that it will retain the |
181 | /// trailing nul terminator instead of chopping it off. |
182 | #[inline ] |
183 | pub const fn as_bytes_with_nul(&self) -> &[u8] { |
184 | self.0.as_bytes() |
185 | } |
186 | // rustdoc-stripper-ignore-next |
187 | /// Converts this GLib string to a byte slice. |
188 | /// |
189 | /// The returned slice will **not** contain the trailing nul terminator that this GLib |
190 | /// string has. |
191 | #[inline ] |
192 | pub const fn as_bytes(&self) -> &[u8] { |
193 | self.as_str().as_bytes() |
194 | } |
195 | // rustdoc-stripper-ignore-next |
196 | /// Returns the inner pointer to this GLib string. |
197 | /// |
198 | /// The returned pointer will be valid for as long as `self` is, and points to a contiguous |
199 | /// region of memory terminated with a 0 byte to represent the end of the string. |
200 | /// |
201 | /// **WARNING** |
202 | /// |
203 | /// The returned pointer is read-only; writing to it (including passing it to C code that |
204 | /// writes to it) causes undefined behavior. It is your responsibility to make |
205 | /// sure that the underlying memory is not freed too early. |
206 | #[inline ] |
207 | pub const fn as_ptr(&self) -> *const c_char { |
208 | self.0.as_ptr() as *const _ |
209 | } |
210 | // rustdoc-stripper-ignore-next |
211 | /// Converts this GLib string to a string slice. |
212 | #[inline ] |
213 | pub const fn as_str(&self) -> &str { |
214 | // Clip off the nul-byte |
215 | unsafe { |
216 | std::str::from_utf8_unchecked(std::slice::from_raw_parts( |
217 | self.as_ptr() as *const _, |
218 | self.0.len() - 1, |
219 | )) |
220 | } |
221 | } |
222 | // rustdoc-stripper-ignore-next |
223 | /// Converts this GLib string to a C string slice, checking for interior nul-bytes. |
224 | /// |
225 | /// Returns `Err` if the string contains any interior nul-bytes. |
226 | #[inline ] |
227 | pub fn to_cstr(&self) -> Result<&CStr, GStrInteriorNulError> { |
228 | Self::check_interior_nuls(self.as_bytes())?; |
229 | Ok(unsafe { self.to_cstr_unchecked() }) |
230 | } |
231 | // rustdoc-stripper-ignore-next |
232 | /// Converts this GLib string to a C string slice, truncating it at the first nul-byte. |
233 | #[inline ] |
234 | pub fn to_cstr_until_nul(&self) -> &CStr { |
235 | let b = self.as_bytes_with_nul(); |
236 | let nul_pos = memchr::memchr(0, b).unwrap(); |
237 | unsafe { CStr::from_bytes_with_nul_unchecked(b.get_unchecked(..nul_pos + 1)) } |
238 | } |
239 | // rustdoc-stripper-ignore-next |
240 | /// Converts this GLib string to a C string slice, without checking for interior nul-bytes. |
241 | /// |
242 | /// # Safety |
243 | /// |
244 | /// `self` **must** not contain any interior nul-bytes besides the final terminating nul-byte. |
245 | /// It is undefined behavior to call this on a string that contains interior nul-bytes. |
246 | #[inline ] |
247 | pub const unsafe fn to_cstr_unchecked(&self) -> &CStr { |
248 | CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) |
249 | } |
250 | |
251 | #[doc (alias = "g_utf8_collate" )] |
252 | #[doc (alias = "utf8_collate" )] |
253 | pub fn collate(&self, other: impl IntoGStr) -> Ordering { |
254 | other.run_with_gstr(|other| { |
255 | unsafe { ffi::g_utf8_collate(self.to_glib_none().0, other.to_glib_none().0) }.cmp(&0) |
256 | }) |
257 | } |
258 | |
259 | #[inline ] |
260 | fn check_nuls(s: impl AsRef<[u8]>) -> Result<(), GStrError> { |
261 | let s = s.as_ref(); |
262 | if let Some(nul_pos) = memchr::memchr(0, s) { |
263 | if s.len() == nul_pos + 1 { |
264 | Ok(()) |
265 | } else { |
266 | Err(GStrInteriorNulError(nul_pos).into()) |
267 | } |
268 | } else { |
269 | Err(GStrError::NoTrailingNul) |
270 | } |
271 | } |
272 | #[inline ] |
273 | fn check_trailing_nul(s: impl AsRef<[u8]>) -> Result<(), GStrError> { |
274 | if let Some(c) = s.as_ref().last().copied() { |
275 | if c == 0 { |
276 | return Ok(()); |
277 | } |
278 | } |
279 | Err(GStrError::NoTrailingNul) |
280 | } |
281 | // rustdoc-stripper-ignore-next |
282 | /// Returns `Err` if the string slice contains any nul-bytes. |
283 | #[inline ] |
284 | pub(crate) fn check_interior_nuls(s: impl AsRef<[u8]>) -> Result<(), GStrInteriorNulError> { |
285 | if let Some(nul_pos) = memchr::memchr(0, s.as_ref()) { |
286 | Err(GStrInteriorNulError(nul_pos)) |
287 | } else { |
288 | Ok(()) |
289 | } |
290 | } |
291 | pub const NONE: Option<&'static GStr> = None; |
292 | } |
293 | |
294 | // rustdoc-stripper-ignore-next |
295 | /// Error type holding all possible failures when creating a [`GStr`] reference. |
296 | #[derive (thiserror::Error, Debug)] |
297 | pub enum GStrError { |
298 | #[error(transparent)] |
299 | InvalidUtf8(#[from] std::str::Utf8Error), |
300 | #[error(transparent)] |
301 | InteriorNul(#[from] GStrInteriorNulError), |
302 | #[error("data provided is not nul terminated" )] |
303 | NoTrailingNul, |
304 | } |
305 | |
306 | // rustdoc-stripper-ignore-next |
307 | /// Error type indicating that a buffer had unexpected nul-bytes. |
308 | #[derive (thiserror::Error, Copy, Clone, PartialEq, Eq, Debug)] |
309 | #[error("data provided contains an interior nul-byte at byte pos {0}" )] |
310 | pub struct GStrInteriorNulError(usize); |
311 | |
312 | impl GStrInteriorNulError { |
313 | // rustdoc-stripper-ignore-next |
314 | /// Returns the position of the nul-byte in the slice that caused the conversion to fail. |
315 | #[inline ] |
316 | pub fn nul_position(&self) -> usize { |
317 | self.0 |
318 | } |
319 | } |
320 | |
321 | // rustdoc-stripper-ignore-next |
322 | /// Converts a static string literal into a static nul-terminated string. |
323 | /// |
324 | /// The expanded expression has type [`&'static GStr`]. This macro will panic if the |
325 | /// string literal contains any interior nul-bytes. |
326 | /// |
327 | /// # Examples |
328 | /// |
329 | /// ``` |
330 | /// # fn main() { |
331 | /// use glib::{gstr, GStr, GString}; |
332 | /// |
333 | /// const MY_STRING: &GStr = gstr!("Hello" ); |
334 | /// assert_eq!(MY_STRING.as_bytes_with_nul()[5], 0u8); |
335 | /// let owned: GString = MY_STRING.to_owned(); |
336 | /// assert_eq!(MY_STRING, owned); |
337 | /// # } |
338 | /// ``` |
339 | /// |
340 | /// [`&'static GStr`]: crate::GStr |
341 | #[macro_export ] |
342 | macro_rules! gstr { |
343 | ($s:literal) => { |
344 | unsafe { $crate::GStr::from_utf8_with_nul_unchecked($crate::cstr_bytes!($s)) } |
345 | }; |
346 | } |
347 | |
348 | impl fmt::Debug for GStr { |
349 | #[inline ] |
350 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
351 | <&str as fmt::Debug>::fmt(&self.as_str(), f) |
352 | } |
353 | } |
354 | |
355 | impl PartialEq for GStr { |
356 | #[inline ] |
357 | fn eq(&self, other: &Self) -> bool { |
358 | self.as_str().eq(other.as_str()) |
359 | } |
360 | } |
361 | |
362 | impl Eq for GStr {} |
363 | |
364 | impl PartialOrd for GStr { |
365 | #[inline ] |
366 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
367 | Some(self.cmp(other)) |
368 | } |
369 | } |
370 | |
371 | impl Ord for GStr { |
372 | #[inline ] |
373 | fn cmp(&self, other: &Self) -> Ordering { |
374 | self.as_str().cmp(other.as_str()) |
375 | } |
376 | } |
377 | |
378 | impl hash::Hash for GStr { |
379 | #[inline ] |
380 | fn hash<H: hash::Hasher>(&self, state: &mut H) { |
381 | self.as_str().hash(state) |
382 | } |
383 | } |
384 | |
385 | impl Default for &GStr { |
386 | #[inline ] |
387 | fn default() -> Self { |
388 | const SLICE: &[c_char] = &[0]; |
389 | unsafe { GStr::from_ptr(SLICE.as_ptr()) } |
390 | } |
391 | } |
392 | |
393 | impl fmt::Display for GStr { |
394 | #[inline ] |
395 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
396 | f.write_str(self.as_str()) |
397 | } |
398 | } |
399 | |
400 | impl<'a> TryFrom<&'a CStr> for &'a GStr { |
401 | type Error = std::str::Utf8Error; |
402 | #[inline ] |
403 | fn try_from(s: &'a CStr) -> Result<Self, Self::Error> { |
404 | s.to_str()?; |
405 | Ok(unsafe { GStr::from_utf8_with_nul_unchecked(bytes:s.to_bytes_with_nul()) }) |
406 | } |
407 | } |
408 | |
409 | impl PartialEq<GStr> for String { |
410 | #[inline ] |
411 | fn eq(&self, other: &GStr) -> bool { |
412 | self.as_str() == other.as_str() |
413 | } |
414 | } |
415 | |
416 | impl PartialEq<str> for GStr { |
417 | #[inline ] |
418 | fn eq(&self, other: &str) -> bool { |
419 | self.as_str() == other |
420 | } |
421 | } |
422 | |
423 | impl PartialEq<&str> for GStr { |
424 | #[inline ] |
425 | fn eq(&self, other: &&str) -> bool { |
426 | self.as_str() == *other |
427 | } |
428 | } |
429 | |
430 | impl PartialEq<GStr> for &str { |
431 | #[inline ] |
432 | fn eq(&self, other: &GStr) -> bool { |
433 | *self == other.as_str() |
434 | } |
435 | } |
436 | |
437 | impl PartialEq<String> for GStr { |
438 | #[inline ] |
439 | fn eq(&self, other: &String) -> bool { |
440 | self.as_str() == other.as_str() |
441 | } |
442 | } |
443 | |
444 | impl PartialEq<GStr> for str { |
445 | #[inline ] |
446 | fn eq(&self, other: &GStr) -> bool { |
447 | self == other.as_str() |
448 | } |
449 | } |
450 | |
451 | impl PartialOrd<GStr> for String { |
452 | #[inline ] |
453 | fn partial_cmp(&self, other: &GStr) -> Option<Ordering> { |
454 | Some(self.cmp(&String::from(other.as_str()))) |
455 | } |
456 | } |
457 | |
458 | impl PartialOrd<String> for GStr { |
459 | #[inline ] |
460 | fn partial_cmp(&self, other: &String) -> Option<Ordering> { |
461 | Some(self.as_str().cmp(other.as_str())) |
462 | } |
463 | } |
464 | |
465 | impl PartialOrd<GStr> for str { |
466 | #[inline ] |
467 | fn partial_cmp(&self, other: &GStr) -> Option<Ordering> { |
468 | Some(self.cmp(other.as_str())) |
469 | } |
470 | } |
471 | |
472 | impl PartialOrd<str> for GStr { |
473 | #[inline ] |
474 | fn partial_cmp(&self, other: &str) -> Option<Ordering> { |
475 | Some(self.as_str().cmp(other)) |
476 | } |
477 | } |
478 | |
479 | impl AsRef<GStr> for GStr { |
480 | #[inline ] |
481 | fn as_ref(&self) -> &GStr { |
482 | self |
483 | } |
484 | } |
485 | |
486 | impl AsRef<str> for GStr { |
487 | #[inline ] |
488 | fn as_ref(&self) -> &str { |
489 | self.as_str() |
490 | } |
491 | } |
492 | |
493 | impl AsRef<OsStr> for GStr { |
494 | #[inline ] |
495 | fn as_ref(&self) -> &OsStr { |
496 | OsStr::new(self.as_str()) |
497 | } |
498 | } |
499 | |
500 | impl AsRef<Path> for GStr { |
501 | #[inline ] |
502 | fn as_ref(&self) -> &Path { |
503 | Path::new(self.as_str()) |
504 | } |
505 | } |
506 | |
507 | impl AsRef<[u8]> for GStr { |
508 | #[inline ] |
509 | fn as_ref(&self) -> &[u8] { |
510 | self.as_bytes() |
511 | } |
512 | } |
513 | |
514 | impl Deref for GStr { |
515 | type Target = str; |
516 | |
517 | #[inline ] |
518 | fn deref(&self) -> &str { |
519 | self.as_str() |
520 | } |
521 | } |
522 | |
523 | impl ToOwned for GStr { |
524 | type Owned = GString; |
525 | |
526 | #[inline ] |
527 | fn to_owned(&self) -> Self::Owned { |
528 | let b = self.as_bytes_with_nul(); |
529 | if self.len() < INLINE_LEN { |
530 | let mut data = <[u8; INLINE_LEN]>::default(); |
531 | let b = self.as_bytes(); |
532 | unsafe { data.get_unchecked_mut(..b.len()) }.copy_from_slice(b); |
533 | return GString(Inner::Inline { |
534 | len: self.len() as u8, |
535 | data, |
536 | }); |
537 | } |
538 | let inner = unsafe { |
539 | let copy = ffi::g_strndup(b.as_ptr() as *const c_char, b.len()); |
540 | Inner::Foreign { |
541 | ptr: ptr::NonNull::new_unchecked(copy), |
542 | len: b.len() - 1, |
543 | } |
544 | }; |
545 | GString(inner) |
546 | } |
547 | } |
548 | |
549 | impl GlibPtrDefault for GStr { |
550 | type GlibType = *mut c_char; |
551 | } |
552 | |
553 | impl StaticType for GStr { |
554 | #[inline ] |
555 | fn static_type() -> Type { |
556 | str::static_type() |
557 | } |
558 | } |
559 | |
560 | #[doc (hidden)] |
561 | impl FromGlibPtrNone<*const u8> for &GStr { |
562 | #[inline ] |
563 | unsafe fn from_glib_none(ptr: *const u8) -> Self { |
564 | debug_assert!(!ptr.is_null()); |
565 | let cstr: &CStr = CStr::from_ptr(ptr as *const _); |
566 | debug_assert!(cstr.to_str().is_ok(), "C string is not valid utf-8" ); |
567 | GStr::from_utf8_with_nul_unchecked(bytes:cstr.to_bytes_with_nul()) |
568 | } |
569 | } |
570 | |
571 | #[doc (hidden)] |
572 | impl FromGlibPtrNone<*const i8> for &GStr { |
573 | #[inline ] |
574 | unsafe fn from_glib_none(ptr: *const i8) -> Self { |
575 | from_glib_none(ptr as *const u8) |
576 | } |
577 | } |
578 | |
579 | #[doc (hidden)] |
580 | impl FromGlibPtrNone<*mut u8> for &GStr { |
581 | #[inline ] |
582 | unsafe fn from_glib_none(ptr: *mut u8) -> Self { |
583 | from_glib_none(ptr as *const u8) |
584 | } |
585 | } |
586 | |
587 | #[doc (hidden)] |
588 | impl FromGlibPtrNone<*mut i8> for &GStr { |
589 | #[inline ] |
590 | unsafe fn from_glib_none(ptr: *mut i8) -> Self { |
591 | from_glib_none(ptr as *const u8) |
592 | } |
593 | } |
594 | |
595 | unsafe impl<'a> FromValue<'a> for &'a GStr { |
596 | type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>; |
597 | |
598 | #[inline ] |
599 | unsafe fn from_value(value: &'a Value) -> Self { |
600 | let ptr: *const i8 = gobject_ffi::g_value_get_string(value.to_glib_none().0); |
601 | let cstr: &CStr = CStr::from_ptr(ptr); |
602 | debug_assert!( |
603 | cstr.to_str().is_ok(), |
604 | "C string in glib::Value is not valid utf-8" |
605 | ); |
606 | GStr::from_utf8_with_nul_unchecked(bytes:cstr.to_bytes_with_nul()) |
607 | } |
608 | } |
609 | |
610 | impl ToValue for GStr { |
611 | #[inline ] |
612 | fn to_value(&self) -> Value { |
613 | self.as_str().to_value() |
614 | } |
615 | |
616 | #[inline ] |
617 | fn value_type(&self) -> Type { |
618 | str::static_type() |
619 | } |
620 | } |
621 | |
622 | impl ToValue for &GStr { |
623 | #[inline ] |
624 | fn to_value(&self) -> Value { |
625 | (*self).to_value() |
626 | } |
627 | |
628 | #[inline ] |
629 | fn value_type(&self) -> Type { |
630 | str::static_type() |
631 | } |
632 | } |
633 | |
634 | impl crate::value::ToValueOptional for GStr { |
635 | #[inline ] |
636 | fn to_value_optional(s: Option<&Self>) -> Value { |
637 | crate::value::ToValueOptional::to_value_optional(s.map(|s: &GStr| s.as_str())) |
638 | } |
639 | } |
640 | |
641 | #[doc (hidden)] |
642 | impl<'a> ToGlibPtr<'a, *const c_char> for GStr { |
643 | type Storage = PhantomData<&'a Self>; |
644 | |
645 | #[inline ] |
646 | fn to_glib_none(&'a self) -> Stash<'a, *const c_char, Self> { |
647 | Stash(self.as_ptr(), PhantomData) |
648 | } |
649 | |
650 | #[inline ] |
651 | fn to_glib_full(&self) -> *const c_char { |
652 | self.as_str().to_glib_full() |
653 | } |
654 | } |
655 | |
656 | #[doc (hidden)] |
657 | impl<'a> ToGlibPtr<'a, *mut c_char> for GStr { |
658 | type Storage = PhantomData<&'a Self>; |
659 | |
660 | #[inline ] |
661 | fn to_glib_none(&'a self) -> Stash<'a, *mut c_char, Self> { |
662 | Stash(self.as_ptr() as *mut c_char, PhantomData) |
663 | } |
664 | |
665 | #[inline ] |
666 | fn to_glib_full(&self) -> *mut c_char { |
667 | self.as_str().to_glib_full() |
668 | } |
669 | } |
670 | |
671 | // rustdoc-stripper-ignore-next |
672 | /// `NULL`-terminated UTF-8 string as stored in [`StrV`]. |
673 | /// |
674 | /// Unlike [`&GStr`] this does not have its length stored. |
675 | #[repr (transparent)] |
676 | pub struct GStringPtr(ptr::NonNull<c_char>); |
677 | |
678 | #[doc (hidden)] |
679 | unsafe impl TransparentPtrType for GStringPtr {} |
680 | |
681 | #[doc (hidden)] |
682 | impl GlibPtrDefault for GStringPtr { |
683 | type GlibType = *mut c_char; |
684 | } |
685 | |
686 | impl GStringPtr { |
687 | // rustdoc-stripper-ignore-next |
688 | /// Returns the corresponding [`&GStr`]. |
689 | #[inline ] |
690 | pub fn to_gstr(&self) -> &GStr { |
691 | unsafe { GStr::from_ptr(self.0.as_ptr()) } |
692 | } |
693 | |
694 | // rustdoc-stripper-ignore-next |
695 | /// Returns the corresponding [`&str`]. |
696 | #[inline ] |
697 | pub fn as_str(&self) -> &str { |
698 | self.to_gstr().as_str() |
699 | } |
700 | |
701 | // rustdoc-stripper-ignore-next |
702 | /// This is just an alias for [`as_str`]. |
703 | #[inline ] |
704 | #[deprecated = "Use as_str instead" ] |
705 | pub fn to_str(&self) -> &str { |
706 | self |
707 | } |
708 | |
709 | // rustdoc-stripper-ignore-next |
710 | /// Returns the string's C pointer. |
711 | #[inline ] |
712 | pub const fn as_ptr(&self) -> *const c_char { |
713 | self.0.as_ptr() |
714 | } |
715 | |
716 | // rustdoc-stripper-ignore-next |
717 | /// Wrapper around `libc::strcmp` returning `Ordering`. |
718 | /// |
719 | /// # Safety |
720 | /// |
721 | /// `a` and `b` must be non-null pointers to nul-terminated C strings. |
722 | #[inline ] |
723 | unsafe fn strcmp(a: *const c_char, b: *const c_char) -> Ordering { |
724 | from_glib(libc::strcmp(a, b)) |
725 | } |
726 | } |
727 | |
728 | impl Clone for GStringPtr { |
729 | #[inline ] |
730 | fn clone(&self) -> GStringPtr { |
731 | unsafe { GStringPtr(ptr::NonNull::new_unchecked(ptr:ffi::g_strdup(self.0.as_ptr()))) } |
732 | } |
733 | } |
734 | |
735 | impl Deref for GStringPtr { |
736 | type Target = str; |
737 | |
738 | #[inline ] |
739 | fn deref(&self) -> &str { |
740 | self.as_str() |
741 | } |
742 | } |
743 | |
744 | impl IntoGlibPtr<*mut c_char> for GStringPtr { |
745 | #[inline ] |
746 | unsafe fn into_glib_ptr(self) -> *mut c_char { |
747 | self.0.as_ptr() |
748 | } |
749 | } |
750 | |
751 | impl Drop for GStringPtr { |
752 | #[inline ] |
753 | fn drop(&mut self) { |
754 | unsafe { |
755 | ffi::g_free(self.0.as_ptr() as *mut _); |
756 | } |
757 | } |
758 | } |
759 | |
760 | impl fmt::Debug for GStringPtr { |
761 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
762 | <&GStr as fmt::Debug>::fmt(&self.to_gstr(), f) |
763 | } |
764 | } |
765 | |
766 | impl fmt::Display for GStringPtr { |
767 | #[inline ] |
768 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
769 | f.write_str(self.as_str()) |
770 | } |
771 | } |
772 | |
773 | impl Eq for GStringPtr {} |
774 | |
775 | impl PartialEq for GStringPtr { |
776 | #[inline ] |
777 | fn eq(&self, other: &GStringPtr) -> bool { |
778 | self.partial_cmp(other) == Some(Ordering::Equal) |
779 | } |
780 | } |
781 | |
782 | impl PartialEq<GStringPtr> for String { |
783 | #[inline ] |
784 | fn eq(&self, other: &GStringPtr) -> bool { |
785 | self.partial_cmp(other) == Some(Ordering::Equal) |
786 | } |
787 | } |
788 | |
789 | impl PartialEq<GStringPtr> for GString { |
790 | #[inline ] |
791 | fn eq(&self, other: &GStringPtr) -> bool { |
792 | self.partial_cmp(other) == Some(Ordering::Equal) |
793 | } |
794 | } |
795 | |
796 | impl PartialEq<str> for GStringPtr { |
797 | #[inline ] |
798 | fn eq(&self, other: &str) -> bool { |
799 | self.partial_cmp(other) == Some(Ordering::Equal) |
800 | } |
801 | } |
802 | |
803 | impl PartialEq<&str> for GStringPtr { |
804 | #[inline ] |
805 | fn eq(&self, other: &&str) -> bool { |
806 | self.partial_cmp(other) == Some(Ordering::Equal) |
807 | } |
808 | } |
809 | |
810 | impl PartialEq<GStr> for GStringPtr { |
811 | #[inline ] |
812 | fn eq(&self, other: &GStr) -> bool { |
813 | self.partial_cmp(other) == Some(Ordering::Equal) |
814 | } |
815 | } |
816 | |
817 | impl PartialEq<&GStr> for GStringPtr { |
818 | #[inline ] |
819 | fn eq(&self, other: &&GStr) -> bool { |
820 | self.partial_cmp(other) == Some(Ordering::Equal) |
821 | } |
822 | } |
823 | |
824 | impl PartialEq<GStringPtr> for &str { |
825 | #[inline ] |
826 | fn eq(&self, other: &GStringPtr) -> bool { |
827 | self.partial_cmp(other) == Some(Ordering::Equal) |
828 | } |
829 | } |
830 | |
831 | impl PartialEq<GStringPtr> for &GStr { |
832 | #[inline ] |
833 | fn eq(&self, other: &GStringPtr) -> bool { |
834 | self.partial_cmp(other) == Some(Ordering::Equal) |
835 | } |
836 | } |
837 | |
838 | impl PartialEq<String> for GStringPtr { |
839 | #[inline ] |
840 | fn eq(&self, other: &String) -> bool { |
841 | self.partial_cmp(other) == Some(Ordering::Equal) |
842 | } |
843 | } |
844 | |
845 | impl PartialEq<GString> for GStringPtr { |
846 | #[inline ] |
847 | fn eq(&self, other: &GString) -> bool { |
848 | self.partial_cmp(other) == Some(Ordering::Equal) |
849 | } |
850 | } |
851 | |
852 | impl PartialEq<GStringPtr> for str { |
853 | #[inline ] |
854 | fn eq(&self, other: &GStringPtr) -> bool { |
855 | self.partial_cmp(other) == Some(Ordering::Equal) |
856 | } |
857 | } |
858 | |
859 | impl PartialEq<GStringPtr> for GStr { |
860 | #[inline ] |
861 | fn eq(&self, other: &GStringPtr) -> bool { |
862 | self.partial_cmp(other) == Some(Ordering::Equal) |
863 | } |
864 | } |
865 | |
866 | impl PartialOrd<GStringPtr> for GStringPtr { |
867 | #[inline ] |
868 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
869 | Some(self.cmp(other)) |
870 | } |
871 | } |
872 | |
873 | impl Ord for GStringPtr { |
874 | #[inline ] |
875 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
876 | unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) } |
877 | } |
878 | } |
879 | |
880 | impl PartialOrd<GStringPtr> for String { |
881 | #[inline ] |
882 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
883 | Some(self.as_str().cmp(other)) |
884 | } |
885 | } |
886 | |
887 | impl PartialOrd<GStringPtr> for GString { |
888 | #[inline ] |
889 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
890 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
891 | } |
892 | } |
893 | |
894 | impl PartialOrd<String> for GStringPtr { |
895 | #[inline ] |
896 | fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> { |
897 | Some(self.as_str().cmp(other)) |
898 | } |
899 | } |
900 | |
901 | impl PartialOrd<GString> for GStringPtr { |
902 | #[inline ] |
903 | fn partial_cmp(&self, other: &GString) -> Option<std::cmp::Ordering> { |
904 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
905 | } |
906 | } |
907 | |
908 | impl PartialOrd<GStringPtr> for str { |
909 | #[inline ] |
910 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
911 | Some(self.cmp(other.as_str())) |
912 | } |
913 | } |
914 | |
915 | impl PartialOrd<GStringPtr> for GStr { |
916 | #[inline ] |
917 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
918 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
919 | } |
920 | } |
921 | |
922 | impl PartialOrd<str> for GStringPtr { |
923 | #[inline ] |
924 | fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> { |
925 | Some(self.as_str().cmp(other)) |
926 | } |
927 | } |
928 | |
929 | impl PartialOrd<&str> for GStringPtr { |
930 | #[inline ] |
931 | fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> { |
932 | Some(self.as_str().cmp(other)) |
933 | } |
934 | } |
935 | |
936 | impl PartialOrd<GStr> for GStringPtr { |
937 | #[inline ] |
938 | fn partial_cmp(&self, other: &GStr) -> Option<std::cmp::Ordering> { |
939 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
940 | } |
941 | } |
942 | |
943 | impl PartialOrd<&GStr> for GStringPtr { |
944 | #[inline ] |
945 | fn partial_cmp(&self, other: &&GStr) -> Option<std::cmp::Ordering> { |
946 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
947 | } |
948 | } |
949 | |
950 | impl PartialOrd<GStringPtr> for &str { |
951 | #[inline ] |
952 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
953 | Some(self.cmp(&other.as_str())) |
954 | } |
955 | } |
956 | |
957 | impl PartialOrd<GStringPtr> for &GStr { |
958 | #[inline ] |
959 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
960 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
961 | } |
962 | } |
963 | |
964 | impl AsRef<GStringPtr> for GStringPtr { |
965 | #[inline ] |
966 | fn as_ref(&self) -> &GStringPtr { |
967 | self |
968 | } |
969 | } |
970 | |
971 | impl std::hash::Hash for GStringPtr { |
972 | #[inline ] |
973 | fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
974 | self.as_str().hash(state); |
975 | } |
976 | } |
977 | |
978 | // size_of::<Inner>() minus two bytes for length and enum discriminant |
979 | const INLINE_LEN: usize = |
980 | mem::size_of::<Option<Box<str>>>() + mem::size_of::<usize>() - mem::size_of::<u8>() * 2; |
981 | |
982 | // rustdoc-stripper-ignore-next |
983 | /// A type representing an owned, C-compatible, nul-terminated UTF-8 string. |
984 | /// |
985 | /// `GString` is to <code>&[GStr]</code> as [`String`] is to <code>&[str]</code>: the former in |
986 | /// each pair are owned strings; the latter are borrowed references. |
987 | /// |
988 | /// This type is similar to [`std::ffi::CString`], but with some special behavior. When debug |
989 | /// assertions are enabled, <code>[From]<[String]></code> will panic if there are interior |
990 | /// nul-bytes. In production builds, no checks will be made for interior nul-bytes, and strings |
991 | /// that contain interior nul-bytes will simply end at first nul-byte when converting to a C |
992 | /// string. |
993 | /// |
994 | /// The constructors beginning with `from_utf8` `and `from_string` can also be used to further |
995 | /// control how interior nul-bytes are handled. |
996 | pub struct GString(Inner); |
997 | |
998 | enum Inner { |
999 | Native(Box<str>), |
1000 | Foreign { |
1001 | ptr: ptr::NonNull<c_char>, |
1002 | len: usize, |
1003 | }, |
1004 | Inline { |
1005 | len: u8, |
1006 | data: [u8; INLINE_LEN], |
1007 | }, |
1008 | } |
1009 | |
1010 | unsafe impl Send for GString {} |
1011 | unsafe impl Sync for GString {} |
1012 | |
1013 | impl GString { |
1014 | // rustdoc-stripper-ignore-next |
1015 | /// Creates a new empty [`GString`]. |
1016 | /// |
1017 | /// Does not allocate. |
1018 | #[inline ] |
1019 | pub fn new() -> Self { |
1020 | Self(Inner::Inline { |
1021 | len: 0, |
1022 | data: Default::default(), |
1023 | }) |
1024 | } |
1025 | // rustdoc-stripper-ignore-next |
1026 | /// Formats an [`Arguments`](std::fmt::Arguments) into a [`GString`]. |
1027 | /// |
1028 | /// This function is the same as [`std::fmt::format`], except it returns a [`GString`]. The |
1029 | /// [`Arguments`](std::fmt::Arguments) instance can be created with the |
1030 | /// [`format_args!`](std::format_args) macro. |
1031 | /// |
1032 | /// Please note that using [`gformat!`] might be preferable. |
1033 | pub fn format(args: fmt::Arguments) -> Self { |
1034 | if let Some(s) = args.as_str() { |
1035 | return Self::from(s); |
1036 | } |
1037 | |
1038 | let mut s = crate::GStringBuilder::default(); |
1039 | fmt::Write::write_fmt(&mut s, args).unwrap(); |
1040 | s.into_string() |
1041 | } |
1042 | // rustdoc-stripper-ignore-next |
1043 | /// Creates a GLib string by consuming a byte vector. |
1044 | /// |
1045 | /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8. |
1046 | /// |
1047 | /// A trailing nul-byte will be appended by this function. |
1048 | #[inline ] |
1049 | pub fn from_utf8(bytes: Vec<u8>) -> Result<Self, std::string::FromUtf8Error> { |
1050 | Ok(Self::from_string_unchecked(String::from_utf8(bytes)?)) |
1051 | } |
1052 | // rustdoc-stripper-ignore-next |
1053 | /// Creates a GLib string by consuming a byte vector, checking for interior nul-bytes. |
1054 | /// |
1055 | /// Takes ownership of `bytes`, as long as it is valid UTF-8 and does not contain any interior |
1056 | /// nul-bytes. Otherwise, `Err` is returned. |
1057 | /// |
1058 | /// A trailing nul-byte will be appended by this function. |
1059 | #[inline ] |
1060 | pub fn from_utf8_checked(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> { |
1061 | Ok(Self::from_string_checked(String::from_utf8(bytes)?) |
1062 | .map_err(|e| GStringInteriorNulError(e.0.into_bytes(), e.1))?) |
1063 | } |
1064 | // rustdoc-stripper-ignore-next |
1065 | /// Unsafely creates a GLib string by consuming a byte vector, without checking for UTF-8 or |
1066 | /// interior nul-bytes. |
1067 | /// |
1068 | /// A trailing nul-byte will be appended by this function. |
1069 | /// |
1070 | /// # Safety |
1071 | /// |
1072 | /// The byte vector **must** not contain invalid UTF-8 characters. It is undefined behavior to |
1073 | /// pass a vector that contains invalid UTF-8. |
1074 | #[inline ] |
1075 | pub unsafe fn from_utf8_unchecked(mut v: Vec<u8>) -> Self { |
1076 | if v.is_empty() { |
1077 | Self::new() |
1078 | } else { |
1079 | v.reserve_exact(1); |
1080 | v.push(0); |
1081 | Self(Inner::Native(String::from_utf8_unchecked(v).into())) |
1082 | } |
1083 | } |
1084 | // rustdoc-stripper-ignore-next |
1085 | /// Creates a GLib string by consuming a nul-terminated byte vector, without checking for |
1086 | /// interior nul-bytes. |
1087 | /// |
1088 | /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8 or does not have a |
1089 | /// trailing nul-byte. |
1090 | #[inline ] |
1091 | pub fn from_utf8_with_nul(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> { |
1092 | let s = String::from_utf8(bytes)?; |
1093 | if s.as_bytes().last().copied() != Some(0u8) { |
1094 | return Err(GStringNoTrailingNulError(s.into_bytes()).into()); |
1095 | } |
1096 | if s.len() == 1 { |
1097 | Ok(Self::new()) |
1098 | } else { |
1099 | Ok(Self(Inner::Native(s.into()))) |
1100 | } |
1101 | } |
1102 | // rustdoc-stripper-ignore-next |
1103 | /// Creates a GLib string by consuming a nul-terminated byte vector. |
1104 | /// |
1105 | /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8, does not have a |
1106 | /// trailing nul-byte, or contains interior nul-bytes. |
1107 | #[inline ] |
1108 | pub fn from_utf8_with_nul_checked(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> { |
1109 | let s = Self::from_utf8_with_nul(bytes)?; |
1110 | if let Err(e) = GStr::check_interior_nuls(&s) { |
1111 | return Err(GStringInteriorNulError(s.into_bytes(), e).into()); |
1112 | } |
1113 | Ok(s) |
1114 | } |
1115 | // rustdoc-stripper-ignore-next |
1116 | /// Creates a GLib string by consuming a byte vector, without checking for UTF-8, a trailing |
1117 | /// nul-byte, or interior nul-bytes. |
1118 | /// |
1119 | /// # Safety |
1120 | /// |
1121 | /// The byte vector **must** not contain invalid UTF-8 characters, and **must** have a trailing |
1122 | /// nul-byte. It is undefined behavior to pass a vector that does not uphold those conditions. |
1123 | #[inline ] |
1124 | pub unsafe fn from_utf8_with_nul_unchecked(v: Vec<u8>) -> Self { |
1125 | debug_assert!(!v.is_empty() && v[v.len() - 1] == 0); |
1126 | let s = if cfg!(debug_assertions) { |
1127 | let s = String::from_utf8(v).unwrap(); |
1128 | GStr::check_interior_nuls(&s[..s.len() - 1]).unwrap(); |
1129 | s |
1130 | } else { |
1131 | String::from_utf8_unchecked(v) |
1132 | }; |
1133 | if s.len() == 1 { |
1134 | Self::new() |
1135 | } else { |
1136 | Self(Inner::Native(s.into())) |
1137 | } |
1138 | } |
1139 | // rustdoc-stripper-ignore-next |
1140 | /// Creates a GLib string by consuming a nul-terminated byte vector, truncating it at the first |
1141 | /// nul-byte. |
1142 | /// |
1143 | /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8 or does not contain |
1144 | /// at least one nul-byte. |
1145 | #[inline ] |
1146 | pub fn from_utf8_until_nul(mut bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> { |
1147 | let nul_pos = if let Some(nul_pos) = memchr::memchr(0, &bytes) { |
1148 | nul_pos |
1149 | } else { |
1150 | return Err(GStringNoTrailingNulError(bytes).into()); |
1151 | }; |
1152 | if nul_pos == 0 { |
1153 | Ok(Self::new()) |
1154 | } else { |
1155 | if let Err(e) = std::str::from_utf8(unsafe { bytes.get_unchecked(..nul_pos) }) { |
1156 | return Err(GStringUtf8Error(bytes, e).into()); |
1157 | } |
1158 | bytes.truncate(nul_pos + 1); |
1159 | let s = unsafe { String::from_utf8_unchecked(bytes) }; |
1160 | Ok(Self(Inner::Native(s.into()))) |
1161 | } |
1162 | } |
1163 | // rustdoc-stripper-ignore-next |
1164 | /// Creates a GLib string by consuming a string, checking for interior nul-bytes. |
1165 | /// |
1166 | /// Takes ownership of `s`, as long as it does not contain any interior nul-bytes. Otherwise, |
1167 | /// `Err` is returned. |
1168 | /// |
1169 | /// A trailing nul-byte will be appended by this function. |
1170 | #[inline ] |
1171 | pub fn from_string_checked(s: String) -> Result<Self, GStringInteriorNulError<String>> { |
1172 | if let Err(e) = GStr::check_interior_nuls(&s) { |
1173 | return Err(GStringInteriorNulError(s, e)); |
1174 | } |
1175 | Ok(Self::from_string_unchecked(s)) |
1176 | } |
1177 | // rustdoc-stripper-ignore-next |
1178 | /// Creates a GLib string by consuming a string, without checking for interior nul-bytes. |
1179 | /// |
1180 | /// A trailing nul-byte will be appended by this function. |
1181 | #[inline ] |
1182 | pub fn from_string_unchecked(mut s: String) -> Self { |
1183 | if s.is_empty() { |
1184 | Self::new() |
1185 | } else { |
1186 | s.reserve_exact(1); |
1187 | s.push(' \0' ); |
1188 | Self(Inner::Native(s.into())) |
1189 | } |
1190 | } |
1191 | // rustdoc-stripper-ignore-next |
1192 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
1193 | /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here. |
1194 | /// |
1195 | /// If the string is valid UTF-8 then it is directly returned otherwise a copy is created with |
1196 | /// every invalid character replaced by the Unicode replacement character (U+FFFD). |
1197 | #[inline ] |
1198 | pub unsafe fn from_ptr_lossy<'a>(ptr: *const c_char) -> Cow<'a, GStr> { |
1199 | GStr::from_ptr_lossy(ptr) |
1200 | } |
1201 | |
1202 | // rustdoc-stripper-ignore-next |
1203 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
1204 | /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here. |
1205 | /// |
1206 | /// `len` is the length without the nul-terminator, i.e. if `len == 0` is passed then `*ptr` |
1207 | /// must be the nul-terminator. |
1208 | #[inline ] |
1209 | pub unsafe fn from_ptr_and_len_unchecked(ptr: *const c_char, len: usize) -> Self { |
1210 | debug_assert!(!ptr.is_null()); |
1211 | |
1212 | GString(Inner::Foreign { |
1213 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
1214 | len, |
1215 | }) |
1216 | } |
1217 | |
1218 | // rustdoc-stripper-ignore-next |
1219 | /// Return the `GString` as string slice. |
1220 | #[inline ] |
1221 | pub fn as_str(&self) -> &str { |
1222 | unsafe { |
1223 | let (ptr, len) = match self.0 { |
1224 | Inner::Native(ref s) => (s.as_ptr(), s.len() - 1), |
1225 | Inner::Foreign { ptr, len } => (ptr.as_ptr() as *const u8, len), |
1226 | Inner::Inline { len, ref data } => (data.as_ptr(), len as usize), |
1227 | }; |
1228 | if len == 0 { |
1229 | "" |
1230 | } else { |
1231 | let slice = slice::from_raw_parts(ptr, len); |
1232 | std::str::from_utf8_unchecked(slice) |
1233 | } |
1234 | } |
1235 | } |
1236 | |
1237 | // rustdoc-stripper-ignore-next |
1238 | /// Extracts the [`GStr`] containing the entire string. |
1239 | #[inline ] |
1240 | pub fn as_gstr(&self) -> &GStr { |
1241 | let bytes = match self.0 { |
1242 | Inner::Native(ref s) => s.as_bytes(), |
1243 | Inner::Foreign { len: 0, .. } => &[0], |
1244 | Inner::Foreign { ptr, len } => unsafe { |
1245 | slice::from_raw_parts(ptr.as_ptr() as *const _, len + 1) |
1246 | }, |
1247 | Inner::Inline { len, ref data } => unsafe { data.get_unchecked(..len as usize + 1) }, |
1248 | }; |
1249 | unsafe { GStr::from_utf8_with_nul_unchecked(bytes) } |
1250 | } |
1251 | |
1252 | // rustdoc-stripper-ignore-next |
1253 | /// Return the underlying pointer of the `GString`. |
1254 | #[inline ] |
1255 | pub fn as_ptr(&self) -> *const c_char { |
1256 | match self.0 { |
1257 | Inner::Native(ref s) => s.as_ptr() as *const _, |
1258 | Inner::Foreign { ptr, .. } => ptr.as_ptr(), |
1259 | Inner::Inline { ref data, .. } => data.as_ptr() as *const _, |
1260 | } |
1261 | } |
1262 | |
1263 | // rustdoc-stripper-ignore-next |
1264 | /// Consumes the `GString` and returns the underlying byte buffer. |
1265 | /// |
1266 | /// The returned buffer is not guaranteed to contain a trailing nul-byte. |
1267 | #[inline ] |
1268 | pub fn into_bytes(mut self) -> Vec<u8> { |
1269 | match &mut self.0 { |
1270 | Inner::Native(s) => { |
1271 | let mut s = String::from(mem::replace(s, "" .into())); |
1272 | let _nul = s.pop(); |
1273 | debug_assert_eq!(_nul, Some(' \0' )); |
1274 | s.into_bytes() |
1275 | } |
1276 | Inner::Foreign { ptr, len } => { |
1277 | let bytes = unsafe { slice::from_raw_parts(ptr.as_ptr() as *const u8, *len - 1) }; |
1278 | bytes.to_owned() |
1279 | } |
1280 | Inner::Inline { len, data } => { |
1281 | unsafe { data.get_unchecked(..*len as usize) }.to_owned() |
1282 | } |
1283 | } |
1284 | } |
1285 | |
1286 | // rustdoc-stripper-ignore-next |
1287 | /// Consumes the `GString` and returns the underlying byte buffer, with trailing nul-byte. |
1288 | #[inline ] |
1289 | pub fn into_bytes_with_nul(mut self) -> Vec<u8> { |
1290 | match &mut self.0 { |
1291 | Inner::Native(s) => str::into_boxed_bytes(mem::replace(s, "" .into())).into(), |
1292 | Inner::Foreign { ptr, len } => { |
1293 | let bytes = unsafe { slice::from_raw_parts(ptr.as_ptr() as *const u8, *len) }; |
1294 | bytes.to_owned() |
1295 | } |
1296 | Inner::Inline { len, data } => { |
1297 | unsafe { data.get_unchecked(..*len as usize + 1) }.to_owned() |
1298 | } |
1299 | } |
1300 | } |
1301 | } |
1302 | |
1303 | // rustdoc-stripper-ignore-next |
1304 | /// Creates a [`GString`] using interpolation of runtime expressions. |
1305 | /// |
1306 | /// This macro is the same as [`std::format!`] except it returns a [`GString`]. It is faster than |
1307 | /// creating a [`String`] and then converting it manually to a [`GString`]. |
1308 | #[macro_export ] |
1309 | macro_rules! gformat { |
1310 | ($($arg:tt)*) => { $crate::GString::format(std::format_args!($($arg)*)) }; |
1311 | } |
1312 | |
1313 | // rustdoc-stripper-ignore-next |
1314 | /// Error type indicating that a buffer did not have a trailing nul-byte. |
1315 | /// |
1316 | /// `T` is the type of the value the conversion was attempted from. |
1317 | #[derive (thiserror::Error, Clone, PartialEq, Eq, Debug)] |
1318 | #[error("data provided is not nul terminated" )] |
1319 | pub struct GStringNoTrailingNulError<T>(T); |
1320 | |
1321 | impl<T> GStringNoTrailingNulError<T> { |
1322 | // rustdoc-stripper-ignore-next |
1323 | /// Returns the original value that was attempted to convert to [`GString`]. |
1324 | #[inline ] |
1325 | pub fn into_inner(self) -> T { |
1326 | self.0 |
1327 | } |
1328 | } |
1329 | |
1330 | // rustdoc-stripper-ignore-next |
1331 | /// Error type indicating that a buffer had unexpected nul-bytes. |
1332 | /// |
1333 | /// `T` is the type of the value the conversion was attempted from. |
1334 | #[derive (thiserror::Error, Clone, PartialEq, Eq, Debug)] |
1335 | #[error("{1}" )] |
1336 | pub struct GStringInteriorNulError<T>(T, GStrInteriorNulError); |
1337 | |
1338 | impl<T> GStringInteriorNulError<T> { |
1339 | // rustdoc-stripper-ignore-next |
1340 | /// Returns the original value that was attempted to convert to [`GString`]. |
1341 | #[inline ] |
1342 | pub fn into_inner(self) -> T { |
1343 | self.0 |
1344 | } |
1345 | // rustdoc-stripper-ignore-next |
1346 | /// Fetch a [`GStrInteriorNulError`] to get more details about the conversion failure. |
1347 | #[inline ] |
1348 | pub fn nul_error(&self) -> GStrInteriorNulError { |
1349 | self.1 |
1350 | } |
1351 | } |
1352 | |
1353 | // rustdoc-stripper-ignore-next |
1354 | /// Error type indicating that a buffer had invalid UTF-8. |
1355 | /// |
1356 | /// `T` is the type of the value the conversion was attempted from. |
1357 | #[derive (thiserror::Error, Clone, PartialEq, Eq, Debug)] |
1358 | #[error("{1}" )] |
1359 | pub struct GStringUtf8Error<T>(T, std::str::Utf8Error); |
1360 | |
1361 | impl<T> GStringUtf8Error<T> { |
1362 | // rustdoc-stripper-ignore-next |
1363 | /// Returns the original value that was attempted to convert to [`GString`]. |
1364 | #[inline ] |
1365 | pub fn into_inner(self) -> T { |
1366 | self.0 |
1367 | } |
1368 | // rustdoc-stripper-ignore-next |
1369 | /// Fetch a [`Utf8Error`](std::str::Utf8Error) to get more details about the conversion |
1370 | /// failure. |
1371 | #[inline ] |
1372 | pub fn utf8_error(&self) -> std::str::Utf8Error { |
1373 | self.1 |
1374 | } |
1375 | } |
1376 | |
1377 | // rustdoc-stripper-ignore-next |
1378 | /// Error type holding all possible failures when creating a [`GString`]. |
1379 | #[derive (thiserror::Error, Debug)] |
1380 | pub enum GStringFromError<T> { |
1381 | #[error(transparent)] |
1382 | NoTrailingNul(#[from] GStringNoTrailingNulError<T>), |
1383 | #[error(transparent)] |
1384 | InteriorNul(#[from] GStringInteriorNulError<T>), |
1385 | #[error(transparent)] |
1386 | InvalidUtf8(#[from] GStringUtf8Error<T>), |
1387 | #[error("unable to convert" )] |
1388 | Unspecified(T), |
1389 | } |
1390 | |
1391 | impl<T> GStringFromError<T> { |
1392 | pub fn into_inner(self) -> T { |
1393 | match self { |
1394 | Self::NoTrailingNul(GStringNoTrailingNulError(t)) => t, |
1395 | Self::InteriorNul(GStringInteriorNulError(t, _)) => t, |
1396 | Self::InvalidUtf8(GStringUtf8Error(t, _)) => t, |
1397 | Self::Unspecified(t) => t, |
1398 | } |
1399 | } |
1400 | #[inline ] |
1401 | fn convert<R>(self, func: impl FnOnce(T) -> R) -> GStringFromError<R> { |
1402 | match self { |
1403 | Self::NoTrailingNul(GStringNoTrailingNulError(t)) => { |
1404 | GStringFromError::NoTrailingNul(GStringNoTrailingNulError(func(t))) |
1405 | } |
1406 | Self::InteriorNul(GStringInteriorNulError(t, e)) => { |
1407 | GStringFromError::InteriorNul(GStringInteriorNulError(func(t), e)) |
1408 | } |
1409 | Self::InvalidUtf8(GStringUtf8Error(t, e)) => { |
1410 | GStringFromError::InvalidUtf8(GStringUtf8Error(func(t), e)) |
1411 | } |
1412 | Self::Unspecified(t) => GStringFromError::Unspecified(func(t)), |
1413 | } |
1414 | } |
1415 | } |
1416 | |
1417 | impl From<std::string::FromUtf8Error> for GStringFromError<Vec<u8>> { |
1418 | #[inline ] |
1419 | fn from(e: std::string::FromUtf8Error) -> Self { |
1420 | let ue: Utf8Error = e.utf8_error(); |
1421 | Self::InvalidUtf8(GStringUtf8Error(e.into_bytes(), ue)) |
1422 | } |
1423 | } |
1424 | |
1425 | impl IntoGlibPtr<*mut c_char> for GString { |
1426 | // rustdoc-stripper-ignore-next |
1427 | /// Transform into a nul-terminated raw C string pointer. |
1428 | #[inline ] |
1429 | unsafe fn into_glib_ptr(self) -> *mut c_char { |
1430 | match self.0 { |
1431 | Inner::Native(ref s: &Box) => ffi::g_strndup(str:s.as_ptr() as *const _, n:s.len()), |
1432 | Inner::Foreign { ptr: NonNull, .. } => { |
1433 | let _s: ManuallyDrop = mem::ManuallyDrop::new(self); |
1434 | ptr.as_ptr() |
1435 | } |
1436 | Inner::Inline { len: u8, ref data: &[u8; 22] } => { |
1437 | ffi::g_strndup(str:data.as_ptr() as *const _, n:len as usize) |
1438 | } |
1439 | } |
1440 | } |
1441 | } |
1442 | |
1443 | impl Default for GString { |
1444 | #[inline ] |
1445 | fn default() -> Self { |
1446 | Self::new() |
1447 | } |
1448 | } |
1449 | |
1450 | impl Clone for GString { |
1451 | #[inline ] |
1452 | fn clone(&self) -> GString { |
1453 | self.as_str().into() |
1454 | } |
1455 | } |
1456 | |
1457 | impl fmt::Debug for GString { |
1458 | #[inline ] |
1459 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1460 | <&str as fmt::Debug>::fmt(&self.as_str(), f) |
1461 | } |
1462 | } |
1463 | |
1464 | impl Drop for GString { |
1465 | #[inline ] |
1466 | fn drop(&mut self) { |
1467 | if let Inner::Foreign { ptr: NonNull, .. } = self.0 { |
1468 | unsafe { |
1469 | ffi::g_free(mem:ptr.as_ptr() as *mut _); |
1470 | } |
1471 | } |
1472 | } |
1473 | } |
1474 | |
1475 | impl fmt::Display for GString { |
1476 | #[inline ] |
1477 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1478 | f.write_str(self.as_str()) |
1479 | } |
1480 | } |
1481 | |
1482 | impl hash::Hash for GString { |
1483 | #[inline ] |
1484 | fn hash<H: hash::Hasher>(&self, state: &mut H) { |
1485 | self.as_str().hash(state) |
1486 | } |
1487 | } |
1488 | |
1489 | impl Borrow<GStr> for GString { |
1490 | #[inline ] |
1491 | fn borrow(&self) -> &GStr { |
1492 | self.as_gstr() |
1493 | } |
1494 | } |
1495 | |
1496 | impl Borrow<str> for GString { |
1497 | #[inline ] |
1498 | fn borrow(&self) -> &str { |
1499 | self.as_str() |
1500 | } |
1501 | } |
1502 | |
1503 | impl Ord for GString { |
1504 | #[inline ] |
1505 | fn cmp(&self, other: &GString) -> Ordering { |
1506 | self.as_str().cmp(other.as_str()) |
1507 | } |
1508 | } |
1509 | |
1510 | impl PartialOrd for GString { |
1511 | #[inline ] |
1512 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
1513 | Some(self.cmp(other)) |
1514 | } |
1515 | } |
1516 | |
1517 | impl PartialEq for GString { |
1518 | #[inline ] |
1519 | fn eq(&self, other: &GString) -> bool { |
1520 | self.as_str() == other.as_str() |
1521 | } |
1522 | } |
1523 | |
1524 | impl PartialEq<GString> for String { |
1525 | #[inline ] |
1526 | fn eq(&self, other: &GString) -> bool { |
1527 | self.as_str() == other.as_str() |
1528 | } |
1529 | } |
1530 | |
1531 | impl PartialEq<GStr> for GString { |
1532 | #[inline ] |
1533 | fn eq(&self, other: &GStr) -> bool { |
1534 | self.as_str() == other.as_str() |
1535 | } |
1536 | } |
1537 | |
1538 | impl PartialEq<&GStr> for GString { |
1539 | #[inline ] |
1540 | fn eq(&self, other: &&GStr) -> bool { |
1541 | self.as_str() == other.as_str() |
1542 | } |
1543 | } |
1544 | |
1545 | impl PartialEq<str> for GString { |
1546 | #[inline ] |
1547 | fn eq(&self, other: &str) -> bool { |
1548 | self.as_str() == other |
1549 | } |
1550 | } |
1551 | |
1552 | impl PartialEq<&str> for GString { |
1553 | #[inline ] |
1554 | fn eq(&self, other: &&str) -> bool { |
1555 | self.as_str() == *other |
1556 | } |
1557 | } |
1558 | |
1559 | impl PartialEq<GString> for &GStr { |
1560 | #[inline ] |
1561 | fn eq(&self, other: &GString) -> bool { |
1562 | self.as_str() == other.as_str() |
1563 | } |
1564 | } |
1565 | |
1566 | impl PartialEq<GString> for &str { |
1567 | #[inline ] |
1568 | fn eq(&self, other: &GString) -> bool { |
1569 | *self == other.as_str() |
1570 | } |
1571 | } |
1572 | |
1573 | impl PartialEq<String> for GString { |
1574 | #[inline ] |
1575 | fn eq(&self, other: &String) -> bool { |
1576 | self.as_str() == other.as_str() |
1577 | } |
1578 | } |
1579 | |
1580 | impl PartialEq<GString> for str { |
1581 | #[inline ] |
1582 | fn eq(&self, other: &GString) -> bool { |
1583 | self == other.as_str() |
1584 | } |
1585 | } |
1586 | |
1587 | impl PartialEq<GString> for GStr { |
1588 | #[inline ] |
1589 | fn eq(&self, other: &GString) -> bool { |
1590 | self.as_str() == other.as_str() |
1591 | } |
1592 | } |
1593 | |
1594 | impl PartialOrd<GString> for String { |
1595 | #[inline ] |
1596 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
1597 | Some(self.cmp(&String::from(other.as_str()))) |
1598 | } |
1599 | } |
1600 | |
1601 | impl PartialOrd<String> for GString { |
1602 | #[inline ] |
1603 | fn partial_cmp(&self, other: &String) -> Option<Ordering> { |
1604 | Some(self.as_str().cmp(other.as_str())) |
1605 | } |
1606 | } |
1607 | |
1608 | impl PartialOrd<GString> for GStr { |
1609 | #[inline ] |
1610 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
1611 | Some(self.as_str().cmp(other)) |
1612 | } |
1613 | } |
1614 | |
1615 | impl PartialOrd<GStr> for GString { |
1616 | #[inline ] |
1617 | fn partial_cmp(&self, other: &GStr) -> Option<Ordering> { |
1618 | Some(self.as_str().cmp(other.as_str())) |
1619 | } |
1620 | } |
1621 | |
1622 | impl PartialOrd<GString> for str { |
1623 | #[inline ] |
1624 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
1625 | Some(self.cmp(other)) |
1626 | } |
1627 | } |
1628 | |
1629 | impl PartialOrd<str> for GString { |
1630 | #[inline ] |
1631 | fn partial_cmp(&self, other: &str) -> Option<Ordering> { |
1632 | Some(self.as_str().cmp(other)) |
1633 | } |
1634 | } |
1635 | |
1636 | impl Eq for GString {} |
1637 | |
1638 | impl AsRef<GStr> for GString { |
1639 | #[inline ] |
1640 | fn as_ref(&self) -> &GStr { |
1641 | self.as_gstr() |
1642 | } |
1643 | } |
1644 | |
1645 | impl AsRef<str> for GString { |
1646 | #[inline ] |
1647 | fn as_ref(&self) -> &str { |
1648 | self.as_str() |
1649 | } |
1650 | } |
1651 | |
1652 | impl AsRef<OsStr> for GString { |
1653 | #[inline ] |
1654 | fn as_ref(&self) -> &OsStr { |
1655 | OsStr::new(self.as_str()) |
1656 | } |
1657 | } |
1658 | |
1659 | impl AsRef<Path> for GString { |
1660 | #[inline ] |
1661 | fn as_ref(&self) -> &Path { |
1662 | Path::new(self.as_str()) |
1663 | } |
1664 | } |
1665 | |
1666 | impl AsRef<[u8]> for GString { |
1667 | #[inline ] |
1668 | fn as_ref(&self) -> &[u8] { |
1669 | self.as_str().as_bytes() |
1670 | } |
1671 | } |
1672 | |
1673 | impl Deref for GString { |
1674 | type Target = str; |
1675 | |
1676 | #[inline ] |
1677 | fn deref(&self) -> &str { |
1678 | self.as_str() |
1679 | } |
1680 | } |
1681 | |
1682 | impl From<GString> for String { |
1683 | #[inline ] |
1684 | fn from(mut s: GString) -> Self { |
1685 | match &mut s.0 { |
1686 | Inner::Native(s: &mut Box) => { |
1687 | // Moves the underlying string |
1688 | let mut s: String = String::from(mem::replace(dest:s, src:"" .into())); |
1689 | let _nul: Option = s.pop(); |
1690 | debug_assert_eq!(_nul, Some(' \0' )); |
1691 | s |
1692 | } |
1693 | Inner::Foreign { len: &mut usize, .. } if *len == 0 => String::new(), |
1694 | Inner::Foreign { ptr: &mut NonNull, len: &mut usize } => unsafe { |
1695 | // Creates a copy |
1696 | let slice: &[u8] = slice::from_raw_parts(data:ptr.as_ptr() as *const u8, *len); |
1697 | std::str::from_utf8_unchecked(slice).into() |
1698 | }, |
1699 | Inner::Inline { len: &mut u8, data: &mut [u8; 22] } => unsafe { |
1700 | std::str::from_utf8_unchecked(data.get_unchecked(..*len as usize)).to_owned() |
1701 | }, |
1702 | } |
1703 | } |
1704 | } |
1705 | |
1706 | impl From<GString> for Box<str> { |
1707 | #[inline ] |
1708 | fn from(s: GString) -> Self { |
1709 | // Potentially creates a copy |
1710 | String::from(s).into() |
1711 | } |
1712 | } |
1713 | |
1714 | impl From<GString> for Vec<u8> { |
1715 | #[inline ] |
1716 | fn from(value: GString) -> Vec<u8> { |
1717 | value.into_bytes_with_nul() |
1718 | } |
1719 | } |
1720 | |
1721 | impl TryFrom<GString> for CString { |
1722 | type Error = GStringInteriorNulError<GString>; |
1723 | #[inline ] |
1724 | fn try_from(value: GString) -> Result<Self, Self::Error> { |
1725 | if let Some(nul_pos: usize) = memchr::memchr(needle:0, haystack:value.as_bytes()) { |
1726 | return Err(GStringInteriorNulError( |
1727 | value, |
1728 | GStrInteriorNulError(nul_pos), |
1729 | )); |
1730 | } |
1731 | let v: Vec = value.into_bytes_with_nul(); |
1732 | Ok(unsafe { CString::from_vec_with_nul_unchecked(v) }) |
1733 | } |
1734 | } |
1735 | |
1736 | impl From<GString> for OsString { |
1737 | #[inline ] |
1738 | fn from(s: GString) -> Self { |
1739 | OsString::from(String::from(s)) |
1740 | } |
1741 | } |
1742 | |
1743 | impl From<GString> for PathBuf { |
1744 | #[inline ] |
1745 | fn from(s: GString) -> Self { |
1746 | PathBuf::from(OsString::from(s)) |
1747 | } |
1748 | } |
1749 | |
1750 | impl From<String> for GString { |
1751 | #[inline ] |
1752 | fn from(mut s: String) -> Self { |
1753 | // Moves the content of the String |
1754 | if cfg!(debug_assertions) { |
1755 | GStr::check_interior_nuls(&s).unwrap(); |
1756 | } |
1757 | if s.is_empty() { |
1758 | Self::new() |
1759 | } else { |
1760 | s.reserve_exact(additional:1); |
1761 | s.push(ch:' \0' ); |
1762 | // No check for valid UTF-8 here |
1763 | Self(Inner::Native(s.into())) |
1764 | } |
1765 | } |
1766 | } |
1767 | |
1768 | impl From<Box<str>> for GString { |
1769 | #[inline ] |
1770 | fn from(s: Box<str>) -> Self { |
1771 | // Moves the content of the String |
1772 | s.into_string().into() |
1773 | } |
1774 | } |
1775 | |
1776 | impl<'a> From<Cow<'a, str>> for GString { |
1777 | #[inline ] |
1778 | fn from(s: Cow<'a, str>) -> Self { |
1779 | match s { |
1780 | Cow::Borrowed(s: &str) => Self::from(s), |
1781 | Cow::Owned(s: String) => Self::from(s), |
1782 | } |
1783 | } |
1784 | } |
1785 | |
1786 | impl From<&GStr> for GString { |
1787 | #[inline ] |
1788 | fn from(s: &GStr) -> GString { |
1789 | s.to_owned() |
1790 | } |
1791 | } |
1792 | |
1793 | impl From<&str> for GString { |
1794 | #[inline ] |
1795 | fn from(s: &str) -> Self { |
1796 | if cfg!(debug_assertions) { |
1797 | GStr::check_interior_nuls(s).unwrap(); |
1798 | } |
1799 | if s.len() < INLINE_LEN { |
1800 | let mut data = <[u8; INLINE_LEN]>::default(); |
1801 | let b = s.as_bytes(); |
1802 | unsafe { data.get_unchecked_mut(..b.len()) }.copy_from_slice(b); |
1803 | return Self(Inner::Inline { |
1804 | len: b.len() as u8, |
1805 | data, |
1806 | }); |
1807 | } |
1808 | // Allocates with the GLib allocator |
1809 | unsafe { |
1810 | // No check for valid UTF-8 here |
1811 | let copy = ffi::g_strndup(s.as_ptr() as *const c_char, s.len()); |
1812 | GString(Inner::Foreign { |
1813 | ptr: ptr::NonNull::new_unchecked(copy), |
1814 | len: s.len(), |
1815 | }) |
1816 | } |
1817 | } |
1818 | } |
1819 | |
1820 | impl From<&String> for GString { |
1821 | #[inline ] |
1822 | fn from(s: &String) -> Self { |
1823 | GString::from(s.as_str()) |
1824 | } |
1825 | } |
1826 | |
1827 | impl From<GStringPtr> for GString { |
1828 | #[inline ] |
1829 | fn from(s: GStringPtr) -> Self { |
1830 | let s: ManuallyDrop = mem::ManuallyDrop::new(s); |
1831 | let len: usize = unsafe { GStr::from_ptr(s.0.as_ptr()).len() }; |
1832 | GString(Inner::Foreign { ptr: s.0, len }) |
1833 | } |
1834 | } |
1835 | |
1836 | impl TryFrom<CString> for GString { |
1837 | type Error = GStringUtf8Error<CString>; |
1838 | #[inline ] |
1839 | fn try_from(value: CString) -> Result<Self, Self::Error> { |
1840 | if value.as_bytes().is_empty() { |
1841 | Ok(Self::new()) |
1842 | } else { |
1843 | // Moves the content of the CString |
1844 | // Also check if it's valid UTF-8 |
1845 | let s: String = String::from_utf8(value.into_bytes_with_nul()).map_err(|e: FromUtf8Error| { |
1846 | let err: Utf8Error = e.utf8_error(); |
1847 | GStringUtf8Error( |
1848 | unsafe { CString::from_vec_with_nul_unchecked(e.into_bytes()) }, |
1849 | err, |
1850 | ) |
1851 | })?; |
1852 | Ok(Self(Inner::Native(s.into()))) |
1853 | } |
1854 | } |
1855 | } |
1856 | |
1857 | impl TryFrom<OsString> for GString { |
1858 | type Error = GStringFromError<OsString>; |
1859 | #[inline ] |
1860 | fn try_from(value: OsString) -> Result<Self, Self::Error> { |
1861 | Self::from_string_checked(value.into_string().map_err(GStringFromError::Unspecified)?) |
1862 | .map_err(|e: GStringInteriorNulError| GStringFromError::from(e).convert(func:OsString::from)) |
1863 | } |
1864 | } |
1865 | |
1866 | impl TryFrom<PathBuf> for GString { |
1867 | type Error = GStringFromError<PathBuf>; |
1868 | #[inline ] |
1869 | fn try_from(value: PathBuf) -> Result<Self, Self::Error> { |
1870 | GString::try_from(value.into_os_string()).map_err(|e: GStringFromError| e.convert(func:PathBuf::from)) |
1871 | } |
1872 | } |
1873 | |
1874 | impl TryFrom<&CStr> for GString { |
1875 | type Error = std::str::Utf8Error; |
1876 | #[inline ] |
1877 | fn try_from(value: &CStr) -> Result<Self, Self::Error> { |
1878 | // Check if it's valid UTF-8 |
1879 | value.to_str()?; |
1880 | let gstr: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(bytes:value.to_bytes_with_nul()) }; |
1881 | Ok(gstr.to_owned()) |
1882 | } |
1883 | } |
1884 | |
1885 | impl<'a> From<Cow<'a, GStr>> for GString { |
1886 | #[inline ] |
1887 | fn from(s: Cow<'a, GStr>) -> Self { |
1888 | s.into_owned() |
1889 | } |
1890 | } |
1891 | |
1892 | impl<'a> From<&'a GString> for Cow<'a, GStr> { |
1893 | #[inline ] |
1894 | fn from(s: &'a GString) -> Self { |
1895 | Cow::Borrowed(s.as_gstr()) |
1896 | } |
1897 | } |
1898 | |
1899 | impl<'a> From<GString> for Cow<'a, GStr> { |
1900 | #[inline ] |
1901 | fn from(v: GString) -> Self { |
1902 | Cow::Owned(v) |
1903 | } |
1904 | } |
1905 | |
1906 | impl<'a> From<&'a GStr> for Cow<'a, GStr> { |
1907 | #[inline ] |
1908 | fn from(v: &'a GStr) -> Self { |
1909 | Cow::Borrowed(v) |
1910 | } |
1911 | } |
1912 | |
1913 | #[doc (hidden)] |
1914 | impl FromGlibPtrFull<*mut u8> for GString { |
1915 | #[inline ] |
1916 | unsafe fn from_glib_full(ptr: *mut u8) -> Self { |
1917 | debug_assert!(!ptr.is_null()); |
1918 | |
1919 | let cstr: &CStr = CStr::from_ptr(ptr as *const _); |
1920 | // Check for valid UTF-8 here |
1921 | debug_assert!(cstr.to_str().is_ok()); |
1922 | Self(Inner::Foreign { |
1923 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
1924 | len: cstr.to_bytes().len(), |
1925 | }) |
1926 | } |
1927 | } |
1928 | |
1929 | #[doc (hidden)] |
1930 | impl FromGlibPtrFull<*mut i8> for GString { |
1931 | #[inline ] |
1932 | unsafe fn from_glib_full(ptr: *mut i8) -> Self { |
1933 | from_glib_full(ptr as *mut u8) |
1934 | } |
1935 | } |
1936 | |
1937 | #[doc (hidden)] |
1938 | impl FromGlibPtrFull<*const u8> for GString { |
1939 | #[inline ] |
1940 | unsafe fn from_glib_full(ptr: *const u8) -> Self { |
1941 | from_glib_full(ptr as *mut u8) |
1942 | } |
1943 | } |
1944 | |
1945 | #[doc (hidden)] |
1946 | impl FromGlibPtrFull<*const i8> for GString { |
1947 | #[inline ] |
1948 | unsafe fn from_glib_full(ptr: *const i8) -> Self { |
1949 | from_glib_full(ptr as *mut u8) |
1950 | } |
1951 | } |
1952 | |
1953 | #[doc (hidden)] |
1954 | impl FromGlibPtrNone<*const u8> for GString { |
1955 | #[inline ] |
1956 | unsafe fn from_glib_none(ptr: *const u8) -> Self { |
1957 | debug_assert!(!ptr.is_null()); |
1958 | <&GStr>::from_glib_none(ptr).into() |
1959 | } |
1960 | } |
1961 | |
1962 | #[doc (hidden)] |
1963 | impl FromGlibPtrNone<*const i8> for GString { |
1964 | #[inline ] |
1965 | unsafe fn from_glib_none(ptr: *const i8) -> Self { |
1966 | from_glib_none(ptr as *const u8) |
1967 | } |
1968 | } |
1969 | |
1970 | #[doc (hidden)] |
1971 | impl FromGlibPtrNone<*mut u8> for GString { |
1972 | #[inline ] |
1973 | unsafe fn from_glib_none(ptr: *mut u8) -> Self { |
1974 | from_glib_none(ptr as *const u8) |
1975 | } |
1976 | } |
1977 | |
1978 | #[doc (hidden)] |
1979 | impl FromGlibPtrNone<*mut i8> for GString { |
1980 | #[inline ] |
1981 | unsafe fn from_glib_none(ptr: *mut i8) -> Self { |
1982 | from_glib_none(ptr as *const u8) |
1983 | } |
1984 | } |
1985 | |
1986 | #[doc (hidden)] |
1987 | impl FromGlibPtrBorrow<*const u8> for GString { |
1988 | #[inline ] |
1989 | unsafe fn from_glib_borrow(ptr: *const u8) -> Borrowed<Self> { |
1990 | debug_assert!(!ptr.is_null()); |
1991 | |
1992 | // Check for valid UTF-8 here |
1993 | let cstr: &CStr = CStr::from_ptr(ptr as *const _); |
1994 | debug_assert!(cstr.to_str().is_ok()); |
1995 | Borrowed::new(Self(Inner::Foreign { |
1996 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
1997 | len: cstr.to_bytes().len(), |
1998 | })) |
1999 | } |
2000 | } |
2001 | |
2002 | #[doc (hidden)] |
2003 | impl FromGlibPtrBorrow<*const i8> for GString { |
2004 | #[inline ] |
2005 | unsafe fn from_glib_borrow(ptr: *const i8) -> Borrowed<Self> { |
2006 | from_glib_borrow(ptr as *const u8) |
2007 | } |
2008 | } |
2009 | |
2010 | #[doc (hidden)] |
2011 | impl FromGlibPtrBorrow<*mut u8> for GString { |
2012 | #[inline ] |
2013 | unsafe fn from_glib_borrow(ptr: *mut u8) -> Borrowed<Self> { |
2014 | from_glib_borrow(ptr as *const u8) |
2015 | } |
2016 | } |
2017 | |
2018 | #[doc (hidden)] |
2019 | impl FromGlibPtrBorrow<*mut i8> for GString { |
2020 | #[inline ] |
2021 | unsafe fn from_glib_borrow(ptr: *mut i8) -> Borrowed<Self> { |
2022 | from_glib_borrow(ptr as *const u8) |
2023 | } |
2024 | } |
2025 | |
2026 | #[allow (clippy::unnecessary_cast)] |
2027 | #[doc (hidden)] |
2028 | impl<'a> ToGlibPtr<'a, *const u8> for GString { |
2029 | type Storage = PhantomData<&'a Self>; |
2030 | |
2031 | #[inline ] |
2032 | fn to_glib_none(&'a self) -> Stash<'a, *const u8, Self> { |
2033 | Stash(self.as_ptr() as *const u8, PhantomData) |
2034 | } |
2035 | |
2036 | #[inline ] |
2037 | fn to_glib_full(&self) -> *const u8 { |
2038 | unsafe { self.clone().into_glib_ptr() as *const u8 } |
2039 | } |
2040 | } |
2041 | |
2042 | #[allow (clippy::unnecessary_cast)] |
2043 | #[doc (hidden)] |
2044 | impl<'a> ToGlibPtr<'a, *const i8> for GString { |
2045 | type Storage = PhantomData<&'a Self>; |
2046 | |
2047 | #[inline ] |
2048 | fn to_glib_none(&'a self) -> Stash<'a, *const i8, Self> { |
2049 | Stash(self.as_ptr() as *const i8, PhantomData) |
2050 | } |
2051 | |
2052 | #[inline ] |
2053 | fn to_glib_full(&self) -> *const i8 { |
2054 | unsafe { self.clone().into_glib_ptr() as *const i8 } |
2055 | } |
2056 | } |
2057 | |
2058 | #[allow (clippy::unnecessary_cast)] |
2059 | #[doc (hidden)] |
2060 | impl<'a> ToGlibPtr<'a, *mut u8> for GString { |
2061 | type Storage = PhantomData<&'a Self>; |
2062 | |
2063 | #[inline ] |
2064 | fn to_glib_none(&'a self) -> Stash<'a, *mut u8, Self> { |
2065 | Stash(self.as_ptr() as *mut u8, PhantomData) |
2066 | } |
2067 | |
2068 | #[inline ] |
2069 | fn to_glib_full(&self) -> *mut u8 { |
2070 | unsafe { self.clone().into_glib_ptr() as *mut u8 } |
2071 | } |
2072 | } |
2073 | |
2074 | #[allow (clippy::unnecessary_cast)] |
2075 | #[doc (hidden)] |
2076 | impl<'a> ToGlibPtr<'a, *mut i8> for GString { |
2077 | type Storage = PhantomData<&'a Self>; |
2078 | |
2079 | #[inline ] |
2080 | fn to_glib_none(&'a self) -> Stash<'a, *mut i8, Self> { |
2081 | Stash(self.as_ptr() as *mut i8, PhantomData) |
2082 | } |
2083 | |
2084 | #[inline ] |
2085 | fn to_glib_full(&self) -> *mut i8 { |
2086 | unsafe { self.clone().into_glib_ptr() as *mut i8 } |
2087 | } |
2088 | } |
2089 | |
2090 | #[doc (hidden)] |
2091 | impl FromGlibContainer<*const c_char, *const i8> for GString { |
2092 | unsafe fn from_glib_none_num(ptr: *const i8, num: usize) -> Self { |
2093 | if num == 0 || ptr.is_null() { |
2094 | return Self::default(); |
2095 | } |
2096 | let slice = slice::from_raw_parts(ptr as *const u8, num); |
2097 | if cfg!(debug_assertions) { |
2098 | // Also check if it's valid UTF-8 |
2099 | std::str::from_utf8(slice).unwrap().into() |
2100 | } else { |
2101 | std::str::from_utf8_unchecked(slice).into() |
2102 | } |
2103 | } |
2104 | |
2105 | unsafe fn from_glib_container_num(ptr: *const i8, num: usize) -> Self { |
2106 | if num == 0 || ptr.is_null() { |
2107 | return Self::default(); |
2108 | } |
2109 | |
2110 | if cfg!(debug_assertions) { |
2111 | // Check if it's valid UTF-8 |
2112 | let slice = slice::from_raw_parts(ptr as *const u8, num); |
2113 | std::str::from_utf8(slice).unwrap(); |
2114 | } |
2115 | |
2116 | GString(Inner::Foreign { |
2117 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
2118 | len: num, |
2119 | }) |
2120 | } |
2121 | |
2122 | unsafe fn from_glib_full_num(ptr: *const i8, num: usize) -> Self { |
2123 | if num == 0 || ptr.is_null() { |
2124 | return Self::default(); |
2125 | } |
2126 | |
2127 | if cfg!(debug_assertions) { |
2128 | // Check if it's valid UTF-8 |
2129 | let slice = slice::from_raw_parts(ptr as *const u8, num); |
2130 | std::str::from_utf8(slice).unwrap(); |
2131 | } |
2132 | |
2133 | GString(Inner::Foreign { |
2134 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
2135 | len: num, |
2136 | }) |
2137 | } |
2138 | } |
2139 | |
2140 | #[doc (hidden)] |
2141 | impl FromGlibContainer<*const c_char, *mut i8> for GString { |
2142 | unsafe fn from_glib_none_num(ptr: *mut i8, num: usize) -> Self { |
2143 | FromGlibContainer::from_glib_none_num(ptr as *const i8, num) |
2144 | } |
2145 | |
2146 | unsafe fn from_glib_container_num(ptr: *mut i8, num: usize) -> Self { |
2147 | FromGlibContainer::from_glib_container_num(ptr as *const i8, num) |
2148 | } |
2149 | |
2150 | unsafe fn from_glib_full_num(ptr: *mut i8, num: usize) -> Self { |
2151 | FromGlibContainer::from_glib_full_num(ptr as *const i8, num) |
2152 | } |
2153 | } |
2154 | |
2155 | #[doc (hidden)] |
2156 | impl FromGlibContainer<*const c_char, *const u8> for GString { |
2157 | unsafe fn from_glib_none_num(ptr: *const u8, num: usize) -> Self { |
2158 | FromGlibContainer::from_glib_none_num(ptr as *const i8, num) |
2159 | } |
2160 | |
2161 | unsafe fn from_glib_container_num(ptr: *const u8, num: usize) -> Self { |
2162 | FromGlibContainer::from_glib_container_num(ptr as *const i8, num) |
2163 | } |
2164 | |
2165 | unsafe fn from_glib_full_num(ptr: *const u8, num: usize) -> Self { |
2166 | FromGlibContainer::from_glib_full_num(ptr as *const i8, num) |
2167 | } |
2168 | } |
2169 | |
2170 | #[doc (hidden)] |
2171 | impl FromGlibContainer<*const c_char, *mut u8> for GString { |
2172 | unsafe fn from_glib_none_num(ptr: *mut u8, num: usize) -> Self { |
2173 | FromGlibContainer::from_glib_none_num(ptr as *const i8, num) |
2174 | } |
2175 | |
2176 | unsafe fn from_glib_container_num(ptr: *mut u8, num: usize) -> Self { |
2177 | FromGlibContainer::from_glib_container_num(ptr as *const i8, num) |
2178 | } |
2179 | |
2180 | unsafe fn from_glib_full_num(ptr: *mut u8, num: usize) -> Self { |
2181 | FromGlibContainer::from_glib_full_num(ptr as *const i8, num) |
2182 | } |
2183 | } |
2184 | |
2185 | impl GlibPtrDefault for GString { |
2186 | type GlibType = *const c_char; |
2187 | } |
2188 | |
2189 | impl StaticType for GString { |
2190 | #[inline ] |
2191 | fn static_type() -> Type { |
2192 | String::static_type() |
2193 | } |
2194 | } |
2195 | |
2196 | impl crate::value::ValueType for GString { |
2197 | type Type = String; |
2198 | } |
2199 | |
2200 | impl crate::value::ValueTypeOptional for GString {} |
2201 | |
2202 | unsafe impl<'a> crate::value::FromValue<'a> for GString { |
2203 | type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>; |
2204 | |
2205 | #[inline ] |
2206 | unsafe fn from_value(value: &'a Value) -> Self { |
2207 | Self::from(<&str>::from_value(value)) |
2208 | } |
2209 | } |
2210 | |
2211 | impl crate::value::ToValue for GString { |
2212 | #[inline ] |
2213 | fn to_value(&self) -> Value { |
2214 | <&str>::to_value(&self.as_str()) |
2215 | } |
2216 | |
2217 | #[inline ] |
2218 | fn value_type(&self) -> Type { |
2219 | String::static_type() |
2220 | } |
2221 | } |
2222 | |
2223 | impl crate::value::ToValueOptional for GString { |
2224 | #[inline ] |
2225 | fn to_value_optional(s: Option<&Self>) -> Value { |
2226 | <str>::to_value_optional(s.as_ref().map(|s: &&GString| s.as_str())) |
2227 | } |
2228 | } |
2229 | |
2230 | impl From<GString> for Value { |
2231 | #[inline ] |
2232 | fn from(s: GString) -> Self { |
2233 | unsafe { |
2234 | let mut value: Value = Value::for_value_type::<GString>(); |
2235 | gobject_ffi::g_value_take_string(value:value.to_glib_none_mut().0, v_string:s.into_glib_ptr()); |
2236 | value |
2237 | } |
2238 | } |
2239 | } |
2240 | |
2241 | impl StaticType for Vec<GString> { |
2242 | #[inline ] |
2243 | fn static_type() -> Type { |
2244 | <Vec<String>>::static_type() |
2245 | } |
2246 | } |
2247 | |
2248 | impl crate::value::ValueType for Vec<GString> { |
2249 | type Type = Vec<GString>; |
2250 | } |
2251 | |
2252 | unsafe impl<'a> FromValue<'a> for Vec<GString> { |
2253 | type Checker = crate::value::GenericValueTypeChecker<Self>; |
2254 | |
2255 | #[inline ] |
2256 | unsafe fn from_value(value: &'a Value) -> Self { |
2257 | let ptr: *const *const i8 = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char; |
2258 | FromGlibPtrContainer::from_glib_none(ptr) |
2259 | } |
2260 | } |
2261 | |
2262 | impl ToValue for Vec<GString> { |
2263 | #[inline ] |
2264 | fn to_value(&self) -> Value { |
2265 | unsafe { |
2266 | let mut value: Value = Value::for_value_type::<Self>(); |
2267 | let ptr: *mut *mut c_char = self.to_glib_full(); |
2268 | gobject_ffi::g_value_take_boxed(value:value.to_glib_none_mut().0, v_boxed:ptr as *const c_void); |
2269 | value |
2270 | } |
2271 | } |
2272 | |
2273 | #[inline ] |
2274 | fn value_type(&self) -> Type { |
2275 | <Vec<GString>>::static_type() |
2276 | } |
2277 | } |
2278 | |
2279 | impl From<Vec<GString>> for Value { |
2280 | #[inline ] |
2281 | fn from(mut v: Vec<GString>) -> Self { |
2282 | unsafe { |
2283 | let mut value: Value = Value::for_value_type::<Vec<GString>>(); |
2284 | let container: (*mut *mut i8, (Vec>, …)) = |
2285 | ToGlibContainerFromSlice::<*mut *mut c_char>::to_glib_container_from_slice(&v); |
2286 | gobject_ffi::g_value_take_boxed( |
2287 | value:value.to_glib_none_mut().0, |
2288 | v_boxed:container.0 as *const c_void, |
2289 | ); |
2290 | v.set_len(new_len:0); |
2291 | value |
2292 | } |
2293 | } |
2294 | } |
2295 | |
2296 | impl_from_glib_container_as_vec_string!(GString, *const c_char); |
2297 | impl_from_glib_container_as_vec_string!(GString, *mut c_char); |
2298 | |
2299 | // rustdoc-stripper-ignore-next |
2300 | /// A trait to accept both <code>&[str]</code> or <code>&[GStr]</code> as an argument. |
2301 | pub trait IntoGStr { |
2302 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T; |
2303 | } |
2304 | |
2305 | impl IntoGStr for &GStr { |
2306 | #[inline ] |
2307 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
2308 | f(self) |
2309 | } |
2310 | } |
2311 | |
2312 | impl IntoGStr for GString { |
2313 | #[inline ] |
2314 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
2315 | f(self.as_gstr()) |
2316 | } |
2317 | } |
2318 | |
2319 | impl IntoGStr for &GString { |
2320 | #[inline ] |
2321 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
2322 | f(self.as_gstr()) |
2323 | } |
2324 | } |
2325 | |
2326 | // Limit borrowed from rust std CStr optimization: |
2327 | // https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/small_c_string.rs#L10 |
2328 | const MAX_STACK_ALLOCATION: usize = 384; |
2329 | |
2330 | impl IntoGStr for &str { |
2331 | #[inline ] |
2332 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
2333 | if self.len() < MAX_STACK_ALLOCATION { |
2334 | let mut s: MaybeUninit<[u8; 384]> = mem::MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); |
2335 | let ptr: *mut u8 = s.as_mut_ptr() as *mut u8; |
2336 | let gs: &GStr = unsafe { |
2337 | ptr::copy_nonoverlapping(self.as_ptr(), dst:ptr, self.len()); |
2338 | ptr.add(self.len()).write(val:0); |
2339 | GStr::from_utf8_with_nul_unchecked(bytes:slice::from_raw_parts(data:ptr, self.len() + 1)) |
2340 | }; |
2341 | f(gs) |
2342 | } else { |
2343 | f(GString::from(self).as_gstr()) |
2344 | } |
2345 | } |
2346 | } |
2347 | |
2348 | impl IntoGStr for String { |
2349 | #[inline ] |
2350 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(mut self, f: F) -> T { |
2351 | let len: usize = self.len(); |
2352 | if len < self.capacity() { |
2353 | self.reserve_exact(additional:1); |
2354 | self.push(ch:' \0' ); |
2355 | let gs: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(self.as_bytes()) }; |
2356 | f(gs) |
2357 | } else if len < MAX_STACK_ALLOCATION { |
2358 | self.as_str().run_with_gstr(f) |
2359 | } else { |
2360 | f(GString::from(self).as_gstr()) |
2361 | } |
2362 | } |
2363 | } |
2364 | |
2365 | impl IntoGStr for &String { |
2366 | #[inline ] |
2367 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
2368 | self.as_str().run_with_gstr(f) |
2369 | } |
2370 | } |
2371 | |
2372 | pub const NONE_STR: Option<&'static str> = None; |
2373 | |
2374 | // rustdoc-stripper-ignore-next |
2375 | /// A trait to accept both <code>[Option]<&[str]></code> or <code>[Option]<&[GStr]></code> as |
2376 | /// an argument. |
2377 | pub trait IntoOptionalGStr { |
2378 | fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T; |
2379 | } |
2380 | |
2381 | impl<S: IntoGStr> IntoOptionalGStr for Option<S> { |
2382 | #[inline ] |
2383 | fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T { |
2384 | match self { |
2385 | Some(t: S) => t.run_with_gstr(|s: &GStr| f(Some(s))), |
2386 | None => f(None), |
2387 | } |
2388 | } |
2389 | } |
2390 | |
2391 | #[cfg (test)] |
2392 | #[allow (clippy::disallowed_names)] |
2393 | mod tests { |
2394 | use std::ffi::CString; |
2395 | |
2396 | use super::*; |
2397 | |
2398 | #[test ] |
2399 | fn test_gstring() { |
2400 | let data = CString::new("foo" ).unwrap(); |
2401 | let ptr = data.as_ptr(); |
2402 | |
2403 | unsafe { |
2404 | let ptr_copy = ffi::g_strdup(ptr); |
2405 | let gstring = GString::from_glib_full(ptr_copy); |
2406 | assert_eq!(gstring.as_str(), "foo" ); |
2407 | let foo: Box<str> = gstring.into(); |
2408 | assert_eq!(foo.as_ref(), "foo" ); |
2409 | } |
2410 | } |
2411 | |
2412 | #[test ] |
2413 | fn test_owned_glib_string() { |
2414 | let data = CString::new("foo" ).unwrap(); |
2415 | let ptr = data.as_ptr(); |
2416 | unsafe { |
2417 | let ptr_copy = ffi::g_strdup(ptr); |
2418 | let gstr = GString::from_glib_full(ptr_copy); |
2419 | assert_eq!(gstr, "foo" ); |
2420 | } |
2421 | } |
2422 | |
2423 | #[test ] |
2424 | fn test_gstring_from_str() { |
2425 | let gstring: GString = "foo" .into(); |
2426 | assert_eq!(gstring.as_str(), "foo" ); |
2427 | let foo: Box<str> = gstring.into(); |
2428 | assert_eq!(foo.as_ref(), "foo" ); |
2429 | } |
2430 | |
2431 | #[test ] |
2432 | fn test_string_from_gstring() { |
2433 | let gstring = GString::from("foo" ); |
2434 | assert_eq!(gstring.as_str(), "foo" ); |
2435 | let s = String::from(gstring); |
2436 | assert_eq!(s, "foo" ); |
2437 | } |
2438 | |
2439 | #[test ] |
2440 | fn test_gstring_from_cstring() { |
2441 | let cstr = CString::new("foo" ).unwrap(); |
2442 | let gstring = GString::try_from(cstr).unwrap(); |
2443 | assert_eq!(gstring.as_str(), "foo" ); |
2444 | let foo: Box<str> = gstring.into(); |
2445 | assert_eq!(foo.as_ref(), "foo" ); |
2446 | } |
2447 | |
2448 | #[test ] |
2449 | fn test_string_from_gstring_from_cstring() { |
2450 | let cstr = CString::new("foo" ).unwrap(); |
2451 | let gstring = GString::try_from(cstr).unwrap(); |
2452 | assert_eq!(gstring.as_str(), "foo" ); |
2453 | let s = String::from(gstring); |
2454 | assert_eq!(s, "foo" ); |
2455 | } |
2456 | |
2457 | #[test ] |
2458 | fn test_vec_u8_to_gstring() { |
2459 | let v: &[u8] = b"foo" ; |
2460 | let s: GString = GString::from_utf8(Vec::from(v)).unwrap(); |
2461 | assert_eq!(s.as_str(), "foo" ); |
2462 | } |
2463 | |
2464 | #[test ] |
2465 | fn test_as_ref_path() { |
2466 | fn foo<P: AsRef<Path>>(_path: P) {} |
2467 | let gstring: GString = "/my/path/" .into(); |
2468 | let gstr: &GStr = gstring.as_gstr(); |
2469 | foo(gstr); |
2470 | foo(gstring); |
2471 | } |
2472 | |
2473 | #[test ] |
2474 | fn test_from_glib_container() { |
2475 | unsafe { |
2476 | let test_a: GString = FromGlibContainer::from_glib_container_num( |
2477 | ffi::g_strdup("hello_world \0" .as_ptr() as *const _), |
2478 | 5, |
2479 | ); |
2480 | assert_eq!("hello" , test_a.as_str()); |
2481 | |
2482 | let test_b: GString = FromGlibContainer::from_glib_none_num("hello_world" .as_ptr(), 5); |
2483 | assert_eq!("hello" , test_b.as_str()); |
2484 | |
2485 | let test_c: GString = |
2486 | FromGlibContainer::from_glib_none_num(std::ptr::null::<std::os::raw::c_char>(), 0); |
2487 | assert_eq!("" , test_c.as_str()); |
2488 | |
2489 | let test_d: GString = FromGlibContainer::from_glib_none_num("" .as_ptr(), 0); |
2490 | assert_eq!("" , test_d.as_str()); |
2491 | |
2492 | let test_e: GString = |
2493 | FromGlibContainer::from_glib_container_num(ffi::g_strdup(std::ptr::null()), 0); |
2494 | assert_eq!("" , test_e.as_str()); |
2495 | } |
2496 | } |
2497 | |
2498 | #[test ] |
2499 | fn test_hashmap() { |
2500 | use std::collections::HashMap; |
2501 | |
2502 | let gstring = GString::from("foo" ); |
2503 | assert_eq!(gstring.as_str(), "foo" ); |
2504 | let mut h: HashMap<GString, i32> = HashMap::new(); |
2505 | h.insert(gstring, 42); |
2506 | let gstring: GString = "foo" .into(); |
2507 | assert!(h.contains_key(&gstring)); |
2508 | } |
2509 | |
2510 | #[test ] |
2511 | fn test_gstring_from_ptr_lossy() { |
2512 | let data = CString::new("foo" ).unwrap(); |
2513 | let ptr = data.as_ptr(); |
2514 | |
2515 | unsafe { |
2516 | let gstring = GString::from_ptr_lossy(ptr); |
2517 | assert_eq!(gstring.as_str(), "foo" ); |
2518 | assert_eq!(ptr, gstring.as_ptr()); |
2519 | } |
2520 | |
2521 | let data = b"foo \xF0\x90\x80bar \0" ; |
2522 | let ptr = data.as_ptr(); |
2523 | |
2524 | unsafe { |
2525 | let gstring = GString::from_ptr_lossy(ptr as *const _); |
2526 | assert_eq!(gstring.as_str(), "foo���bar" ); |
2527 | assert_ne!(ptr, gstring.as_ptr() as *const _); |
2528 | } |
2529 | } |
2530 | |
2531 | #[test ] |
2532 | fn gformat() { |
2533 | let s = gformat!("bla bla {} bla" , 123); |
2534 | assert_eq!(s, "bla bla 123 bla" ); |
2535 | } |
2536 | |
2537 | #[test ] |
2538 | fn layout() { |
2539 | // ensure the inline variant is not wider than the other variants |
2540 | enum NoInline { |
2541 | _Native(Box<str>), |
2542 | _Foreign(ptr::NonNull<c_char>, usize), |
2543 | } |
2544 | assert_eq!(mem::size_of::<GString>(), mem::size_of::<NoInline>()); |
2545 | assert_eq!(mem::size_of::<GString>(), mem::size_of::<String>()); |
2546 | } |
2547 | } |
2548 | |