1use 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#[repr(transparent)]
24pub struct GStr(str);
25
26impl GStr {
27 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
183 pub const fn as_bytes_with_nul(&self) -> &[u8] {
184 self.0.as_bytes()
185 }
186 #[inline]
192 pub const fn as_bytes(&self) -> &[u8] {
193 self.as_str().as_bytes()
194 }
195 #[inline]
207 pub const fn as_ptr(&self) -> *const c_char {
208 self.0.as_ptr() as *const _
209 }
210 #[inline]
213 pub const fn as_str(&self) -> &str {
214 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[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#[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 #[inline]
389 pub fn nul_position(&self) -> usize {
390 self.0
391 }
392}
393
394#[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#[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 #[inline]
763 pub fn to_gstr(&self) -> &GStr {
764 unsafe { GStr::from_ptr(self.0.as_ptr()) }
765 }
766
767 #[inline]
770 pub fn as_str(&self) -> &str {
771 self.to_gstr().as_str()
772 }
773
774 #[inline]
777 #[deprecated = "Use as_str instead"]
778 pub fn to_str(&self) -> &str {
779 self
780 }
781
782 #[inline]
785 pub const fn as_ptr(&self) -> *const c_char {
786 self.0.as_ptr()
787 }
788
789 #[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
1063const INLINE_LEN: usize =
1065 mem::size_of::<Option<Box<str>>>() + mem::size_of::<usize>() - mem::size_of::<u8>() * 2;
1066
1067pub 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 #[inline]
1104 pub fn new() -> Self {
1105 Self(Inner::Inline {
1106 len: 0,
1107 data: Default::default(),
1108 })
1109 }
1110 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[macro_export]
1394macro_rules! gformat {
1395 ($($arg:tt)*) => { $crate::GString::format(std::format_args!($($arg)*)) };
1396}
1397
1398#[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 #[inline]
1417 pub fn into_inner(self) -> T {
1418 self.0
1419 }
1420}
1421
1422#[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 #[inline]
1441 pub fn into_inner(self) -> T {
1442 self.0
1443 }
1444 #[inline]
1447 pub fn nul_error(&self) -> GStrInteriorNulError {
1448 self.1
1449 }
1450}
1451
1452#[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 #[inline]
1471 pub fn into_inner(self) -> T {
1472 self.0
1473 }
1474 #[inline]
1478 pub fn utf8_error(&self) -> std::str::Utf8Error {
1479 self.1
1480 }
1481}
1482
1483#[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 #[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 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 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 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 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 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 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 unsafe {
1955 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 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 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 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 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 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 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 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
2444pub 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
2471const 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
2519pub 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 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}