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