1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use 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
16use 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)]
24pub struct GStr(str);
25
26impl 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)]
297pub 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}")]
310pub struct GStrInteriorNulError(usize);
311
312impl 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]
342macro_rules! gstr {
343 ($s:literal) => {
344 unsafe { $crate::GStr::from_utf8_with_nul_unchecked($crate::cstr_bytes!($s)) }
345 };
346}
347
348impl 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
355impl PartialEq for GStr {
356 #[inline]
357 fn eq(&self, other: &Self) -> bool {
358 self.as_str().eq(other.as_str())
359 }
360}
361
362impl Eq for GStr {}
363
364impl PartialOrd for GStr {
365 #[inline]
366 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
367 Some(self.cmp(other))
368 }
369}
370
371impl Ord for GStr {
372 #[inline]
373 fn cmp(&self, other: &Self) -> Ordering {
374 self.as_str().cmp(other.as_str())
375 }
376}
377
378impl 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
385impl 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
393impl 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
400impl<'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
409impl PartialEq<GStr> for String {
410 #[inline]
411 fn eq(&self, other: &GStr) -> bool {
412 self.as_str() == other.as_str()
413 }
414}
415
416impl PartialEq<str> for GStr {
417 #[inline]
418 fn eq(&self, other: &str) -> bool {
419 self.as_str() == other
420 }
421}
422
423impl PartialEq<&str> for GStr {
424 #[inline]
425 fn eq(&self, other: &&str) -> bool {
426 self.as_str() == *other
427 }
428}
429
430impl PartialEq<GStr> for &str {
431 #[inline]
432 fn eq(&self, other: &GStr) -> bool {
433 *self == other.as_str()
434 }
435}
436
437impl PartialEq<String> for GStr {
438 #[inline]
439 fn eq(&self, other: &String) -> bool {
440 self.as_str() == other.as_str()
441 }
442}
443
444impl PartialEq<GStr> for str {
445 #[inline]
446 fn eq(&self, other: &GStr) -> bool {
447 self == other.as_str()
448 }
449}
450
451impl 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
458impl 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
465impl 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
472impl 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
479impl AsRef<GStr> for GStr {
480 #[inline]
481 fn as_ref(&self) -> &GStr {
482 self
483 }
484}
485
486impl AsRef<str> for GStr {
487 #[inline]
488 fn as_ref(&self) -> &str {
489 self.as_str()
490 }
491}
492
493impl AsRef<OsStr> for GStr {
494 #[inline]
495 fn as_ref(&self) -> &OsStr {
496 OsStr::new(self.as_str())
497 }
498}
499
500impl AsRef<Path> for GStr {
501 #[inline]
502 fn as_ref(&self) -> &Path {
503 Path::new(self.as_str())
504 }
505}
506
507impl AsRef<[u8]> for GStr {
508 #[inline]
509 fn as_ref(&self) -> &[u8] {
510 self.as_bytes()
511 }
512}
513
514impl Deref for GStr {
515 type Target = str;
516
517 #[inline]
518 fn deref(&self) -> &str {
519 self.as_str()
520 }
521}
522
523impl 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
549impl GlibPtrDefault for GStr {
550 type GlibType = *mut c_char;
551}
552
553impl StaticType for GStr {
554 #[inline]
555 fn static_type() -> Type {
556 str::static_type()
557 }
558}
559
560#[doc(hidden)]
561impl 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)]
572impl 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)]
580impl 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)]
588impl 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
595unsafe 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
610impl 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
622impl 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
634impl 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)]
642impl<'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)]
657impl<'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)]
676pub struct GStringPtr(ptr::NonNull<c_char>);
677
678#[doc(hidden)]
679unsafe impl TransparentPtrType for GStringPtr {}
680
681#[doc(hidden)]
682impl GlibPtrDefault for GStringPtr {
683 type GlibType = *mut c_char;
684}
685
686impl 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
728impl 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
735impl Deref for GStringPtr {
736 type Target = str;
737
738 #[inline]
739 fn deref(&self) -> &str {
740 self.as_str()
741 }
742}
743
744impl 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
751impl 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
760impl 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
766impl 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
773impl Eq for GStringPtr {}
774
775impl PartialEq for GStringPtr {
776 #[inline]
777 fn eq(&self, other: &GStringPtr) -> bool {
778 self.partial_cmp(other) == Some(Ordering::Equal)
779 }
780}
781
782impl PartialEq<GStringPtr> for String {
783 #[inline]
784 fn eq(&self, other: &GStringPtr) -> bool {
785 self.partial_cmp(other) == Some(Ordering::Equal)
786 }
787}
788
789impl PartialEq<GStringPtr> for GString {
790 #[inline]
791 fn eq(&self, other: &GStringPtr) -> bool {
792 self.partial_cmp(other) == Some(Ordering::Equal)
793 }
794}
795
796impl PartialEq<str> for GStringPtr {
797 #[inline]
798 fn eq(&self, other: &str) -> bool {
799 self.partial_cmp(other) == Some(Ordering::Equal)
800 }
801}
802
803impl PartialEq<&str> for GStringPtr {
804 #[inline]
805 fn eq(&self, other: &&str) -> bool {
806 self.partial_cmp(other) == Some(Ordering::Equal)
807 }
808}
809
810impl PartialEq<GStr> for GStringPtr {
811 #[inline]
812 fn eq(&self, other: &GStr) -> bool {
813 self.partial_cmp(other) == Some(Ordering::Equal)
814 }
815}
816
817impl PartialEq<&GStr> for GStringPtr {
818 #[inline]
819 fn eq(&self, other: &&GStr) -> bool {
820 self.partial_cmp(other) == Some(Ordering::Equal)
821 }
822}
823
824impl PartialEq<GStringPtr> for &str {
825 #[inline]
826 fn eq(&self, other: &GStringPtr) -> bool {
827 self.partial_cmp(other) == Some(Ordering::Equal)
828 }
829}
830
831impl PartialEq<GStringPtr> for &GStr {
832 #[inline]
833 fn eq(&self, other: &GStringPtr) -> bool {
834 self.partial_cmp(other) == Some(Ordering::Equal)
835 }
836}
837
838impl PartialEq<String> for GStringPtr {
839 #[inline]
840 fn eq(&self, other: &String) -> bool {
841 self.partial_cmp(other) == Some(Ordering::Equal)
842 }
843}
844
845impl PartialEq<GString> for GStringPtr {
846 #[inline]
847 fn eq(&self, other: &GString) -> bool {
848 self.partial_cmp(other) == Some(Ordering::Equal)
849 }
850}
851
852impl PartialEq<GStringPtr> for str {
853 #[inline]
854 fn eq(&self, other: &GStringPtr) -> bool {
855 self.partial_cmp(other) == Some(Ordering::Equal)
856 }
857}
858
859impl PartialEq<GStringPtr> for GStr {
860 #[inline]
861 fn eq(&self, other: &GStringPtr) -> bool {
862 self.partial_cmp(other) == Some(Ordering::Equal)
863 }
864}
865
866impl 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
873impl 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
880impl 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
887impl 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
894impl 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
901impl 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
908impl 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
915impl 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
922impl 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
929impl 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
936impl 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
943impl 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
950impl 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
957impl 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
964impl AsRef<GStringPtr> for GStringPtr {
965 #[inline]
966 fn as_ref(&self) -> &GStringPtr {
967 self
968 }
969}
970
971impl 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
979const 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]&lt;[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.
996pub struct GString(Inner);
997
998enum 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
1010unsafe impl Send for GString {}
1011unsafe impl Sync for GString {}
1012
1013impl 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]
1309macro_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")]
1319pub struct GStringNoTrailingNulError<T>(T);
1320
1321impl<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}")]
1336pub struct GStringInteriorNulError<T>(T, GStrInteriorNulError);
1337
1338impl<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}")]
1359pub struct GStringUtf8Error<T>(T, std::str::Utf8Error);
1360
1361impl<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)]
1380pub 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
1391impl<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
1417impl 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
1425impl 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
1443impl Default for GString {
1444 #[inline]
1445 fn default() -> Self {
1446 Self::new()
1447 }
1448}
1449
1450impl Clone for GString {
1451 #[inline]
1452 fn clone(&self) -> GString {
1453 self.as_str().into()
1454 }
1455}
1456
1457impl 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
1464impl 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
1475impl 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
1482impl 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
1489impl Borrow<GStr> for GString {
1490 #[inline]
1491 fn borrow(&self) -> &GStr {
1492 self.as_gstr()
1493 }
1494}
1495
1496impl Borrow<str> for GString {
1497 #[inline]
1498 fn borrow(&self) -> &str {
1499 self.as_str()
1500 }
1501}
1502
1503impl Ord for GString {
1504 #[inline]
1505 fn cmp(&self, other: &GString) -> Ordering {
1506 self.as_str().cmp(other.as_str())
1507 }
1508}
1509
1510impl PartialOrd for GString {
1511 #[inline]
1512 fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1513 Some(self.cmp(other))
1514 }
1515}
1516
1517impl PartialEq for GString {
1518 #[inline]
1519 fn eq(&self, other: &GString) -> bool {
1520 self.as_str() == other.as_str()
1521 }
1522}
1523
1524impl PartialEq<GString> for String {
1525 #[inline]
1526 fn eq(&self, other: &GString) -> bool {
1527 self.as_str() == other.as_str()
1528 }
1529}
1530
1531impl PartialEq<GStr> for GString {
1532 #[inline]
1533 fn eq(&self, other: &GStr) -> bool {
1534 self.as_str() == other.as_str()
1535 }
1536}
1537
1538impl PartialEq<&GStr> for GString {
1539 #[inline]
1540 fn eq(&self, other: &&GStr) -> bool {
1541 self.as_str() == other.as_str()
1542 }
1543}
1544
1545impl PartialEq<str> for GString {
1546 #[inline]
1547 fn eq(&self, other: &str) -> bool {
1548 self.as_str() == other
1549 }
1550}
1551
1552impl PartialEq<&str> for GString {
1553 #[inline]
1554 fn eq(&self, other: &&str) -> bool {
1555 self.as_str() == *other
1556 }
1557}
1558
1559impl PartialEq<GString> for &GStr {
1560 #[inline]
1561 fn eq(&self, other: &GString) -> bool {
1562 self.as_str() == other.as_str()
1563 }
1564}
1565
1566impl PartialEq<GString> for &str {
1567 #[inline]
1568 fn eq(&self, other: &GString) -> bool {
1569 *self == other.as_str()
1570 }
1571}
1572
1573impl PartialEq<String> for GString {
1574 #[inline]
1575 fn eq(&self, other: &String) -> bool {
1576 self.as_str() == other.as_str()
1577 }
1578}
1579
1580impl PartialEq<GString> for str {
1581 #[inline]
1582 fn eq(&self, other: &GString) -> bool {
1583 self == other.as_str()
1584 }
1585}
1586
1587impl PartialEq<GString> for GStr {
1588 #[inline]
1589 fn eq(&self, other: &GString) -> bool {
1590 self.as_str() == other.as_str()
1591 }
1592}
1593
1594impl 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
1601impl 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
1608impl 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
1615impl 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
1622impl PartialOrd<GString> for str {
1623 #[inline]
1624 fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1625 Some(self.cmp(other))
1626 }
1627}
1628
1629impl 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
1636impl Eq for GString {}
1637
1638impl AsRef<GStr> for GString {
1639 #[inline]
1640 fn as_ref(&self) -> &GStr {
1641 self.as_gstr()
1642 }
1643}
1644
1645impl AsRef<str> for GString {
1646 #[inline]
1647 fn as_ref(&self) -> &str {
1648 self.as_str()
1649 }
1650}
1651
1652impl AsRef<OsStr> for GString {
1653 #[inline]
1654 fn as_ref(&self) -> &OsStr {
1655 OsStr::new(self.as_str())
1656 }
1657}
1658
1659impl AsRef<Path> for GString {
1660 #[inline]
1661 fn as_ref(&self) -> &Path {
1662 Path::new(self.as_str())
1663 }
1664}
1665
1666impl AsRef<[u8]> for GString {
1667 #[inline]
1668 fn as_ref(&self) -> &[u8] {
1669 self.as_str().as_bytes()
1670 }
1671}
1672
1673impl Deref for GString {
1674 type Target = str;
1675
1676 #[inline]
1677 fn deref(&self) -> &str {
1678 self.as_str()
1679 }
1680}
1681
1682impl 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
1706impl 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
1714impl From<GString> for Vec<u8> {
1715 #[inline]
1716 fn from(value: GString) -> Vec<u8> {
1717 value.into_bytes_with_nul()
1718 }
1719}
1720
1721impl 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
1736impl From<GString> for OsString {
1737 #[inline]
1738 fn from(s: GString) -> Self {
1739 OsString::from(String::from(s))
1740 }
1741}
1742
1743impl From<GString> for PathBuf {
1744 #[inline]
1745 fn from(s: GString) -> Self {
1746 PathBuf::from(OsString::from(s))
1747 }
1748}
1749
1750impl 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
1768impl 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
1776impl<'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
1786impl From<&GStr> for GString {
1787 #[inline]
1788 fn from(s: &GStr) -> GString {
1789 s.to_owned()
1790 }
1791}
1792
1793impl 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
1820impl From<&String> for GString {
1821 #[inline]
1822 fn from(s: &String) -> Self {
1823 GString::from(s.as_str())
1824 }
1825}
1826
1827impl 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
1836impl 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
1857impl 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
1866impl 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
1874impl 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
1885impl<'a> From<Cow<'a, GStr>> for GString {
1886 #[inline]
1887 fn from(s: Cow<'a, GStr>) -> Self {
1888 s.into_owned()
1889 }
1890}
1891
1892impl<'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
1899impl<'a> From<GString> for Cow<'a, GStr> {
1900 #[inline]
1901 fn from(v: GString) -> Self {
1902 Cow::Owned(v)
1903 }
1904}
1905
1906impl<'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)]
1914impl 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)]
1930impl 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)]
1938impl 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)]
1946impl 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)]
1954impl 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)]
1963impl 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)]
1971impl 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)]
1979impl 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)]
1987impl 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)]
2003impl 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)]
2011impl 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)]
2019impl 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)]
2028impl<'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)]
2044impl<'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)]
2060impl<'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)]
2076impl<'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)]
2091impl 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)]
2141impl 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)]
2156impl 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)]
2171impl 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
2185impl GlibPtrDefault for GString {
2186 type GlibType = *const c_char;
2187}
2188
2189impl StaticType for GString {
2190 #[inline]
2191 fn static_type() -> Type {
2192 String::static_type()
2193 }
2194}
2195
2196impl crate::value::ValueType for GString {
2197 type Type = String;
2198}
2199
2200impl crate::value::ValueTypeOptional for GString {}
2201
2202unsafe 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
2211impl 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
2223impl 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
2230impl 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
2241impl StaticType for Vec<GString> {
2242 #[inline]
2243 fn static_type() -> Type {
2244 <Vec<String>>::static_type()
2245 }
2246}
2247
2248impl crate::value::ValueType for Vec<GString> {
2249 type Type = Vec<GString>;
2250}
2251
2252unsafe 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
2262impl 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
2279impl 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
2296impl_from_glib_container_as_vec_string!(GString, *const c_char);
2297impl_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.
2301pub trait IntoGStr {
2302 fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T;
2303}
2304
2305impl 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
2312impl 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
2319impl 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
2328const MAX_STACK_ALLOCATION: usize = 384;
2329
2330impl 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
2348impl 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
2365impl 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
2372pub const NONE_STR: Option<&'static str> = None;
2373
2374// rustdoc-stripper-ignore-next
2375/// A trait to accept both <code>[Option]&lt;&[str]></code> or <code>[Option]&lt;&[GStr]></code> as
2376/// an argument.
2377pub trait IntoOptionalGStr {
2378 fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T;
2379}
2380
2381impl<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)]
2393mod 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