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 pub const fn as_ptr(&self) -> *const c_char {
778 self.0.as_ptr()
779 }
780
781 #[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
1055const INLINE_LEN: usize =
1057 mem::size_of::<Option<Box<str>>>() + mem::size_of::<usize>() - mem::size_of::<u8>() * 2;
1058
1059pub 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 #[inline]
1096 pub fn new() -> Self {
1097 Self(Inner::Inline {
1098 len: 0,
1099 data: Default::default(),
1100 })
1101 }
1102 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[macro_export]
1386macro_rules! gformat {
1387 ($($arg:tt)*) => { $crate::GString::format(std::format_args!($($arg)*)) };
1388}
1389
1390#[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 #[inline]
1409 pub fn into_inner(self) -> T {
1410 self.0
1411 }
1412}
1413
1414#[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 #[inline]
1433 pub fn into_inner(self) -> T {
1434 self.0
1435 }
1436 #[inline]
1439 pub fn nul_error(&self) -> GStrInteriorNulError {
1440 self.1
1441 }
1442}
1443
1444#[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 #[inline]
1463 pub fn into_inner(self) -> T {
1464 self.0
1465 }
1466 #[inline]
1470 pub fn utf8_error(&self) -> std::str::Utf8Error {
1471 self.1
1472 }
1473}
1474
1475#[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 #[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 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 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 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 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 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 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 unsafe {
1947 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 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 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 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 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 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 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 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
2436pub 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
2463const 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
2511pub 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 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}