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::{Type, Value, ffi, gobject_ffi, prelude::*, translate::*};
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 unsafe {
62 debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0);
63 debug_assert!(std::str::from_utf8(bytes).is_ok());
64 mem::transmute::<&[u8], &GStr>(bytes)
65 }
66 }
67 #[inline]
73 pub fn from_utf8_until_nul(bytes: &[u8]) -> Result<&Self, GStrError> {
74 let nul_pos = memchr::memchr(0, bytes).ok_or(GStrError::NoTrailingNul)?;
75 let bytes = unsafe { bytes.get_unchecked(..nul_pos + 1) };
76 std::str::from_utf8(bytes)?;
77 Ok(unsafe { mem::transmute::<&[u8], &GStr>(bytes) })
78 }
79 #[inline]
87 pub fn from_str_with_nul(s: &str) -> Result<&Self, GStrError> {
88 Self::check_trailing_nul(s)?;
89 Ok(unsafe { mem::transmute::<&str, &GStr>(s) })
90 }
91 #[inline]
99 pub fn from_str_with_nul_checked(s: &str) -> Result<&Self, GStrError> {
100 Self::check_nuls(s)?;
101 Ok(unsafe { mem::transmute::<&str, &GStr>(s) })
102 }
103 #[inline]
115 pub const unsafe fn from_str_with_nul_unchecked(s: &str) -> &Self {
116 unsafe {
117 debug_assert!(!s.is_empty() && s.as_bytes()[s.len() - 1] == 0);
118 mem::transmute::<&str, &GStr>(s)
119 }
120 }
121 #[inline]
129 pub fn from_str_until_nul(s: &str) -> Result<&Self, GStrError> {
130 let b = s.as_bytes();
131 let nul_pos = memchr::memchr(0, b).ok_or(GStrError::NoTrailingNul)?;
132 let s = unsafe { std::str::from_utf8_unchecked(b.get_unchecked(..nul_pos + 1)) };
133 Ok(unsafe { mem::transmute::<&str, &GStr>(s) })
134 }
135 #[inline]
143 pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self {
144 unsafe {
145 let cstr = CStr::from_ptr(ptr);
146 Self::from_utf8_with_nul_unchecked(cstr.to_bytes_with_nul())
147 }
148 }
149 #[inline]
155 pub unsafe fn from_ptr_checked<'a>(ptr: *const c_char) -> Option<&'a Self> {
156 unsafe {
157 let mut end_ptr = ptr::null();
158 if ffi::g_utf8_validate(ptr as *const _, -1, &mut end_ptr) != ffi::GFALSE {
159 Some(Self::from_utf8_with_nul_unchecked(slice::from_raw_parts(
160 ptr as *const u8,
161 end_ptr.offset_from(ptr as *const u8) as usize + 1,
162 )))
163 } else {
164 None
165 }
166 }
167 }
168 #[inline]
175 pub unsafe fn from_ptr_lossy<'a>(ptr: *const c_char) -> Cow<'a, Self> {
176 unsafe {
177 if let Some(gs) = Self::from_ptr_checked(ptr) {
178 Cow::Borrowed(gs)
179 } else {
180 Cow::Owned(GString::from_glib_full(ffi::g_utf8_make_valid(
181 ptr as *const _,
182 -1,
183 )))
184 }
185 }
186 }
187 #[inline]
193 pub const fn as_bytes_with_nul(&self) -> &[u8] {
194 self.0.as_bytes()
195 }
196 #[inline]
202 pub const fn as_bytes(&self) -> &[u8] {
203 self.as_str().as_bytes()
204 }
205 #[inline]
217 pub const fn as_ptr(&self) -> *const c_char {
218 self.0.as_ptr() as *const _
219 }
220 #[inline]
223 pub const fn as_str(&self) -> &str {
224 unsafe {
226 std::str::from_utf8_unchecked(std::slice::from_raw_parts(
227 self.as_ptr() as *const _,
228 self.0.len() - 1,
229 ))
230 }
231 }
232 #[inline]
237 pub fn to_cstr(&self) -> Result<&CStr, GStrInteriorNulError> {
238 Self::check_interior_nuls(self.as_bytes())?;
239 Ok(unsafe { self.to_cstr_unchecked() })
240 }
241 #[inline]
244 pub fn to_cstr_until_nul(&self) -> &CStr {
245 let b = self.as_bytes_with_nul();
246 let nul_pos = memchr::memchr(0, b).unwrap();
247 unsafe { CStr::from_bytes_with_nul_unchecked(b.get_unchecked(..nul_pos + 1)) }
248 }
249 #[inline]
257 pub const unsafe fn to_cstr_unchecked(&self) -> &CStr {
258 unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) }
259 }
260
261 #[doc(alias = "g_utf8_collate")]
262 #[doc(alias = "utf8_collate")]
263 pub fn collate(&self, other: impl IntoGStr) -> Ordering {
264 other.run_with_gstr(|other| {
265 unsafe { ffi::g_utf8_collate(self.to_glib_none().0, other.to_glib_none().0) }.cmp(&0)
266 })
267 }
268
269 #[inline]
270 fn check_nuls(s: impl AsRef<[u8]>) -> Result<(), GStrError> {
271 let s = s.as_ref();
272 if let Some(nul_pos) = memchr::memchr(0, s) {
273 if s.len() == nul_pos + 1 {
274 Ok(())
275 } else {
276 Err(GStrInteriorNulError(nul_pos).into())
277 }
278 } else {
279 Err(GStrError::NoTrailingNul)
280 }
281 }
282 #[inline]
283 fn check_trailing_nul(s: impl AsRef<[u8]>) -> Result<(), GStrError> {
284 if let Some(c) = s.as_ref().last().copied()
285 && c == 0
286 {
287 return Ok(());
288 }
289 Err(GStrError::NoTrailingNul)
290 }
291 #[inline]
294 pub(crate) fn check_interior_nuls(s: impl AsRef<[u8]>) -> Result<(), GStrInteriorNulError> {
295 if let Some(nul_pos) = memchr::memchr(0, s.as_ref()) {
296 Err(GStrInteriorNulError(nul_pos))
297 } else {
298 Ok(())
299 }
300 }
301 pub const NONE: Option<&'static GStr> = None;
302
303 #[inline]
306 #[doc(alias = "g_intern_string")]
307 pub fn intern(&self) -> &'static GStr {
308 unsafe {
309 let s = ffi::g_intern_string(self.to_glib_none().0);
310 GStr::from_ptr(s)
311 }
312 }
313
314 #[inline]
317 #[doc(alias = "g_intern_static_string")]
318 pub fn intern_static(&'static self) -> &'static GStr {
319 unsafe {
320 let s = ffi::g_intern_static_string(self.to_glib_none().0);
321 GStr::from_ptr(s)
322 }
323 }
324
325 #[inline]
328 #[doc(alias = "g_intern_string")]
329 pub fn intern_from_str(s: impl AsRef<str>) -> &'static GStr {
330 unsafe {
331 let s = ffi::g_intern_string(s.as_ref().to_glib_none().0);
332 GStr::from_ptr(s)
333 }
334 }
335}
336
337#[derive(Debug)]
340pub enum GStrError {
341 InvalidUtf8(std::str::Utf8Error),
342 InteriorNul(GStrInteriorNulError),
343 NoTrailingNul,
344}
345
346impl std::error::Error for GStrError {
347 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
348 match self {
349 Self::InvalidUtf8(err) => std::error::Error::source(err),
350 Self::InteriorNul(err) => std::error::Error::source(err),
351 Self::NoTrailingNul => None,
352 }
353 }
354}
355
356impl fmt::Display for GStrError {
357 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
358 match self {
359 Self::InvalidUtf8(err) => fmt::Display::fmt(err, fmt),
360 Self::InteriorNul(err) => fmt::Display::fmt(err, fmt),
361 Self::NoTrailingNul => fmt.write_str("data provided is not nul terminated"),
362 }
363 }
364}
365
366impl std::convert::From<std::str::Utf8Error> for GStrError {
367 fn from(err: std::str::Utf8Error) -> Self {
368 Self::InvalidUtf8(err)
369 }
370}
371
372impl std::convert::From<GStrInteriorNulError> for GStrError {
373 fn from(err: GStrInteriorNulError) -> Self {
374 Self::InteriorNul(err)
375 }
376}
377
378#[derive(Copy, Clone, PartialEq, Eq, Debug)]
381pub struct GStrInteriorNulError(usize);
382
383impl std::error::Error for GStrInteriorNulError {}
384
385impl fmt::Display for GStrInteriorNulError {
386 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
387 write!(
388 fmt,
389 "data provided contains an interior nul-byte at byte pos {}",
390 self.0
391 )
392 }
393}
394
395impl GStrInteriorNulError {
396 #[inline]
399 pub fn nul_position(&self) -> usize {
400 self.0
401 }
402}
403
404#[macro_export]
425macro_rules! gstr {
426 ($s:literal) => {
427 unsafe { $crate::GStr::from_utf8_with_nul_unchecked($crate::cstr_bytes!($s)) }
428 };
429}
430
431impl fmt::Debug for GStr {
432 #[inline]
433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434 <&str as fmt::Debug>::fmt(&self.as_str(), f)
435 }
436}
437
438impl PartialEq for GStr {
439 #[inline]
440 fn eq(&self, other: &Self) -> bool {
441 self.as_str().eq(other.as_str())
442 }
443}
444
445impl Eq for GStr {}
446
447impl PartialOrd for GStr {
448 #[inline]
449 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
450 Some(self.cmp(other))
451 }
452}
453
454impl Ord for GStr {
455 #[inline]
456 fn cmp(&self, other: &Self) -> Ordering {
457 self.as_str().cmp(other.as_str())
458 }
459}
460
461impl hash::Hash for GStr {
462 #[inline]
463 fn hash<H: hash::Hasher>(&self, state: &mut H) {
464 self.as_str().hash(state)
465 }
466}
467
468impl Default for &GStr {
469 #[inline]
470 fn default() -> Self {
471 const SLICE: &[c_char] = &[0];
472 unsafe { GStr::from_ptr(SLICE.as_ptr()) }
473 }
474}
475
476impl fmt::Display for GStr {
477 #[inline]
478 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
479 f.write_str(self.as_str())
480 }
481}
482
483impl<'a> TryFrom<&'a CStr> for &'a GStr {
484 type Error = std::str::Utf8Error;
485 #[inline]
486 fn try_from(s: &'a CStr) -> Result<Self, Self::Error> {
487 s.to_str()?;
488 Ok(unsafe { GStr::from_utf8_with_nul_unchecked(s.to_bytes_with_nul()) })
489 }
490}
491
492impl PartialEq<GStr> for String {
493 #[inline]
494 fn eq(&self, other: &GStr) -> bool {
495 self.as_str() == other.as_str()
496 }
497}
498
499impl PartialEq<str> for GStr {
500 #[inline]
501 fn eq(&self, other: &str) -> bool {
502 self.as_str() == other
503 }
504}
505
506impl PartialEq<&str> for GStr {
507 #[inline]
508 fn eq(&self, other: &&str) -> bool {
509 self.as_str() == *other
510 }
511}
512
513impl PartialEq<GStr> for &str {
514 #[inline]
515 fn eq(&self, other: &GStr) -> bool {
516 *self == other.as_str()
517 }
518}
519
520impl PartialEq<String> for GStr {
521 #[inline]
522 fn eq(&self, other: &String) -> bool {
523 self.as_str() == other.as_str()
524 }
525}
526
527impl PartialEq<GStr> for str {
528 #[inline]
529 fn eq(&self, other: &GStr) -> bool {
530 self == other.as_str()
531 }
532}
533
534impl PartialOrd<GStr> for String {
535 #[inline]
536 fn partial_cmp(&self, other: &GStr) -> Option<Ordering> {
537 Some(self.cmp(&String::from(other.as_str())))
538 }
539}
540
541impl PartialOrd<String> for GStr {
542 #[inline]
543 fn partial_cmp(&self, other: &String) -> Option<Ordering> {
544 Some(self.as_str().cmp(other.as_str()))
545 }
546}
547
548impl PartialOrd<GStr> for str {
549 #[inline]
550 fn partial_cmp(&self, other: &GStr) -> Option<Ordering> {
551 Some(self.cmp(other.as_str()))
552 }
553}
554
555impl PartialOrd<str> for GStr {
556 #[inline]
557 fn partial_cmp(&self, other: &str) -> Option<Ordering> {
558 Some(self.as_str().cmp(other))
559 }
560}
561
562impl AsRef<GStr> for GStr {
563 #[inline]
564 fn as_ref(&self) -> &GStr {
565 self
566 }
567}
568
569impl AsRef<str> for GStr {
570 #[inline]
571 fn as_ref(&self) -> &str {
572 self.as_str()
573 }
574}
575
576impl AsRef<OsStr> for GStr {
577 #[inline]
578 fn as_ref(&self) -> &OsStr {
579 OsStr::new(self.as_str())
580 }
581}
582
583impl AsRef<Path> for GStr {
584 #[inline]
585 fn as_ref(&self) -> &Path {
586 Path::new(self.as_str())
587 }
588}
589
590impl AsRef<[u8]> for GStr {
591 #[inline]
592 fn as_ref(&self) -> &[u8] {
593 self.as_bytes()
594 }
595}
596
597impl Deref for GStr {
598 type Target = str;
599
600 #[inline]
601 fn deref(&self) -> &str {
602 self.as_str()
603 }
604}
605
606impl ToOwned for GStr {
607 type Owned = GString;
608
609 #[inline]
610 fn to_owned(&self) -> Self::Owned {
611 let b = self.as_bytes_with_nul();
612 if self.len() < INLINE_LEN {
613 let mut data = <[u8; INLINE_LEN]>::default();
614 let b = self.as_bytes();
615 unsafe { data.get_unchecked_mut(..b.len()) }.copy_from_slice(b);
616 return GString(Inner::Inline {
617 len: self.len() as u8,
618 data,
619 });
620 }
621 let inner = unsafe {
622 let copy = ffi::g_strndup(b.as_ptr() as *const c_char, b.len());
623 Inner::Foreign {
624 ptr: ptr::NonNull::new_unchecked(copy),
625 len: b.len() - 1,
626 }
627 };
628 GString(inner)
629 }
630}
631
632impl GlibPtrDefault for GStr {
633 type GlibType = *mut c_char;
634}
635
636impl StaticType for GStr {
637 #[inline]
638 fn static_type() -> Type {
639 str::static_type()
640 }
641}
642
643#[doc(hidden)]
644impl FromGlibPtrNone<*const u8> for &GStr {
645 #[inline]
646 unsafe fn from_glib_none(ptr: *const u8) -> Self {
647 unsafe {
648 debug_assert!(!ptr.is_null());
649 let cstr = CStr::from_ptr(ptr as *const _);
650 debug_assert!(cstr.to_str().is_ok(), "C string is not valid utf-8");
651 GStr::from_utf8_with_nul_unchecked(cstr.to_bytes_with_nul())
652 }
653 }
654}
655
656#[doc(hidden)]
657impl FromGlibPtrNone<*const i8> for &GStr {
658 #[inline]
659 unsafe fn from_glib_none(ptr: *const i8) -> Self {
660 unsafe { from_glib_none(ptr as *const u8) }
661 }
662}
663
664#[doc(hidden)]
665impl FromGlibPtrNone<*mut u8> for &GStr {
666 #[inline]
667 unsafe fn from_glib_none(ptr: *mut u8) -> Self {
668 unsafe { from_glib_none(ptr as *const u8) }
669 }
670}
671
672#[doc(hidden)]
673impl FromGlibPtrNone<*mut i8> for &GStr {
674 #[inline]
675 unsafe fn from_glib_none(ptr: *mut i8) -> Self {
676 unsafe { from_glib_none(ptr as *const u8) }
677 }
678}
679
680unsafe impl<'a> crate::value::FromValue<'a> for &'a GStr {
681 type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>;
682
683 #[inline]
684 unsafe fn from_value(value: &'a Value) -> Self {
685 unsafe {
686 let ptr = gobject_ffi::g_value_get_string(value.to_glib_none().0);
687 let cstr = CStr::from_ptr(ptr);
688 debug_assert!(
689 cstr.to_str().is_ok(),
690 "C string in glib::Value is not valid utf-8"
691 );
692 GStr::from_utf8_with_nul_unchecked(cstr.to_bytes_with_nul())
693 }
694 }
695}
696
697impl ToValue for GStr {
698 #[inline]
699 fn to_value(&self) -> Value {
700 self.as_str().to_value()
701 }
702
703 #[inline]
704 fn value_type(&self) -> Type {
705 str::static_type()
706 }
707}
708
709impl ToValue for &GStr {
710 #[inline]
711 fn to_value(&self) -> Value {
712 (*self).to_value()
713 }
714
715 #[inline]
716 fn value_type(&self) -> Type {
717 str::static_type()
718 }
719}
720
721impl crate::value::ToValueOptional for GStr {
722 #[inline]
723 fn to_value_optional(s: Option<&Self>) -> Value {
724 crate::value::ToValueOptional::to_value_optional(s.map(|s| s.as_str()))
725 }
726}
727
728#[doc(hidden)]
729impl<'a> ToGlibPtr<'a, *const c_char> for GStr {
730 type Storage = PhantomData<&'a Self>;
731
732 #[inline]
733 fn to_glib_none(&'a self) -> Stash<'a, *const c_char, Self> {
734 Stash(self.as_ptr(), PhantomData)
735 }
736
737 #[inline]
738 fn to_glib_full(&self) -> *const c_char {
739 self.as_str().to_glib_full()
740 }
741}
742
743#[doc(hidden)]
744impl<'a> ToGlibPtr<'a, *mut c_char> for GStr {
745 type Storage = PhantomData<&'a Self>;
746
747 #[inline]
748 fn to_glib_none(&'a self) -> Stash<'a, *mut c_char, Self> {
749 Stash(self.as_ptr() as *mut c_char, PhantomData)
750 }
751
752 #[inline]
753 fn to_glib_full(&self) -> *mut c_char {
754 self.as_str().to_glib_full()
755 }
756}
757
758#[repr(transparent)]
763pub struct GStringPtr(ptr::NonNull<c_char>);
764
765#[doc(hidden)]
766unsafe impl TransparentPtrType for GStringPtr {}
767
768#[doc(hidden)]
769impl GlibPtrDefault for GStringPtr {
770 type GlibType = *mut c_char;
771}
772
773impl GStringPtr {
774 #[inline]
777 pub fn to_gstr(&self) -> &GStr {
778 unsafe { GStr::from_ptr(self.0.as_ptr()) }
779 }
780
781 #[inline]
784 pub fn as_str(&self) -> &str {
785 self.to_gstr().as_str()
786 }
787
788 #[inline]
791 pub const fn as_ptr(&self) -> *const c_char {
792 self.0.as_ptr()
793 }
794
795 #[inline]
802 unsafe fn strcmp(a: *const c_char, b: *const c_char) -> Ordering {
803 unsafe { from_glib(libc::strcmp(a, b)) }
804 }
805}
806
807impl Clone for GStringPtr {
808 #[inline]
809 fn clone(&self) -> GStringPtr {
810 unsafe { GStringPtr(ptr::NonNull::new_unchecked(ffi::g_strdup(self.0.as_ptr()))) }
811 }
812}
813
814impl Deref for GStringPtr {
815 type Target = str;
816
817 #[inline]
818 fn deref(&self) -> &str {
819 self.as_str()
820 }
821}
822
823impl IntoGlibPtr<*mut c_char> for GStringPtr {
824 #[inline]
825 fn into_glib_ptr(self) -> *mut c_char {
826 self.0.as_ptr()
827 }
828}
829
830impl Drop for GStringPtr {
831 #[inline]
832 fn drop(&mut self) {
833 unsafe {
834 ffi::g_free(self.0.as_ptr() as *mut _);
835 }
836 }
837}
838
839impl fmt::Debug for GStringPtr {
840 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
841 <&GStr as fmt::Debug>::fmt(&self.to_gstr(), f)
842 }
843}
844
845impl fmt::Display for GStringPtr {
846 #[inline]
847 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
848 f.write_str(self.as_str())
849 }
850}
851
852impl Eq for GStringPtr {}
853
854impl PartialEq for GStringPtr {
855 #[inline]
856 fn eq(&self, other: &GStringPtr) -> bool {
857 self.partial_cmp(other) == Some(Ordering::Equal)
858 }
859}
860
861impl PartialEq<GStringPtr> for String {
862 #[inline]
863 fn eq(&self, other: &GStringPtr) -> bool {
864 self.partial_cmp(other) == Some(Ordering::Equal)
865 }
866}
867
868impl PartialEq<GStringPtr> for GString {
869 #[inline]
870 fn eq(&self, other: &GStringPtr) -> bool {
871 self.partial_cmp(other) == Some(Ordering::Equal)
872 }
873}
874
875impl PartialEq<str> for GStringPtr {
876 #[inline]
877 fn eq(&self, other: &str) -> bool {
878 self.partial_cmp(other) == Some(Ordering::Equal)
879 }
880}
881
882impl PartialEq<&str> for GStringPtr {
883 #[inline]
884 fn eq(&self, other: &&str) -> bool {
885 self.partial_cmp(other) == Some(Ordering::Equal)
886 }
887}
888
889impl PartialEq<GStr> for GStringPtr {
890 #[inline]
891 fn eq(&self, other: &GStr) -> bool {
892 self.partial_cmp(other) == Some(Ordering::Equal)
893 }
894}
895
896impl PartialEq<&GStr> for GStringPtr {
897 #[inline]
898 fn eq(&self, other: &&GStr) -> bool {
899 self.partial_cmp(other) == Some(Ordering::Equal)
900 }
901}
902
903impl PartialEq<GStringPtr> for &str {
904 #[inline]
905 fn eq(&self, other: &GStringPtr) -> bool {
906 self.partial_cmp(other) == Some(Ordering::Equal)
907 }
908}
909
910impl PartialEq<GStringPtr> for &GStr {
911 #[inline]
912 fn eq(&self, other: &GStringPtr) -> bool {
913 self.partial_cmp(other) == Some(Ordering::Equal)
914 }
915}
916
917impl PartialEq<String> for GStringPtr {
918 #[inline]
919 fn eq(&self, other: &String) -> bool {
920 self.partial_cmp(other) == Some(Ordering::Equal)
921 }
922}
923
924impl PartialEq<GString> for GStringPtr {
925 #[inline]
926 fn eq(&self, other: &GString) -> bool {
927 self.partial_cmp(other) == Some(Ordering::Equal)
928 }
929}
930
931impl PartialEq<GStringPtr> for str {
932 #[inline]
933 fn eq(&self, other: &GStringPtr) -> bool {
934 self.partial_cmp(other) == Some(Ordering::Equal)
935 }
936}
937
938impl PartialEq<GStringPtr> for GStr {
939 #[inline]
940 fn eq(&self, other: &GStringPtr) -> bool {
941 self.partial_cmp(other) == Some(Ordering::Equal)
942 }
943}
944
945impl PartialOrd<GStringPtr> for GStringPtr {
946 #[inline]
947 fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
948 Some(self.cmp(other))
949 }
950}
951
952impl Ord for GStringPtr {
953 #[inline]
954 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
955 unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) }
956 }
957}
958
959impl PartialOrd<GStringPtr> for String {
960 #[inline]
961 fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
962 Some(self.as_str().cmp(other))
963 }
964}
965
966impl PartialOrd<GStringPtr> for GString {
967 #[inline]
968 fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
969 Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
970 }
971}
972
973impl PartialOrd<String> for GStringPtr {
974 #[inline]
975 fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> {
976 Some(self.as_str().cmp(other))
977 }
978}
979
980impl PartialOrd<GString> for GStringPtr {
981 #[inline]
982 fn partial_cmp(&self, other: &GString) -> Option<std::cmp::Ordering> {
983 Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
984 }
985}
986
987impl PartialOrd<GStringPtr> for str {
988 #[inline]
989 fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
990 Some(self.cmp(other.as_str()))
991 }
992}
993
994impl PartialOrd<GStringPtr> for GStr {
995 #[inline]
996 fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
997 Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
998 }
999}
1000
1001impl PartialOrd<str> for GStringPtr {
1002 #[inline]
1003 fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> {
1004 Some(self.as_str().cmp(other))
1005 }
1006}
1007
1008impl PartialOrd<&str> for GStringPtr {
1009 #[inline]
1010 fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
1011 Some(self.as_str().cmp(other))
1012 }
1013}
1014
1015impl PartialOrd<GStr> for GStringPtr {
1016 #[inline]
1017 fn partial_cmp(&self, other: &GStr) -> Option<std::cmp::Ordering> {
1018 Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
1019 }
1020}
1021
1022impl PartialOrd<&GStr> for GStringPtr {
1023 #[inline]
1024 fn partial_cmp(&self, other: &&GStr) -> Option<std::cmp::Ordering> {
1025 Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
1026 }
1027}
1028
1029impl PartialOrd<GStringPtr> for &str {
1030 #[inline]
1031 fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
1032 Some(self.cmp(&other.as_str()))
1033 }
1034}
1035
1036impl PartialOrd<GStringPtr> for &GStr {
1037 #[inline]
1038 fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
1039 Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
1040 }
1041}
1042
1043impl AsRef<GStringPtr> for GStringPtr {
1044 #[inline]
1045 fn as_ref(&self) -> &GStringPtr {
1046 self
1047 }
1048}
1049
1050impl std::hash::Hash for GStringPtr {
1051 #[inline]
1052 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1053 self.as_str().hash(state);
1054 }
1055}
1056
1057impl<T: AsRef<str>> From<T> for GStringPtr {
1058 fn from(value: T) -> Self {
1059 unsafe {
1060 let value = value.as_ref();
1061 GStringPtr(ptr::NonNull::new_unchecked(ffi::g_strndup(
1062 value.as_ptr() as *const _,
1063 value.len(),
1064 )))
1065 }
1066 }
1067}
1068
1069const INLINE_LEN: usize =
1071 mem::size_of::<Option<Box<str>>>() + mem::size_of::<usize>() - mem::size_of::<u8>() * 2;
1072
1073pub struct GString(Inner);
1088
1089enum Inner {
1090 Native(Box<str>),
1091 Foreign {
1092 ptr: ptr::NonNull<c_char>,
1093 len: usize,
1094 },
1095 Inline {
1096 len: u8,
1097 data: [u8; INLINE_LEN],
1098 },
1099}
1100
1101unsafe impl Send for GString {}
1102unsafe impl Sync for GString {}
1103
1104impl GString {
1105 #[inline]
1110 pub fn new() -> Self {
1111 Self(Inner::Inline {
1112 len: 0,
1113 data: Default::default(),
1114 })
1115 }
1116 pub fn format(args: fmt::Arguments) -> Self {
1125 if let Some(s) = args.as_str() {
1126 return Self::from(s);
1127 }
1128
1129 let mut s = crate::GStringBuilder::default();
1130 fmt::Write::write_fmt(&mut s, args).unwrap();
1131 s.into_string()
1132 }
1133 #[inline]
1140 pub fn from_utf8(bytes: Vec<u8>) -> Result<Self, std::string::FromUtf8Error> {
1141 Ok(Self::from_string_unchecked(String::from_utf8(bytes)?))
1142 }
1143 #[inline]
1151 pub fn from_utf8_checked(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> {
1152 Ok(Self::from_string_checked(String::from_utf8(bytes)?)
1153 .map_err(|e| GStringInteriorNulError(e.0.into_bytes(), e.1))?)
1154 }
1155 #[inline]
1166 pub unsafe fn from_utf8_unchecked(mut v: Vec<u8>) -> Self {
1167 unsafe {
1168 if v.is_empty() {
1169 Self::new()
1170 } else {
1171 v.reserve_exact(1);
1172 v.push(0);
1173 Self(Inner::Native(String::from_utf8_unchecked(v).into()))
1174 }
1175 }
1176 }
1177 #[inline]
1184 pub fn from_utf8_with_nul(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> {
1185 let s = String::from_utf8(bytes)?;
1186 if s.as_bytes().last().copied() != Some(0u8) {
1187 return Err(GStringNoTrailingNulError(s.into_bytes()).into());
1188 }
1189 if s.len() == 1 {
1190 Ok(Self::new())
1191 } else {
1192 Ok(Self(Inner::Native(s.into())))
1193 }
1194 }
1195 #[inline]
1201 pub fn from_utf8_with_nul_checked(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> {
1202 let s = Self::from_utf8_with_nul(bytes)?;
1203 if let Err(e) = GStr::check_interior_nuls(&s) {
1204 return Err(GStringInteriorNulError(s.into_bytes(), e).into());
1205 }
1206 Ok(s)
1207 }
1208 #[inline]
1217 pub unsafe fn from_utf8_with_nul_unchecked(v: Vec<u8>) -> Self {
1218 unsafe {
1219 debug_assert!(!v.is_empty() && v[v.len() - 1] == 0);
1220 let s = if cfg!(debug_assertions) {
1221 let s = String::from_utf8(v).unwrap();
1222 GStr::check_interior_nuls(&s[..s.len() - 1]).unwrap();
1223 s
1224 } else {
1225 String::from_utf8_unchecked(v)
1226 };
1227 if s.len() == 1 {
1228 Self::new()
1229 } else {
1230 Self(Inner::Native(s.into()))
1231 }
1232 }
1233 }
1234 #[inline]
1241 pub fn from_utf8_until_nul(mut bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> {
1242 let nul_pos = if let Some(nul_pos) = memchr::memchr(0, &bytes) {
1243 nul_pos
1244 } else {
1245 return Err(GStringNoTrailingNulError(bytes).into());
1246 };
1247 if nul_pos == 0 {
1248 Ok(Self::new())
1249 } else {
1250 if let Err(e) = std::str::from_utf8(unsafe { bytes.get_unchecked(..nul_pos) }) {
1251 return Err(GStringUtf8Error(bytes, e).into());
1252 }
1253 bytes.truncate(nul_pos + 1);
1254 let s = unsafe { String::from_utf8_unchecked(bytes) };
1255 Ok(Self(Inner::Native(s.into())))
1256 }
1257 }
1258 #[inline]
1266 pub fn from_string_checked(s: String) -> Result<Self, GStringInteriorNulError<String>> {
1267 if let Err(e) = GStr::check_interior_nuls(&s) {
1268 return Err(GStringInteriorNulError(s, e));
1269 }
1270 Ok(Self::from_string_unchecked(s))
1271 }
1272 #[inline]
1277 pub fn from_string_unchecked(mut s: String) -> Self {
1278 if s.is_empty() {
1279 Self::new()
1280 } else {
1281 s.reserve_exact(1);
1282 s.push('\0');
1283 Self(Inner::Native(s.into()))
1284 }
1285 }
1286 #[inline]
1293 pub unsafe fn from_ptr_lossy<'a>(ptr: *const c_char) -> Cow<'a, GStr> {
1294 unsafe { GStr::from_ptr_lossy(ptr) }
1295 }
1296
1297 #[inline]
1304 pub unsafe fn from_ptr_and_len_unchecked(ptr: *const c_char, len: usize) -> Self {
1305 unsafe {
1306 debug_assert!(!ptr.is_null());
1307
1308 GString(Inner::Foreign {
1309 ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
1310 len,
1311 })
1312 }
1313 }
1314
1315 #[inline]
1318 pub fn as_str(&self) -> &str {
1319 unsafe {
1320 let (ptr, len) = match self.0 {
1321 Inner::Native(ref s) => (s.as_ptr(), s.len() - 1),
1322 Inner::Foreign { ptr, len } => (ptr.as_ptr() as *const u8, len),
1323 Inner::Inline { len, ref data } => (data.as_ptr(), len as usize),
1324 };
1325 if len == 0 {
1326 ""
1327 } else {
1328 let slice = slice::from_raw_parts(ptr, len);
1329 std::str::from_utf8_unchecked(slice)
1330 }
1331 }
1332 }
1333
1334 #[inline]
1337 pub fn as_gstr(&self) -> &GStr {
1338 let bytes = match self.0 {
1339 Inner::Native(ref s) => s.as_bytes(),
1340 Inner::Foreign { len: 0, .. } => &[0],
1341 Inner::Foreign { ptr, len } => unsafe {
1342 slice::from_raw_parts(ptr.as_ptr() as *const _, len + 1)
1343 },
1344 Inner::Inline { len, ref data } => unsafe { data.get_unchecked(..len as usize + 1) },
1345 };
1346 unsafe { GStr::from_utf8_with_nul_unchecked(bytes) }
1347 }
1348
1349 #[inline]
1352 pub fn as_ptr(&self) -> *const c_char {
1353 match self.0 {
1354 Inner::Native(ref s) => s.as_ptr() as *const _,
1355 Inner::Foreign { ptr, .. } => ptr.as_ptr(),
1356 Inner::Inline { ref data, .. } => data.as_ptr() as *const _,
1357 }
1358 }
1359
1360 #[inline]
1365 pub fn into_bytes(mut self) -> Vec<u8> {
1366 match &mut self.0 {
1367 Inner::Native(s) => {
1368 let mut s = String::from(mem::replace(s, "".into()));
1369 let _nul = s.pop();
1370 debug_assert_eq!(_nul, Some('\0'));
1371 s.into_bytes()
1372 }
1373 Inner::Foreign { ptr, len } => {
1374 let bytes = unsafe { slice::from_raw_parts(ptr.as_ptr() as *const u8, *len - 1) };
1375 bytes.to_owned()
1376 }
1377 Inner::Inline { len, data } => {
1378 unsafe { data.get_unchecked(..*len as usize) }.to_owned()
1379 }
1380 }
1381 }
1382
1383 #[inline]
1386 pub fn into_bytes_with_nul(mut self) -> Vec<u8> {
1387 match &mut self.0 {
1388 Inner::Native(s) => str::into_boxed_bytes(mem::replace(s, "".into())).into(),
1389 Inner::Foreign { ptr, len } => {
1390 let bytes = unsafe { slice::from_raw_parts(ptr.as_ptr() as *const u8, *len) };
1391 bytes.to_owned()
1392 }
1393 Inner::Inline { len, data } => {
1394 unsafe { data.get_unchecked(..*len as usize + 1) }.to_owned()
1395 }
1396 }
1397 }
1398}
1399
1400#[macro_export]
1406macro_rules! gformat {
1407 ($($arg:tt)*) => { $crate::GString::format(std::format_args!($($arg)*)) };
1408}
1409
1410#[derive(Clone, PartialEq, Eq, Debug)]
1415pub struct GStringNoTrailingNulError<T>(T);
1416
1417impl<T> std::error::Error for GStringNoTrailingNulError<T> where T: fmt::Debug {}
1418
1419impl<T> fmt::Display for GStringNoTrailingNulError<T> {
1420 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1421 fmt.write_str("data provided is not nul terminated")
1422 }
1423}
1424
1425impl<T> GStringNoTrailingNulError<T> {
1426 #[inline]
1429 pub fn into_inner(self) -> T {
1430 self.0
1431 }
1432}
1433
1434#[derive(Clone, PartialEq, Eq, Debug)]
1439pub struct GStringInteriorNulError<T>(T, GStrInteriorNulError);
1440
1441impl<T> std::error::Error for GStringInteriorNulError<T> where T: fmt::Debug {}
1442
1443impl<T> fmt::Display for GStringInteriorNulError<T> {
1444 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1445 fmt::Display::fmt(&self.1, fmt)
1446 }
1447}
1448
1449impl<T> GStringInteriorNulError<T> {
1450 #[inline]
1453 pub fn into_inner(self) -> T {
1454 self.0
1455 }
1456 #[inline]
1459 pub fn nul_error(&self) -> GStrInteriorNulError {
1460 self.1
1461 }
1462}
1463
1464#[derive(Clone, PartialEq, Eq, Debug)]
1469pub struct GStringUtf8Error<T>(T, std::str::Utf8Error);
1470
1471impl<T> std::error::Error for GStringUtf8Error<T> where T: fmt::Debug {}
1472
1473impl<T> fmt::Display for GStringUtf8Error<T> {
1474 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1475 fmt::Display::fmt(&self.1, fmt)
1476 }
1477}
1478
1479impl<T> GStringUtf8Error<T> {
1480 #[inline]
1483 pub fn into_inner(self) -> T {
1484 self.0
1485 }
1486 #[inline]
1490 pub fn utf8_error(&self) -> std::str::Utf8Error {
1491 self.1
1492 }
1493}
1494
1495#[derive(Debug)]
1498pub enum GStringFromError<T> {
1499 NoTrailingNul(GStringNoTrailingNulError<T>),
1500 InteriorNul(GStringInteriorNulError<T>),
1501 InvalidUtf8(GStringUtf8Error<T>),
1502 Unspecified(T),
1503}
1504
1505impl<T> std::error::Error for GStringFromError<T>
1506where
1507 T: fmt::Debug,
1508{
1509 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1510 match self {
1511 Self::NoTrailingNul(err) => std::error::Error::source(err),
1512 Self::InteriorNul(err) => std::error::Error::source(err),
1513 Self::InvalidUtf8(err) => std::error::Error::source(err),
1514 Self::Unspecified { .. } => None,
1515 }
1516 }
1517}
1518
1519impl<T> fmt::Display for GStringFromError<T> {
1520 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1521 match self {
1522 Self::NoTrailingNul(err) => fmt::Display::fmt(err, fmt),
1523 Self::InteriorNul(err) => fmt::Display::fmt(err, fmt),
1524 Self::InvalidUtf8(err) => fmt::Display::fmt(err, fmt),
1525 Self::Unspecified(_) => fmt.write_str("unable to convert"),
1526 }
1527 }
1528}
1529
1530impl<T> std::convert::From<GStringNoTrailingNulError<T>> for GStringFromError<T> {
1531 fn from(err: GStringNoTrailingNulError<T>) -> Self {
1532 GStringFromError::NoTrailingNul(err)
1533 }
1534}
1535
1536impl<T> std::convert::From<GStringInteriorNulError<T>> for GStringFromError<T> {
1537 fn from(err: GStringInteriorNulError<T>) -> Self {
1538 GStringFromError::InteriorNul(err)
1539 }
1540}
1541
1542impl<T> std::convert::From<GStringUtf8Error<T>> for GStringFromError<T> {
1543 fn from(err: GStringUtf8Error<T>) -> Self {
1544 GStringFromError::InvalidUtf8(err)
1545 }
1546}
1547
1548impl<T> GStringFromError<T> {
1549 pub fn into_inner(self) -> T {
1550 match self {
1551 Self::NoTrailingNul(GStringNoTrailingNulError(t)) => t,
1552 Self::InteriorNul(GStringInteriorNulError(t, _)) => t,
1553 Self::InvalidUtf8(GStringUtf8Error(t, _)) => t,
1554 Self::Unspecified(t) => t,
1555 }
1556 }
1557 #[inline]
1558 fn convert<R>(self, func: impl FnOnce(T) -> R) -> GStringFromError<R> {
1559 match self {
1560 Self::NoTrailingNul(GStringNoTrailingNulError(t)) => {
1561 GStringFromError::NoTrailingNul(GStringNoTrailingNulError(func(t)))
1562 }
1563 Self::InteriorNul(GStringInteriorNulError(t, e)) => {
1564 GStringFromError::InteriorNul(GStringInteriorNulError(func(t), e))
1565 }
1566 Self::InvalidUtf8(GStringUtf8Error(t, e)) => {
1567 GStringFromError::InvalidUtf8(GStringUtf8Error(func(t), e))
1568 }
1569 Self::Unspecified(t) => GStringFromError::Unspecified(func(t)),
1570 }
1571 }
1572}
1573
1574impl From<std::string::FromUtf8Error> for GStringFromError<Vec<u8>> {
1575 #[inline]
1576 fn from(e: std::string::FromUtf8Error) -> Self {
1577 let ue = e.utf8_error();
1578 Self::InvalidUtf8(GStringUtf8Error(e.into_bytes(), ue))
1579 }
1580}
1581
1582impl IntoGlibPtr<*mut c_char> for GString {
1583 #[inline]
1586 fn into_glib_ptr(self) -> *mut c_char {
1587 match self.0 {
1588 Inner::Native(ref s) => unsafe { ffi::g_strndup(s.as_ptr() as *const _, s.len()) },
1589 Inner::Foreign { ptr, .. } => {
1590 let _s = mem::ManuallyDrop::new(self);
1591 ptr.as_ptr()
1592 }
1593 Inner::Inline { len, ref data } => unsafe {
1594 ffi::g_strndup(data.as_ptr() as *const _, len as usize)
1595 },
1596 }
1597 }
1598}
1599
1600impl Default for GString {
1601 #[inline]
1602 fn default() -> Self {
1603 Self::new()
1604 }
1605}
1606
1607impl Clone for GString {
1608 #[inline]
1609 fn clone(&self) -> GString {
1610 self.as_str().into()
1611 }
1612}
1613
1614impl fmt::Debug for GString {
1615 #[inline]
1616 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1617 <&str as fmt::Debug>::fmt(&self.as_str(), f)
1618 }
1619}
1620
1621impl Drop for GString {
1622 #[inline]
1623 fn drop(&mut self) {
1624 if let Inner::Foreign { ptr, .. } = self.0 {
1625 unsafe {
1626 ffi::g_free(ptr.as_ptr() as *mut _);
1627 }
1628 }
1629 }
1630}
1631
1632impl fmt::Display for GString {
1633 #[inline]
1634 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1635 f.write_str(self.as_str())
1636 }
1637}
1638
1639impl hash::Hash for GString {
1640 #[inline]
1641 fn hash<H: hash::Hasher>(&self, state: &mut H) {
1642 self.as_str().hash(state)
1643 }
1644}
1645
1646impl Borrow<GStr> for GString {
1647 #[inline]
1648 fn borrow(&self) -> &GStr {
1649 self.as_gstr()
1650 }
1651}
1652
1653impl Borrow<str> for GString {
1654 #[inline]
1655 fn borrow(&self) -> &str {
1656 self.as_str()
1657 }
1658}
1659
1660impl Ord for GString {
1661 #[inline]
1662 fn cmp(&self, other: &GString) -> Ordering {
1663 self.as_str().cmp(other.as_str())
1664 }
1665}
1666
1667impl PartialOrd for GString {
1668 #[inline]
1669 fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1670 Some(self.cmp(other))
1671 }
1672}
1673
1674impl PartialEq for GString {
1675 #[inline]
1676 fn eq(&self, other: &GString) -> bool {
1677 self.as_str() == other.as_str()
1678 }
1679}
1680
1681impl PartialEq<GString> for String {
1682 #[inline]
1683 fn eq(&self, other: &GString) -> bool {
1684 self.as_str() == other.as_str()
1685 }
1686}
1687
1688impl PartialEq<GStr> for GString {
1689 #[inline]
1690 fn eq(&self, other: &GStr) -> bool {
1691 self.as_str() == other.as_str()
1692 }
1693}
1694
1695impl PartialEq<&GStr> for GString {
1696 #[inline]
1697 fn eq(&self, other: &&GStr) -> bool {
1698 self.as_str() == other.as_str()
1699 }
1700}
1701
1702impl PartialEq<str> for GString {
1703 #[inline]
1704 fn eq(&self, other: &str) -> bool {
1705 self.as_str() == other
1706 }
1707}
1708
1709impl PartialEq<&str> for GString {
1710 #[inline]
1711 fn eq(&self, other: &&str) -> bool {
1712 self.as_str() == *other
1713 }
1714}
1715
1716impl PartialEq<GString> for &GStr {
1717 #[inline]
1718 fn eq(&self, other: &GString) -> bool {
1719 self.as_str() == other.as_str()
1720 }
1721}
1722
1723impl PartialEq<GString> for &str {
1724 #[inline]
1725 fn eq(&self, other: &GString) -> bool {
1726 *self == other.as_str()
1727 }
1728}
1729
1730impl PartialEq<String> for GString {
1731 #[inline]
1732 fn eq(&self, other: &String) -> bool {
1733 self.as_str() == other.as_str()
1734 }
1735}
1736
1737impl PartialEq<GString> for str {
1738 #[inline]
1739 fn eq(&self, other: &GString) -> bool {
1740 self == other.as_str()
1741 }
1742}
1743
1744impl PartialEq<GString> for GStr {
1745 #[inline]
1746 fn eq(&self, other: &GString) -> bool {
1747 self.as_str() == other.as_str()
1748 }
1749}
1750
1751impl PartialOrd<GString> for String {
1752 #[inline]
1753 fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1754 Some(self.cmp(&String::from(other.as_str())))
1755 }
1756}
1757
1758impl PartialOrd<String> for GString {
1759 #[inline]
1760 fn partial_cmp(&self, other: &String) -> Option<Ordering> {
1761 Some(self.as_str().cmp(other.as_str()))
1762 }
1763}
1764
1765impl PartialOrd<GString> for GStr {
1766 #[inline]
1767 fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1768 Some(self.as_str().cmp(other))
1769 }
1770}
1771
1772impl PartialOrd<GStr> for GString {
1773 #[inline]
1774 fn partial_cmp(&self, other: &GStr) -> Option<Ordering> {
1775 Some(self.as_str().cmp(other.as_str()))
1776 }
1777}
1778
1779impl PartialOrd<GString> for str {
1780 #[inline]
1781 fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1782 Some(self.cmp(other))
1783 }
1784}
1785
1786impl PartialOrd<str> for GString {
1787 #[inline]
1788 fn partial_cmp(&self, other: &str) -> Option<Ordering> {
1789 Some(self.as_str().cmp(other))
1790 }
1791}
1792
1793impl Eq for GString {}
1794
1795impl AsRef<GStr> for GString {
1796 #[inline]
1797 fn as_ref(&self) -> &GStr {
1798 self.as_gstr()
1799 }
1800}
1801
1802impl AsRef<str> for GString {
1803 #[inline]
1804 fn as_ref(&self) -> &str {
1805 self.as_str()
1806 }
1807}
1808
1809impl AsRef<OsStr> for GString {
1810 #[inline]
1811 fn as_ref(&self) -> &OsStr {
1812 OsStr::new(self.as_str())
1813 }
1814}
1815
1816impl AsRef<Path> for GString {
1817 #[inline]
1818 fn as_ref(&self) -> &Path {
1819 Path::new(self.as_str())
1820 }
1821}
1822
1823impl AsRef<[u8]> for GString {
1824 #[inline]
1825 fn as_ref(&self) -> &[u8] {
1826 self.as_str().as_bytes()
1827 }
1828}
1829
1830impl Deref for GString {
1831 type Target = str;
1832
1833 #[inline]
1834 fn deref(&self) -> &str {
1835 self.as_str()
1836 }
1837}
1838
1839impl From<GString> for String {
1840 #[inline]
1841 fn from(mut s: GString) -> Self {
1842 match &mut s.0 {
1843 Inner::Native(s) => {
1844 let mut s = String::from(mem::replace(s, "".into()));
1846 let _nul = s.pop();
1847 debug_assert_eq!(_nul, Some('\0'));
1848 s
1849 }
1850 Inner::Foreign { len, .. } if *len == 0 => String::new(),
1851 Inner::Foreign { ptr, len } => unsafe {
1852 let slice = slice::from_raw_parts(ptr.as_ptr() as *const u8, *len);
1854 std::str::from_utf8_unchecked(slice).into()
1855 },
1856 Inner::Inline { len, data } => unsafe {
1857 std::str::from_utf8_unchecked(data.get_unchecked(..*len as usize)).to_owned()
1858 },
1859 }
1860 }
1861}
1862
1863impl From<GString> for Box<str> {
1864 #[inline]
1865 fn from(s: GString) -> Self {
1866 String::from(s).into()
1868 }
1869}
1870
1871impl From<GString> for Vec<u8> {
1872 #[inline]
1873 fn from(value: GString) -> Vec<u8> {
1874 value.into_bytes_with_nul()
1875 }
1876}
1877
1878impl TryFrom<GString> for CString {
1879 type Error = GStringInteriorNulError<GString>;
1880 #[inline]
1881 fn try_from(value: GString) -> Result<Self, Self::Error> {
1882 if let Some(nul_pos) = memchr::memchr(0, value.as_bytes()) {
1883 return Err(GStringInteriorNulError(
1884 value,
1885 GStrInteriorNulError(nul_pos),
1886 ));
1887 }
1888 let v = value.into_bytes_with_nul();
1889 Ok(unsafe { CString::from_vec_with_nul_unchecked(v) })
1890 }
1891}
1892
1893impl From<GString> for OsString {
1894 #[inline]
1895 fn from(s: GString) -> Self {
1896 OsString::from(String::from(s))
1897 }
1898}
1899
1900impl From<GString> for PathBuf {
1901 #[inline]
1902 fn from(s: GString) -> Self {
1903 PathBuf::from(OsString::from(s))
1904 }
1905}
1906
1907impl From<String> for GString {
1908 #[inline]
1909 fn from(mut s: String) -> Self {
1910 if cfg!(debug_assertions) {
1912 GStr::check_interior_nuls(&s).unwrap();
1913 }
1914 if s.is_empty() {
1915 Self::new()
1916 } else {
1917 s.reserve_exact(1);
1918 s.push('\0');
1919 Self(Inner::Native(s.into()))
1921 }
1922 }
1923}
1924
1925impl From<Box<str>> for GString {
1926 #[inline]
1927 fn from(s: Box<str>) -> Self {
1928 s.into_string().into()
1930 }
1931}
1932
1933impl<'a> From<Cow<'a, str>> for GString {
1934 #[inline]
1935 fn from(s: Cow<'a, str>) -> Self {
1936 match s {
1937 Cow::Borrowed(s) => Self::from(s),
1938 Cow::Owned(s) => Self::from(s),
1939 }
1940 }
1941}
1942
1943impl From<&GStr> for GString {
1944 #[inline]
1945 fn from(s: &GStr) -> GString {
1946 s.to_owned()
1947 }
1948}
1949
1950impl From<&str> for GString {
1951 #[inline]
1952 fn from(s: &str) -> Self {
1953 if cfg!(debug_assertions) {
1954 GStr::check_interior_nuls(s).unwrap();
1955 }
1956 if s.len() < INLINE_LEN {
1957 let mut data = <[u8; INLINE_LEN]>::default();
1958 let b = s.as_bytes();
1959 unsafe { data.get_unchecked_mut(..b.len()) }.copy_from_slice(b);
1960 return Self(Inner::Inline {
1961 len: b.len() as u8,
1962 data,
1963 });
1964 }
1965 unsafe {
1967 let copy = ffi::g_strndup(s.as_ptr() as *const c_char, s.len());
1969 GString(Inner::Foreign {
1970 ptr: ptr::NonNull::new_unchecked(copy),
1971 len: s.len(),
1972 })
1973 }
1974 }
1975}
1976
1977impl From<&String> for GString {
1978 #[inline]
1979 fn from(s: &String) -> Self {
1980 GString::from(s.as_str())
1981 }
1982}
1983
1984impl From<GStringPtr> for GString {
1985 #[inline]
1986 fn from(s: GStringPtr) -> Self {
1987 let s = mem::ManuallyDrop::new(s);
1988 let len = unsafe { GStr::from_ptr(s.0.as_ptr()).len() };
1989 GString(Inner::Foreign { ptr: s.0, len })
1990 }
1991}
1992
1993impl TryFrom<CString> for GString {
1994 type Error = GStringUtf8Error<CString>;
1995 #[inline]
1996 fn try_from(value: CString) -> Result<Self, Self::Error> {
1997 if value.as_bytes().is_empty() {
1998 Ok(Self::new())
1999 } else {
2000 let s = String::from_utf8(value.into_bytes_with_nul()).map_err(|e| {
2003 let err = e.utf8_error();
2004 GStringUtf8Error(
2005 unsafe { CString::from_vec_with_nul_unchecked(e.into_bytes()) },
2006 err,
2007 )
2008 })?;
2009 Ok(Self(Inner::Native(s.into())))
2010 }
2011 }
2012}
2013
2014impl TryFrom<OsString> for GString {
2015 type Error = GStringFromError<OsString>;
2016 #[inline]
2017 fn try_from(value: OsString) -> Result<Self, Self::Error> {
2018 Self::from_string_checked(value.into_string().map_err(GStringFromError::Unspecified)?)
2019 .map_err(|e| GStringFromError::from(e).convert(OsString::from))
2020 }
2021}
2022
2023impl TryFrom<PathBuf> for GString {
2024 type Error = GStringFromError<PathBuf>;
2025 #[inline]
2026 fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
2027 GString::try_from(value.into_os_string()).map_err(|e| e.convert(PathBuf::from))
2028 }
2029}
2030
2031impl TryFrom<&CStr> for GString {
2032 type Error = std::str::Utf8Error;
2033 #[inline]
2034 fn try_from(value: &CStr) -> Result<Self, Self::Error> {
2035 value.to_str()?;
2037 let gstr = unsafe { GStr::from_utf8_with_nul_unchecked(value.to_bytes_with_nul()) };
2038 Ok(gstr.to_owned())
2039 }
2040}
2041
2042impl<'a> From<Cow<'a, GStr>> for GString {
2043 #[inline]
2044 fn from(s: Cow<'a, GStr>) -> Self {
2045 s.into_owned()
2046 }
2047}
2048
2049impl<'a> From<&'a GString> for Cow<'a, GStr> {
2050 #[inline]
2051 fn from(s: &'a GString) -> Self {
2052 Cow::Borrowed(s.as_gstr())
2053 }
2054}
2055
2056impl From<GString> for Cow<'_, GStr> {
2057 #[inline]
2058 fn from(v: GString) -> Self {
2059 Cow::Owned(v)
2060 }
2061}
2062
2063impl<'a> From<&'a GStr> for Cow<'a, GStr> {
2064 #[inline]
2065 fn from(v: &'a GStr) -> Self {
2066 Cow::Borrowed(v)
2067 }
2068}
2069
2070#[doc(hidden)]
2071impl FromGlibPtrFull<*mut u8> for GString {
2072 #[inline]
2073 unsafe fn from_glib_full(ptr: *mut u8) -> Self {
2074 unsafe {
2075 debug_assert!(!ptr.is_null());
2076
2077 let cstr = CStr::from_ptr(ptr as *const _);
2078 debug_assert!(cstr.to_str().is_ok());
2080 Self(Inner::Foreign {
2081 ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
2082 len: cstr.to_bytes().len(),
2083 })
2084 }
2085 }
2086}
2087
2088#[doc(hidden)]
2089impl FromGlibPtrFull<*mut i8> for GString {
2090 #[inline]
2091 unsafe fn from_glib_full(ptr: *mut i8) -> Self {
2092 unsafe { from_glib_full(ptr as *mut u8) }
2093 }
2094}
2095
2096#[doc(hidden)]
2097impl FromGlibPtrFull<*const u8> for GString {
2098 #[inline]
2099 unsafe fn from_glib_full(ptr: *const u8) -> Self {
2100 unsafe { from_glib_full(ptr as *mut u8) }
2101 }
2102}
2103
2104#[doc(hidden)]
2105impl FromGlibPtrFull<*const i8> for GString {
2106 #[inline]
2107 unsafe fn from_glib_full(ptr: *const i8) -> Self {
2108 unsafe { from_glib_full(ptr as *mut u8) }
2109 }
2110}
2111
2112#[doc(hidden)]
2113impl FromGlibPtrNone<*const u8> for GString {
2114 #[inline]
2115 unsafe fn from_glib_none(ptr: *const u8) -> Self {
2116 unsafe {
2117 debug_assert!(!ptr.is_null());
2118 <&GStr>::from_glib_none(ptr).into()
2119 }
2120 }
2121}
2122
2123#[doc(hidden)]
2124impl FromGlibPtrNone<*const i8> for GString {
2125 #[inline]
2126 unsafe fn from_glib_none(ptr: *const i8) -> Self {
2127 unsafe { from_glib_none(ptr as *const u8) }
2128 }
2129}
2130
2131#[doc(hidden)]
2132impl FromGlibPtrNone<*mut u8> for GString {
2133 #[inline]
2134 unsafe fn from_glib_none(ptr: *mut u8) -> Self {
2135 unsafe { from_glib_none(ptr as *const u8) }
2136 }
2137}
2138
2139#[doc(hidden)]
2140impl FromGlibPtrNone<*mut i8> for GString {
2141 #[inline]
2142 unsafe fn from_glib_none(ptr: *mut i8) -> Self {
2143 unsafe { from_glib_none(ptr as *const u8) }
2144 }
2145}
2146
2147#[doc(hidden)]
2148impl FromGlibPtrBorrow<*const u8> for GString {
2149 #[inline]
2150 unsafe fn from_glib_borrow(ptr: *const u8) -> Borrowed<Self> {
2151 unsafe {
2152 debug_assert!(!ptr.is_null());
2153
2154 let cstr = CStr::from_ptr(ptr as *const _);
2156 debug_assert!(cstr.to_str().is_ok());
2157 Borrowed::new(Self(Inner::Foreign {
2158 ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
2159 len: cstr.to_bytes().len(),
2160 }))
2161 }
2162 }
2163}
2164
2165#[doc(hidden)]
2166impl FromGlibPtrBorrow<*const i8> for GString {
2167 #[inline]
2168 unsafe fn from_glib_borrow(ptr: *const i8) -> Borrowed<Self> {
2169 unsafe { from_glib_borrow(ptr as *const u8) }
2170 }
2171}
2172
2173#[doc(hidden)]
2174impl FromGlibPtrBorrow<*mut u8> for GString {
2175 #[inline]
2176 unsafe fn from_glib_borrow(ptr: *mut u8) -> Borrowed<Self> {
2177 unsafe { from_glib_borrow(ptr as *const u8) }
2178 }
2179}
2180
2181#[doc(hidden)]
2182impl FromGlibPtrBorrow<*mut i8> for GString {
2183 #[inline]
2184 unsafe fn from_glib_borrow(ptr: *mut i8) -> Borrowed<Self> {
2185 unsafe { from_glib_borrow(ptr as *const u8) }
2186 }
2187}
2188
2189#[allow(clippy::unnecessary_cast)]
2190#[doc(hidden)]
2191impl<'a> ToGlibPtr<'a, *const u8> for GString {
2192 type Storage = PhantomData<&'a Self>;
2193
2194 #[inline]
2195 fn to_glib_none(&'a self) -> Stash<'a, *const u8, Self> {
2196 Stash(self.as_ptr() as *const u8, PhantomData)
2197 }
2198
2199 #[inline]
2200 fn to_glib_full(&self) -> *const u8 {
2201 self.clone().into_glib_ptr() as *const u8
2202 }
2203}
2204
2205#[allow(clippy::unnecessary_cast)]
2206#[doc(hidden)]
2207impl<'a> ToGlibPtr<'a, *const i8> for GString {
2208 type Storage = PhantomData<&'a Self>;
2209
2210 #[inline]
2211 fn to_glib_none(&'a self) -> Stash<'a, *const i8, Self> {
2212 Stash(self.as_ptr() as *const i8, PhantomData)
2213 }
2214
2215 #[inline]
2216 fn to_glib_full(&self) -> *const i8 {
2217 self.clone().into_glib_ptr() as *const i8
2218 }
2219}
2220
2221#[allow(clippy::unnecessary_cast)]
2222#[doc(hidden)]
2223impl<'a> ToGlibPtr<'a, *mut u8> for GString {
2224 type Storage = PhantomData<&'a Self>;
2225
2226 #[inline]
2227 fn to_glib_none(&'a self) -> Stash<'a, *mut u8, Self> {
2228 Stash(self.as_ptr() as *mut u8, PhantomData)
2229 }
2230
2231 #[inline]
2232 fn to_glib_full(&self) -> *mut u8 {
2233 self.clone().into_glib_ptr() as *mut u8
2234 }
2235}
2236
2237#[allow(clippy::unnecessary_cast)]
2238#[doc(hidden)]
2239impl<'a> ToGlibPtr<'a, *mut i8> for GString {
2240 type Storage = PhantomData<&'a Self>;
2241
2242 #[inline]
2243 fn to_glib_none(&'a self) -> Stash<'a, *mut i8, Self> {
2244 Stash(self.as_ptr() as *mut i8, PhantomData)
2245 }
2246
2247 #[inline]
2248 fn to_glib_full(&self) -> *mut i8 {
2249 self.clone().into_glib_ptr() as *mut i8
2250 }
2251}
2252
2253#[doc(hidden)]
2254impl FromGlibContainer<*const c_char, *const i8> for GString {
2255 unsafe fn from_glib_none_num(ptr: *const i8, num: usize) -> Self {
2256 unsafe {
2257 if num == 0 || ptr.is_null() {
2258 return Self::default();
2259 }
2260 let slice = slice::from_raw_parts(ptr as *const u8, num);
2261 if cfg!(debug_assertions) {
2262 std::str::from_utf8(slice).unwrap().into()
2264 } else {
2265 std::str::from_utf8_unchecked(slice).into()
2266 }
2267 }
2268 }
2269
2270 unsafe fn from_glib_container_num(ptr: *const i8, num: usize) -> Self {
2271 unsafe {
2272 if num == 0 || ptr.is_null() {
2273 return Self::default();
2274 }
2275
2276 if cfg!(debug_assertions) {
2277 let slice = slice::from_raw_parts(ptr as *const u8, num);
2279 std::str::from_utf8(slice).unwrap();
2280 }
2281
2282 GString(Inner::Foreign {
2283 ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
2284 len: num,
2285 })
2286 }
2287 }
2288
2289 unsafe fn from_glib_full_num(ptr: *const i8, num: usize) -> Self {
2290 unsafe {
2291 if num == 0 || ptr.is_null() {
2292 return Self::default();
2293 }
2294
2295 if cfg!(debug_assertions) {
2296 let slice = slice::from_raw_parts(ptr as *const u8, num);
2298 std::str::from_utf8(slice).unwrap();
2299 }
2300
2301 GString(Inner::Foreign {
2302 ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
2303 len: num,
2304 })
2305 }
2306 }
2307}
2308
2309#[doc(hidden)]
2310impl FromGlibContainer<*const c_char, *mut i8> for GString {
2311 unsafe fn from_glib_none_num(ptr: *mut i8, num: usize) -> Self {
2312 unsafe { FromGlibContainer::from_glib_none_num(ptr as *const i8, num) }
2313 }
2314
2315 unsafe fn from_glib_container_num(ptr: *mut i8, num: usize) -> Self {
2316 unsafe { FromGlibContainer::from_glib_container_num(ptr as *const i8, num) }
2317 }
2318
2319 unsafe fn from_glib_full_num(ptr: *mut i8, num: usize) -> Self {
2320 unsafe { FromGlibContainer::from_glib_full_num(ptr as *const i8, num) }
2321 }
2322}
2323
2324#[doc(hidden)]
2325impl FromGlibContainer<*const c_char, *const u8> for GString {
2326 unsafe fn from_glib_none_num(ptr: *const u8, num: usize) -> Self {
2327 unsafe { FromGlibContainer::from_glib_none_num(ptr as *const i8, num) }
2328 }
2329
2330 unsafe fn from_glib_container_num(ptr: *const u8, num: usize) -> Self {
2331 unsafe { FromGlibContainer::from_glib_container_num(ptr as *const i8, num) }
2332 }
2333
2334 unsafe fn from_glib_full_num(ptr: *const u8, num: usize) -> Self {
2335 unsafe { FromGlibContainer::from_glib_full_num(ptr as *const i8, num) }
2336 }
2337}
2338
2339#[doc(hidden)]
2340impl FromGlibContainer<*const c_char, *mut u8> for GString {
2341 unsafe fn from_glib_none_num(ptr: *mut u8, num: usize) -> Self {
2342 unsafe { FromGlibContainer::from_glib_none_num(ptr as *const i8, num) }
2343 }
2344
2345 unsafe fn from_glib_container_num(ptr: *mut u8, num: usize) -> Self {
2346 unsafe { FromGlibContainer::from_glib_container_num(ptr as *const i8, num) }
2347 }
2348
2349 unsafe fn from_glib_full_num(ptr: *mut u8, num: usize) -> Self {
2350 unsafe { FromGlibContainer::from_glib_full_num(ptr as *const i8, num) }
2351 }
2352}
2353
2354impl GlibPtrDefault for GString {
2355 type GlibType = *const c_char;
2356}
2357
2358impl StaticType for GString {
2359 #[inline]
2360 fn static_type() -> Type {
2361 String::static_type()
2362 }
2363}
2364
2365impl crate::value::ValueType for GString {
2366 type Type = String;
2367}
2368
2369impl crate::value::ValueTypeOptional for GString {}
2370
2371unsafe impl<'a> crate::value::FromValue<'a> for GString {
2372 type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>;
2373
2374 #[inline]
2375 unsafe fn from_value(value: &'a Value) -> Self {
2376 unsafe { Self::from(<&str>::from_value(value)) }
2377 }
2378}
2379
2380impl crate::value::ToValue for GString {
2381 #[inline]
2382 fn to_value(&self) -> Value {
2383 <&str>::to_value(&self.as_str())
2384 }
2385
2386 #[inline]
2387 fn value_type(&self) -> Type {
2388 String::static_type()
2389 }
2390}
2391
2392impl crate::value::ToValueOptional for GString {
2393 #[inline]
2394 fn to_value_optional(s: Option<&Self>) -> Value {
2395 <str>::to_value_optional(s.as_ref().map(|s| s.as_str()))
2396 }
2397}
2398
2399impl From<GString> for Value {
2400 #[inline]
2401 fn from(s: GString) -> Self {
2402 unsafe {
2403 let mut value = Value::for_value_type::<GString>();
2404 gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, s.into_glib_ptr());
2405 value
2406 }
2407 }
2408}
2409
2410impl StaticType for Vec<GString> {
2411 #[inline]
2412 fn static_type() -> Type {
2413 <Vec<String>>::static_type()
2414 }
2415}
2416
2417impl crate::value::ValueType for Vec<GString> {
2418 type Type = Vec<GString>;
2419}
2420
2421unsafe impl<'a> crate::value::FromValue<'a> for Vec<GString> {
2422 type Checker = crate::value::GenericValueTypeChecker<Self>;
2423
2424 #[inline]
2425 unsafe fn from_value(value: &'a Value) -> Self {
2426 unsafe {
2427 let ptr =
2428 gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
2429 FromGlibPtrContainer::from_glib_none(ptr)
2430 }
2431 }
2432}
2433
2434impl ToValue for Vec<GString> {
2435 #[inline]
2436 fn to_value(&self) -> Value {
2437 unsafe {
2438 let mut value = Value::for_value_type::<Self>();
2439 let ptr: *mut *mut c_char = self.to_glib_full();
2440 gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, ptr as *const c_void);
2441 value
2442 }
2443 }
2444
2445 #[inline]
2446 fn value_type(&self) -> Type {
2447 <Vec<GString>>::static_type()
2448 }
2449}
2450
2451impl From<Vec<GString>> for Value {
2452 #[inline]
2453 fn from(v: Vec<GString>) -> Self {
2454 unsafe {
2455 let v_ptr =
2456 ffi::g_malloc(mem::size_of::<*mut c_char>() * (v.len() + 1)) as *mut *mut c_char;
2457 v_ptr.add(v.len()).write(ptr::null_mut());
2458 for (i, s) in v.into_iter().enumerate() {
2459 v_ptr.add(i).write(s.into_glib_ptr());
2460 }
2461
2462 let mut value = Value::for_value_type::<Vec<GString>>();
2463 gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, v_ptr as *const c_void);
2464 value
2465 }
2466 }
2467}
2468
2469impl_from_glib_container_as_vec_string!(GString, *const c_char);
2470impl_from_glib_container_as_vec_string!(GString, *mut c_char);
2471
2472pub trait IntoGStr {
2475 fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T;
2476}
2477
2478impl IntoGStr for &GStr {
2479 #[inline]
2480 fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2481 f(self)
2482 }
2483}
2484
2485impl IntoGStr for GString {
2486 #[inline]
2487 fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2488 f(self.as_gstr())
2489 }
2490}
2491
2492impl IntoGStr for &GString {
2493 #[inline]
2494 fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2495 f(self.as_gstr())
2496 }
2497}
2498
2499const MAX_STACK_ALLOCATION: usize = 384;
2502
2503impl IntoGStr for &str {
2504 #[inline]
2505 fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2506 if self.len() < MAX_STACK_ALLOCATION {
2507 let mut s = mem::MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
2508 let ptr = s.as_mut_ptr() as *mut u8;
2509 let gs = unsafe {
2510 ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len());
2511 ptr.add(self.len()).write(0);
2512 GStr::from_utf8_with_nul_unchecked(slice::from_raw_parts(ptr, self.len() + 1))
2513 };
2514 f(gs)
2515 } else {
2516 f(GString::from(self).as_gstr())
2517 }
2518 }
2519}
2520
2521impl IntoGStr for String {
2522 #[inline]
2523 fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(mut self, f: F) -> T {
2524 let len = self.len();
2525 if len < self.capacity() {
2526 self.reserve_exact(1);
2527 self.push('\0');
2528 let gs = unsafe { GStr::from_utf8_with_nul_unchecked(self.as_bytes()) };
2529 f(gs)
2530 } else if len < MAX_STACK_ALLOCATION {
2531 self.as_str().run_with_gstr(f)
2532 } else {
2533 f(GString::from(self).as_gstr())
2534 }
2535 }
2536}
2537
2538impl IntoGStr for &String {
2539 #[inline]
2540 fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2541 self.as_str().run_with_gstr(f)
2542 }
2543}
2544
2545pub const NONE_STR: Option<&'static str> = None;
2546
2547pub trait IntoOptionalGStr {
2551 fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T;
2552}
2553
2554impl<S: IntoGStr> IntoOptionalGStr for Option<S> {
2555 #[inline]
2556 fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T {
2557 match self {
2558 Some(t) => t.run_with_gstr(|s| f(Some(s))),
2559 None => f(None),
2560 }
2561 }
2562}
2563
2564#[cfg(test)]
2565#[allow(clippy::disallowed_names)]
2566mod tests {
2567 use std::ffi::CString;
2568
2569 use super::*;
2570
2571 #[test]
2572 fn test_gstring() {
2573 let data = CString::new("foo").unwrap();
2574 let ptr = data.as_ptr();
2575
2576 unsafe {
2577 let ptr_copy = ffi::g_strdup(ptr);
2578 let gstring = GString::from_glib_full(ptr_copy);
2579 assert_eq!(gstring.as_str(), "foo");
2580 let foo: Box<str> = gstring.into();
2581 assert_eq!(foo.as_ref(), "foo");
2582 }
2583 }
2584
2585 #[test]
2586 fn test_owned_glib_string() {
2587 let data = CString::new("foo").unwrap();
2588 let ptr = data.as_ptr();
2589 unsafe {
2590 let ptr_copy = ffi::g_strdup(ptr);
2591 let gstr = GString::from_glib_full(ptr_copy);
2592 assert_eq!(gstr, "foo");
2593 }
2594 }
2595
2596 #[test]
2597 fn test_gstring_from_str() {
2598 let gstring: GString = "foo".into();
2599 assert_eq!(gstring.as_str(), "foo");
2600 let foo: Box<str> = gstring.into();
2601 assert_eq!(foo.as_ref(), "foo");
2602 }
2603
2604 #[test]
2605 fn test_string_from_gstring() {
2606 let gstring = GString::from("foo");
2607 assert_eq!(gstring.as_str(), "foo");
2608 let s = String::from(gstring);
2609 assert_eq!(s, "foo");
2610 }
2611
2612 #[test]
2613 fn test_gstring_from_cstring() {
2614 let cstr = CString::new("foo").unwrap();
2615 let gstring = GString::try_from(cstr).unwrap();
2616 assert_eq!(gstring.as_str(), "foo");
2617 let foo: Box<str> = gstring.into();
2618 assert_eq!(foo.as_ref(), "foo");
2619 }
2620
2621 #[test]
2622 fn test_string_from_gstring_from_cstring() {
2623 let cstr = CString::new("foo").unwrap();
2624 let gstring = GString::try_from(cstr).unwrap();
2625 assert_eq!(gstring.as_str(), "foo");
2626 let s = String::from(gstring);
2627 assert_eq!(s, "foo");
2628 }
2629
2630 #[test]
2631 fn test_vec_u8_to_gstring() {
2632 let v: &[u8] = b"foo";
2633 let s: GString = GString::from_utf8(Vec::from(v)).unwrap();
2634 assert_eq!(s.as_str(), "foo");
2635 }
2636
2637 #[test]
2638 fn test_value_from_vec_gstring() {
2639 fn roundtrip(s: GString) {
2640 let vec = vec![s.clone()];
2641 let value = crate::Value::from(vec);
2642 let vec: Vec<GString> = value.get().unwrap();
2643 assert_eq!(vec.len(), 1);
2644 assert_eq!(s, vec[0]);
2645 }
2646
2647 roundtrip(GString::from("foo"));
2648 roundtrip(GString::from("very very very long string".to_owned()));
2649 roundtrip(GString::from(gstr!("very very very long string")));
2650 }
2651
2652 #[test]
2653 fn test_as_ref_path() {
2654 fn foo<P: AsRef<Path>>(_path: P) {}
2655 let gstring: GString = "/my/path/".into();
2656 let gstr: &GStr = gstring.as_gstr();
2657 foo(gstr);
2658 foo(gstring);
2659 }
2660
2661 #[test]
2662 fn test_from_glib_container() {
2663 unsafe {
2664 let test_a: GString = FromGlibContainer::from_glib_container_num(
2665 ffi::g_strdup("hello_world\0".as_ptr() as *const _),
2666 5,
2667 );
2668 assert_eq!("hello", test_a.as_str());
2669
2670 let test_b: GString = FromGlibContainer::from_glib_none_num("hello_world".as_ptr(), 5);
2671 assert_eq!("hello", test_b.as_str());
2672
2673 let test_c: GString =
2674 FromGlibContainer::from_glib_none_num(std::ptr::null::<std::os::raw::c_char>(), 0);
2675 assert_eq!("", test_c.as_str());
2676
2677 let test_d: GString = FromGlibContainer::from_glib_none_num("".as_ptr(), 0);
2678 assert_eq!("", test_d.as_str());
2679
2680 let test_e: GString =
2681 FromGlibContainer::from_glib_container_num(ffi::g_strdup(std::ptr::null()), 0);
2682 assert_eq!("", test_e.as_str());
2683 }
2684 }
2685
2686 #[test]
2687 fn test_hashmap() {
2688 use std::collections::HashMap;
2689
2690 let gstring = GString::from("foo");
2691 assert_eq!(gstring.as_str(), "foo");
2692 let mut h: HashMap<GString, i32> = HashMap::new();
2693 h.insert(gstring, 42);
2694 let gstring: GString = "foo".into();
2695 assert!(h.contains_key(&gstring));
2696 }
2697
2698 #[test]
2699 fn test_gstring_from_ptr_lossy() {
2700 let data = CString::new("foo").unwrap();
2701 let ptr = data.as_ptr();
2702
2703 unsafe {
2704 let gstring = GString::from_ptr_lossy(ptr);
2705 assert_eq!(gstring.as_str(), "foo");
2706 assert_eq!(ptr, gstring.as_ptr());
2707 }
2708
2709 let data = b"foo\xF0\x90\x80bar\0";
2710 let ptr = data.as_ptr();
2711
2712 unsafe {
2713 let gstring = GString::from_ptr_lossy(ptr as *const _);
2714 assert_eq!(gstring.as_str(), "foo���bar");
2715 assert_ne!(ptr, gstring.as_ptr() as *const _);
2716 }
2717 }
2718
2719 #[test]
2720 fn gformat() {
2721 let s = gformat!("bla bla {} bla", 123);
2722 assert_eq!(s, "bla bla 123 bla");
2723 }
2724
2725 #[test]
2726 fn layout() {
2727 enum NoInline {
2729 _Native(Box<str>),
2730 _Foreign(ptr::NonNull<c_char>, usize),
2731 }
2732 assert_eq!(mem::size_of::<GString>(), mem::size_of::<NoInline>());
2733 assert_eq!(mem::size_of::<GString>(), mem::size_of::<String>());
2734 }
2735}