glib/
gstring.rs

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