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