Skip to content

Commit

Permalink
glib: Use TryFrom for GString conversions
Browse files Browse the repository at this point in the history
  • Loading branch information
jf2048 committed Mar 10, 2022
1 parent 16a07a4 commit 5cbef4c
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 62 deletions.
2 changes: 1 addition & 1 deletion gio/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ impl<O: IsA<File>> FileExtManual for O {
+ 'static,
>,
> {
let etag = etag.map(glib::GString::from);
let etag = etag.map(|e| glib::GString::try_from(e).unwrap());
Box::pin(crate::GioFuture::new(
self,
move |obj, cancellable, send| {
Expand Down
102 changes: 50 additions & 52 deletions glib/src/gstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl GString {

impl Clone for GString {
fn clone(&self) -> GString {
self.as_str().into()
self.as_str().try_into().unwrap()
}
}

Expand Down Expand Up @@ -247,70 +247,68 @@ impl From<GString> for Box<str> {
}
}

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

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

impl<'a> From<&'a str> for GString {
impl TryFrom<&str> for GString {
type Error = std::ffi::NulError;
#[inline]
fn from(s: &'a str) -> Self {
// Allocates with the GLib allocator
unsafe {
// No check for valid UTF-8 here
let copy = ffi::g_malloc(s.len() + 1) as *mut c_char;
ptr::copy_nonoverlapping(s.as_ptr() as *const c_char, copy, s.len() + 1);
ptr::write(copy.add(s.len()), 0);

GString(Inner::Foreign {
ptr: ptr::NonNull::new_unchecked(copy),
len: s.len(),
})
}
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.to_owned().try_into()
}
}

impl From<Vec<u8>> for GString {
#[derive(thiserror::Error, Debug)]
pub enum GStringError {
#[error("invalid UTF-8")]
Utf8(#[from] std::str::Utf8Error),
#[error("interior nul bytes")]
Nul(#[from] std::ffi::NulError),
}

impl TryFrom<Vec<u8>> for GString {
type Error = GStringError;
#[inline]
fn from(s: Vec<u8>) -> Self {
fn try_from(s: Vec<u8>) -> Result<Self, Self::Error> {
// Moves the content of the Vec
// Also check if it's valid UTF-8
let cstring = CString::new(s).expect("CString::new failed");
cstring.into()
let cstring = CString::new(s)?;
Ok(cstring.try_into()?)
}
}

impl From<CString> for GString {
#[inline]
fn from(s: CString) -> Self {
impl TryFrom<CString> for GString {
type Error = std::str::Utf8Error;
fn try_from(s: CString) -> Result<Self, Self::Error> {
// Moves the content of the CString
// Also check if it's valid UTF-8
assert!(s.to_str().is_ok());
Self(Inner::Native(Some(s)))
s.to_str()?;
Ok(Self(Inner::Native(Some(s))))
}
}

impl<'a> From<&'a CStr> for GString {
impl TryFrom<&CStr> for GString {
type Error = std::str::Utf8Error;
#[inline]
fn from(c: &'a CStr) -> Self {
// Creates a copy with the GLib allocator
// Also check if it's valid UTF-8
c.to_str().unwrap().into()
fn try_from(c: &CStr) -> Result<Self, Self::Error> {
c.to_owned().try_into()
}
}

Expand Down Expand Up @@ -361,7 +359,7 @@ impl FromGlibPtrNone<*const u8> for GString {
assert!(!ptr.is_null());
let cstr = CStr::from_ptr(ptr as *const _);
// Also check if it's valid UTF-8
cstr.to_str().unwrap().into()
cstr.try_into().unwrap()
}
}

Expand Down Expand Up @@ -493,16 +491,16 @@ impl<'a> ToGlibPtr<'a, *mut i8> for GString {
impl<'a> FromGlibContainer<*const c_char, *const i8> for GString {
unsafe fn from_glib_none_num(ptr: *const i8, num: usize) -> Self {
if num == 0 || ptr.is_null() {
return Self::from("");
return Self::try_from("").unwrap();
}
let slice = slice::from_raw_parts(ptr as *const u8, num);
// Also check if it's valid UTF-8
std::str::from_utf8(slice).unwrap().into()
std::str::from_utf8(slice).unwrap().try_into().unwrap()
}

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

// Check if it's valid UTF-8
Expand All @@ -517,7 +515,7 @@ impl<'a> FromGlibContainer<*const c_char, *const i8> for GString {

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

// Check if it's valid UTF-8
Expand Down Expand Up @@ -596,7 +594,7 @@ unsafe impl<'a> crate::value::FromValue<'a> for GString {
type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>;

unsafe fn from_value(value: &'a crate::Value) -> Self {
Self::from(<&str>::from_value(value))
Self::try_from(<&str>::from_value(value)).unwrap()
}
}

Expand Down Expand Up @@ -686,15 +684,15 @@ mod tests {

#[test]
fn test_gstring_from_str() {
let gstring: GString = "foo".into();
let gstring: GString = "foo".try_into().unwrap();
assert_eq!(gstring.as_str(), "foo");
let foo: Box<str> = gstring.into();
assert_eq!(foo.as_ref(), "foo");
}

#[test]
fn test_string_from_gstring() {
let gstring = GString::from("foo");
let gstring = GString::try_from("foo").unwrap();
assert_eq!(gstring.as_str(), "foo");
let s = String::from(gstring);
assert_eq!(s, "foo");
Expand All @@ -703,7 +701,7 @@ mod tests {
#[test]
fn test_gstring_from_cstring() {
let cstr = CString::new("foo").unwrap();
let gstring = GString::from(cstr);
let gstring = GString::try_from(cstr).unwrap();
assert_eq!(gstring.as_str(), "foo");
let foo: Box<str> = gstring.into();
assert_eq!(foo.as_ref(), "foo");
Expand All @@ -712,7 +710,7 @@ mod tests {
#[test]
fn test_string_from_gstring_from_cstring() {
let cstr = CString::new("foo").unwrap();
let gstring = GString::from(cstr);
let gstring = GString::try_from(cstr).unwrap();
assert_eq!(gstring.as_str(), "foo");
let s = String::from(gstring);
assert_eq!(s, "foo");
Expand All @@ -721,7 +719,7 @@ mod tests {
#[test]
fn test_vec_u8_to_gstring() {
let v: &[u8] = b"foo";
let s: GString = Vec::from(v).into();
let s: GString = Vec::from(v).try_into().unwrap();
assert_eq!(s.as_str(), "foo");
}

Expand Down Expand Up @@ -754,11 +752,11 @@ mod tests {
fn test_hashmap() {
use std::collections::HashMap;

let gstring = GString::from("foo");
let gstring = GString::try_from("foo").unwrap();
assert_eq!(gstring.as_str(), "foo");
let mut h: HashMap<GString, i32> = HashMap::new();
h.insert(gstring, 42);
let gstring: GString = "foo".into();
let gstring: GString = "foo".try_into().unwrap();
assert!(h.contains_key(&gstring));
}
}
14 changes: 7 additions & 7 deletions glib/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,18 +336,18 @@ mod tests {
fn test_filename_from_uri() {
use crate::GString;
use std::path::PathBuf;
let uri: GString = "file:///foo/bar.txt".into();
let uri: GString = "file:///foo/bar.txt".try_into().unwrap();
if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) {
assert_eq!(filename, PathBuf::from(r"/foo/bar.txt"));
assert_eq!(hostname, None);
} else {
unreachable!();
}

let uri: GString = "file://host/foo/bar.txt".into();
let uri: GString = "file://host/foo/bar.txt".try_into().unwrap();
if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) {
assert_eq!(filename, PathBuf::from(r"/foo/bar.txt"));
assert_eq!(hostname, Some(GString::from("host")));
assert_eq!(hostname, Some(GString::try_from("host").unwrap()));
} else {
unreachable!();
}
Expand All @@ -358,19 +358,19 @@ mod tests {
use crate::GString;
assert_eq!(
crate::uri_parse_scheme("foo://bar"),
Some(GString::from("foo"))
Some(GString::try_from("foo").unwrap())
);
assert_eq!(crate::uri_parse_scheme("foo"), None);

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

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

assert_eq!(
crate::uri_unescape_segment(Some("/foo"), None, None),
Some(GString::from("/foo"))
Some(GString::try_from("/foo").unwrap())
);
assert_eq!(crate::uri_unescape_segment(Some("/foo%"), None, None), None);
}
Expand Down
10 changes: 8 additions & 2 deletions glib/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1152,13 +1152,19 @@ mod tests {
let v = vec!["123", "456"].to_value();
assert_eq!(
v.get::<Vec<GString>>(),
Ok(vec![GString::from("123"), GString::from("456")])
Ok(vec![
GString::try_from("123").unwrap(),
GString::try_from("456").unwrap()
])
);

let v = vec![String::from("123"), String::from("456")].to_value();
assert_eq!(
v.get::<Vec<GString>>(),
Ok(vec![GString::from("123"), GString::from("456")])
Ok(vec![
GString::try_from("123").unwrap(),
GString::try_from("456").unwrap()
])
);
}

Expand Down

0 comments on commit 5cbef4c

Please sign in to comment.