Skip to content

Commit 130b601

Browse files
committed
glib: Use TryFrom for GString conversions
1 parent a6d1cdf commit 130b601

File tree

6 files changed

+83
-58
lines changed

6 files changed

+83
-58
lines changed

gio/src/file.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl<O: IsA<File>> FileExtManual for O {
237237
+ 'static,
238238
>,
239239
> {
240-
let etag = etag.map(glib::GString::from);
240+
let etag = etag.map(|e| glib::GString::try_from(e).unwrap());
241241
Box::pin(crate::GioFuture::new(
242242
self,
243243
move |obj, cancellable, send| {

glib/src/gstring.rs

Lines changed: 63 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ impl GString {
435435

436436
impl Clone for GString {
437437
fn clone(&self) -> GString {
438-
self.as_str().into()
438+
self.as_gstr().to_owned()
439439
}
440440
}
441441

@@ -445,9 +445,9 @@ impl fmt::Debug for GString {
445445
}
446446
}
447447

448-
impl Drop for GString {
448+
impl Drop for Inner {
449449
fn drop(&mut self) {
450-
if let Inner::Foreign { ptr, .. } = self.0 {
450+
if let Inner::Foreign { ptr, .. } = self {
451451
unsafe {
452452
ffi::g_free(ptr.as_ptr() as *mut _);
453453
}
@@ -660,23 +660,23 @@ impl From<GString> for Box<str> {
660660
}
661661
}
662662

663-
impl From<String> for GString {
663+
impl TryFrom<String> for GString {
664+
type Error = std::ffi::NulError;
664665
#[inline]
665-
fn from(s: String) -> Self {
666+
fn try_from(s: String) -> Result<Self, Self::Error> {
666667
// Moves the content of the String
667-
unsafe {
668-
// No check for valid UTF-8 here
669-
let cstr = CString::from_vec_unchecked(s.into_bytes());
670-
GString(Inner::Native(Some(cstr)))
671-
}
668+
// Check for interior nul bytes
669+
let cstr = CString::new(s.into_bytes())?;
670+
Ok(GString(Inner::Native(Some(cstr))))
672671
}
673672
}
674673

675-
impl From<Box<str>> for GString {
674+
impl TryFrom<Box<str>> for GString {
675+
type Error = std::ffi::NulError;
676676
#[inline]
677-
fn from(s: Box<str>) -> Self {
677+
fn try_from(s: Box<str>) -> Result<Self, Self::Error> {
678678
// Moves the content of the String
679-
s.into_string().into()
679+
s.into_string().try_into()
680680
}
681681
}
682682

@@ -687,50 +687,68 @@ impl From<&GStr> for GString {
687687
}
688688
}
689689

690-
impl From<&str> for GString {
690+
impl TryFrom<&str> for GString {
691+
type Error = std::ffi::FromBytesWithNulError;
691692
#[inline]
692-
fn from(s: &str) -> Self {
693+
fn try_from(s: &str) -> Result<Self, Self::Error> {
693694
// Allocates with the GLib allocator
694695
unsafe {
695696
// No check for valid UTF-8 here
696697
let copy = ffi::g_malloc(s.len() + 1) as *mut c_char;
697698
ptr::copy_nonoverlapping(s.as_ptr() as *const c_char, copy, s.len() + 1);
698699
ptr::write(copy.add(s.len()), 0);
699700

700-
GString(Inner::Foreign {
701+
let inner = Inner::Foreign {
701702
ptr: ptr::NonNull::new_unchecked(copy),
702703
len: s.len(),
703-
})
704+
};
705+
706+
// Check for interior nul bytes
707+
std::ffi::CStr::from_bytes_with_nul(std::slice::from_raw_parts(
708+
copy as *const _,
709+
s.len() + 1,
710+
))?;
711+
712+
Ok(GString(inner))
704713
}
705714
}
706715
}
707716

708-
impl From<Vec<u8>> for GString {
717+
#[derive(thiserror::Error, Debug)]
718+
pub enum GStringError {
719+
#[error("invalid UTF-8")]
720+
Utf8(#[from] std::str::Utf8Error),
721+
#[error("interior nul bytes")]
722+
Nul(#[from] std::ffi::NulError),
723+
}
724+
725+
impl TryFrom<Vec<u8>> for GString {
726+
type Error = GStringError;
709727
#[inline]
710-
fn from(s: Vec<u8>) -> Self {
728+
fn try_from(s: Vec<u8>) -> Result<Self, Self::Error> {
711729
// Moves the content of the Vec
712-
// Also check if it's valid UTF-8
713-
let cstring = CString::new(s).expect("CString::new failed");
714-
cstring.into()
730+
// Also check if it's valid UTF-8 and has no interior nuls
731+
let cstring = CString::new(s)?;
732+
Ok(cstring.try_into()?)
715733
}
716734
}
717735

718-
impl From<CString> for GString {
719-
#[inline]
720-
fn from(s: CString) -> Self {
736+
impl TryFrom<CString> for GString {
737+
type Error = std::str::Utf8Error;
738+
fn try_from(s: CString) -> Result<Self, Self::Error> {
721739
// Moves the content of the CString
722740
// Also check if it's valid UTF-8
723-
assert!(s.to_str().is_ok());
724-
Self(Inner::Native(Some(s)))
741+
s.to_str()?;
742+
Ok(Self(Inner::Native(Some(s))))
725743
}
726744
}
727745

728-
impl From<&CStr> for GString {
746+
impl TryFrom<&CStr> for GString {
747+
type Error = std::str::Utf8Error;
729748
#[inline]
730-
fn from(c: &CStr) -> Self {
731-
// Creates a copy with the GLib allocator
732-
// Also check if it's valid UTF-8
733-
c.to_str().unwrap().into()
749+
fn try_from(c: &CStr) -> Result<Self, Self::Error> {
750+
let g: &GStr = c.try_into()?;
751+
Ok(g.to_owned())
734752
}
735753
}
736754

@@ -781,7 +799,7 @@ impl FromGlibPtrNone<*const u8> for GString {
781799
assert!(!ptr.is_null());
782800
let cstr = CStr::from_ptr(ptr as *const _);
783801
// Also check if it's valid UTF-8
784-
cstr.to_str().unwrap().into()
802+
cstr.try_into().unwrap()
785803
}
786804
}
787805

@@ -913,16 +931,16 @@ impl<'a> ToGlibPtr<'a, *mut i8> for GString {
913931
impl<'a> FromGlibContainer<*const c_char, *const i8> for GString {
914932
unsafe fn from_glib_none_num(ptr: *const i8, num: usize) -> Self {
915933
if num == 0 || ptr.is_null() {
916-
return Self::from("");
934+
return Self::try_from("").unwrap();
917935
}
918936
let slice = slice::from_raw_parts(ptr as *const u8, num);
919937
// Also check if it's valid UTF-8
920-
std::str::from_utf8(slice).unwrap().into()
938+
std::str::from_utf8(slice).unwrap().try_into().unwrap()
921939
}
922940

923941
unsafe fn from_glib_container_num(ptr: *const i8, num: usize) -> Self {
924942
if num == 0 || ptr.is_null() {
925-
return Self::from("");
943+
return Self::try_from("").unwrap();
926944
}
927945

928946
// Check if it's valid UTF-8
@@ -937,7 +955,7 @@ impl<'a> FromGlibContainer<*const c_char, *const i8> for GString {
937955

938956
unsafe fn from_glib_full_num(ptr: *const i8, num: usize) -> Self {
939957
if num == 0 || ptr.is_null() {
940-
return Self::from("");
958+
return Self::try_from("").unwrap();
941959
}
942960

943961
// Check if it's valid UTF-8
@@ -1016,7 +1034,7 @@ unsafe impl<'a> crate::value::FromValue<'a> for GString {
10161034
type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>;
10171035

10181036
unsafe fn from_value(value: &'a crate::Value) -> Self {
1019-
Self::from(<&str>::from_value(value))
1037+
Self::try_from(<&str>::from_value(value)).unwrap()
10201038
}
10211039
}
10221040

@@ -1106,15 +1124,15 @@ mod tests {
11061124

11071125
#[test]
11081126
fn test_gstring_from_str() {
1109-
let gstring: GString = "foo".into();
1127+
let gstring: GString = "foo".try_into().unwrap();
11101128
assert_eq!(gstring.as_str(), "foo");
11111129
let foo: Box<str> = gstring.into();
11121130
assert_eq!(foo.as_ref(), "foo");
11131131
}
11141132

11151133
#[test]
11161134
fn test_string_from_gstring() {
1117-
let gstring = GString::from("foo");
1135+
let gstring = GString::try_from("foo").unwrap();
11181136
assert_eq!(gstring.as_str(), "foo");
11191137
let s = String::from(gstring);
11201138
assert_eq!(s, "foo");
@@ -1123,7 +1141,7 @@ mod tests {
11231141
#[test]
11241142
fn test_gstring_from_cstring() {
11251143
let cstr = CString::new("foo").unwrap();
1126-
let gstring = GString::from(cstr);
1144+
let gstring = GString::try_from(cstr).unwrap();
11271145
assert_eq!(gstring.as_str(), "foo");
11281146
let foo: Box<str> = gstring.into();
11291147
assert_eq!(foo.as_ref(), "foo");
@@ -1132,7 +1150,7 @@ mod tests {
11321150
#[test]
11331151
fn test_string_from_gstring_from_cstring() {
11341152
let cstr = CString::new("foo").unwrap();
1135-
let gstring = GString::from(cstr);
1153+
let gstring = GString::try_from(cstr).unwrap();
11361154
assert_eq!(gstring.as_str(), "foo");
11371155
let s = String::from(gstring);
11381156
assert_eq!(s, "foo");
@@ -1141,7 +1159,7 @@ mod tests {
11411159
#[test]
11421160
fn test_vec_u8_to_gstring() {
11431161
let v: &[u8] = b"foo";
1144-
let s: GString = Vec::from(v).into();
1162+
let s: GString = Vec::from(v).try_into().unwrap();
11451163
assert_eq!(s.as_str(), "foo");
11461164
}
11471165

@@ -1174,11 +1192,11 @@ mod tests {
11741192
fn test_hashmap() {
11751193
use std::collections::HashMap;
11761194

1177-
let gstring = GString::from("foo");
1195+
let gstring = GString::try_from("foo").unwrap();
11781196
assert_eq!(gstring.as_str(), "foo");
11791197
let mut h: HashMap<GString, i32> = HashMap::new();
11801198
h.insert(gstring, 42);
1181-
let gstring: GString = "foo".into();
1199+
let gstring: GString = "foo".try_into().unwrap();
11821200
assert!(h.contains_key(&gstring));
11831201
}
11841202
}

glib/src/log.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,8 +859,8 @@ macro_rules! g_printerr {
859859
/// "MY_FIELD2" => "abc {}", "def";
860860
/// // single argument can be a &str or a &[u8] or anything else satsfying AsRef<[u8]>
861861
/// "MY_FIELD3" => CString::new("my string").unwrap().to_bytes();
862-
/// // field names can also be dynamic
863-
/// GString::from("MY_FIELD4") => b"a binary string".to_owned();
862+
/// // field names can also be dynamic, satisfying AsRef<GStr>
863+
/// GString::try_from("MY_FIELD4").unwrap() => b"a binary string".to_owned();
864864
/// // the main log message goes in the MESSAGE field
865865
/// "MESSAGE" => "test: {} {}", 1, 2, ;
866866
/// }

glib/src/utils.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -336,18 +336,18 @@ mod tests {
336336
fn test_filename_from_uri() {
337337
use crate::GString;
338338
use std::path::PathBuf;
339-
let uri: GString = "file:///foo/bar.txt".into();
339+
let uri: GString = "file:///foo/bar.txt".try_into().unwrap();
340340
if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) {
341341
assert_eq!(filename, PathBuf::from(r"/foo/bar.txt"));
342342
assert_eq!(hostname, None);
343343
} else {
344344
unreachable!();
345345
}
346346

347-
let uri: GString = "file://host/foo/bar.txt".into();
347+
let uri: GString = "file://host/foo/bar.txt".try_into().unwrap();
348348
if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) {
349349
assert_eq!(filename, PathBuf::from(r"/foo/bar.txt"));
350-
assert_eq!(hostname, Some(GString::from("host")));
350+
assert_eq!(hostname, Some(GString::try_from("host").unwrap()));
351351
} else {
352352
unreachable!();
353353
}
@@ -358,19 +358,19 @@ mod tests {
358358
use crate::GString;
359359
assert_eq!(
360360
crate::uri_parse_scheme("foo://bar"),
361-
Some(GString::from("foo"))
361+
Some(GString::try_from("foo").unwrap())
362362
);
363363
assert_eq!(crate::uri_parse_scheme("foo"), None);
364364

365365
let escaped = crate::uri_escape_string("&foo", None, true);
366-
assert_eq!(escaped, GString::from("%26foo"));
366+
assert_eq!(escaped, GString::try_from("%26foo").unwrap());
367367

368368
let unescaped = crate::uri_unescape_string(escaped.as_str(), None);
369-
assert_eq!(unescaped, Some(GString::from("&foo")));
369+
assert_eq!(unescaped, Some(GString::try_from("&foo").unwrap()));
370370

371371
assert_eq!(
372372
crate::uri_unescape_segment(Some("/foo"), None, None),
373-
Some(GString::from("/foo"))
373+
Some(GString::try_from("/foo").unwrap())
374374
);
375375
assert_eq!(crate::uri_unescape_segment(Some("/foo%"), None, None), None);
376376
}

glib/src/value.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,13 +1152,19 @@ mod tests {
11521152
let v = vec!["123", "456"].to_value();
11531153
assert_eq!(
11541154
v.get::<Vec<GString>>(),
1155-
Ok(vec![GString::from("123"), GString::from("456")])
1155+
Ok(vec![
1156+
GString::try_from("123").unwrap(),
1157+
GString::try_from("456").unwrap()
1158+
])
11561159
);
11571160

11581161
let v = vec![String::from("123"), String::from("456")].to_value();
11591162
assert_eq!(
11601163
v.get::<Vec<GString>>(),
1161-
Ok(vec![GString::from("123"), GString::from("456")])
1164+
Ok(vec![
1165+
GString::try_from("123").unwrap(),
1166+
GString::try_from("456").unwrap()
1167+
])
11621168
);
11631169
}
11641170

glib/tests/structured_log.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ fn structured_log() {
3535
}
3636
);
3737

38+
let my_meta3 = GString::try_from("MY_META3").unwrap();
3839
log_structured!(
3940
None,
4041
LogLevel::Message,
@@ -43,7 +44,7 @@ fn structured_log() {
4344
"MESSAGE" => "formatted with meta: {} {}", 123, 456.0;
4445
"MY_META2" => "def{}", "ghi";
4546
"EMPTY" => b"";
46-
GString::from("MY_META3") => b"bstring".to_owned();
47+
my_meta3 => b"bstring".to_owned();
4748
}
4849
);
4950
log_structured_array(

0 commit comments

Comments
 (0)