glib/
gstring.rs

1// Copyright 2018, The Gtk-rs Project Developers.
2// See the COPYRIGHT file at the top-level directory of this distribution.
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5use libc;
6use std::borrow::Borrow;
7use std::cmp::Ordering;
8use std::ffi::{CStr, CString, OsStr};
9use std::fmt;
10use std::hash;
11use std::ops::Deref;
12use std::os::raw::c_char;
13use std::ptr;
14use std::slice;
15use std::string::String;
16use translate::*;
17use types::{StaticType, Type};
18
19use glib_sys;
20use gobject_sys;
21use value::{FromValueOptional, SetValue, SetValueOptional, Value};
22
23#[derive(Debug)]
24pub enum GString {
25    ForeignOwned(Option<CString>),
26    Borrowed(*const c_char, usize),
27    Owned(*mut c_char, usize),
28}
29
30impl GString {
31    pub unsafe fn new(ptr: *mut c_char) -> Self {
32        assert!(!ptr.is_null());
33        GString::Owned(ptr, libc::strlen(ptr))
34    }
35
36    pub unsafe fn new_borrowed(ptr: *const c_char) -> Self {
37        assert!(!ptr.is_null());
38        GString::Borrowed(ptr, libc::strlen(ptr))
39    }
40
41    pub fn as_str(&self) -> &str {
42        let cstr = match self {
43            GString::Borrowed(ptr, length) => unsafe {
44                let bytes = slice::from_raw_parts(*ptr as *const u8, length + 1);
45                CStr::from_bytes_with_nul_unchecked(bytes)
46            },
47            GString::Owned(ptr, length) => unsafe {
48                let bytes = slice::from_raw_parts(*ptr as *const u8, length + 1);
49                CStr::from_bytes_with_nul_unchecked(bytes)
50            },
51            GString::ForeignOwned(cstring) => cstring
52                .as_ref()
53                .expect("ForeignOwned shouldn't be empty")
54                .as_c_str(),
55        };
56        cstr.to_str().unwrap()
57    }
58}
59
60impl Drop for GString {
61    fn drop(&mut self) {
62        if let GString::Owned(ptr, _len) = self {
63            unsafe {
64                glib_sys::g_free(*ptr as *mut _);
65            }
66        }
67    }
68}
69
70impl fmt::Display for GString {
71    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72        write!(f, "{}", self.as_str())
73    }
74}
75
76impl hash::Hash for GString {
77    fn hash<H: hash::Hasher>(&self, state: &mut H) {
78        let bytes = match self {
79            GString::Borrowed(ptr, length) => unsafe {
80                slice::from_raw_parts(*ptr as *const u8, length + 1)
81            },
82            GString::Owned(ptr, length) => unsafe {
83                slice::from_raw_parts(*ptr as *const u8, length + 1)
84            },
85            GString::ForeignOwned(cstring) => cstring
86                .as_ref()
87                .expect("ForeignOwned shouldn't be empty")
88                .as_bytes(),
89        };
90        state.write(bytes);
91    }
92}
93
94impl Borrow<str> for GString {
95    fn borrow(&self) -> &str {
96        self.as_str()
97    }
98}
99
100impl Ord for GString {
101    fn cmp(&self, other: &GString) -> Ordering {
102        self.as_str().cmp(other.as_str())
103    }
104}
105
106impl PartialOrd for GString {
107    fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
108        Some(self.cmp(other))
109    }
110}
111
112impl PartialEq for GString {
113    fn eq(&self, other: &GString) -> bool {
114        self.as_str() == other.as_str()
115    }
116}
117
118impl PartialEq<GString> for String {
119    fn eq(&self, other: &GString) -> bool {
120        self.as_str() == other.as_str()
121    }
122}
123
124impl PartialEq<str> for GString {
125    fn eq(&self, other: &str) -> bool {
126        self.as_str() == other
127    }
128}
129
130impl<'a> PartialEq<&'a str> for GString {
131    fn eq(&self, other: &&'a str) -> bool {
132        self.as_str() == *other
133    }
134}
135
136impl<'a> PartialEq<GString> for &'a str {
137    fn eq(&self, other: &GString) -> bool {
138        *self == other.as_str()
139    }
140}
141
142impl PartialEq<String> for GString {
143    fn eq(&self, other: &String) -> bool {
144        self.as_str() == other.as_str()
145    }
146}
147
148impl PartialEq<GString> for str {
149    fn eq(&self, other: &GString) -> bool {
150        self == other.as_str()
151    }
152}
153
154impl PartialOrd<GString> for String {
155    fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
156        Some(self.cmp(&String::from(other.as_str())))
157    }
158}
159
160impl PartialOrd<String> for GString {
161    fn partial_cmp(&self, other: &String) -> Option<Ordering> {
162        Some(self.as_str().cmp(other.as_str()))
163    }
164}
165
166impl PartialOrd<GString> for str {
167    fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
168        Some(self.cmp(&other))
169    }
170}
171
172impl PartialOrd<str> for GString {
173    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
174        Some(self.as_str().cmp(other))
175    }
176}
177
178impl Eq for GString {}
179
180impl AsRef<str> for GString {
181    fn as_ref(&self) -> &str {
182        self.as_str()
183    }
184}
185
186impl AsRef<OsStr> for GString {
187    fn as_ref(&self) -> &OsStr {
188        OsStr::new(self.as_str())
189    }
190}
191
192impl Deref for GString {
193    type Target = str;
194
195    fn deref(&self) -> &str {
196        self.as_str()
197    }
198}
199
200impl From<GString> for String {
201    #[inline]
202    fn from(mut s: GString) -> Self {
203        if let GString::ForeignOwned(ref mut cstring) = s {
204            if let Ok(s) = cstring
205                .take()
206                .expect("ForeignOwned shouldn't be empty")
207                .into_string()
208            {
209                return s;
210            }
211        }
212        String::from(s.as_str())
213    }
214}
215
216impl From<GString> for Box<str> {
217    #[inline]
218    fn from(s: GString) -> Self {
219        let st: String = s.into();
220        st.into_boxed_str()
221    }
222}
223
224impl From<String> for GString {
225    #[inline]
226    fn from(s: String) -> Self {
227        s.into_bytes().into()
228    }
229}
230
231impl From<Box<str>> for GString {
232    #[inline]
233    fn from(s: Box<str>) -> Self {
234        s.as_bytes().to_vec().into()
235    }
236}
237
238impl<'a> From<&'a str> for GString {
239    #[inline]
240    fn from(s: &'a str) -> Self {
241        s.as_bytes().to_vec().into()
242    }
243}
244
245impl From<Vec<u8>> for GString {
246    #[inline]
247    fn from(s: Vec<u8>) -> Self {
248        let cstring = CString::new(s).expect("CString::new failed");
249        cstring.into()
250    }
251}
252
253impl From<CString> for GString {
254    #[inline]
255    fn from(s: CString) -> Self {
256        GString::ForeignOwned(Some(s))
257    }
258}
259
260impl<'a> From<&'a CStr> for GString {
261    #[inline]
262    fn from(c: &'a CStr) -> Self {
263        CString::from(c).into()
264    }
265}
266
267#[doc(hidden)]
268impl FromGlibPtrFull<*const c_char> for GString {
269    #[inline]
270    unsafe fn from_glib_full(ptr: *const c_char) -> Self {
271        GString::new(ptr as *mut _)
272    }
273}
274
275#[doc(hidden)]
276impl FromGlibPtrFull<*mut u8> for GString {
277    #[inline]
278    unsafe fn from_glib_full(ptr: *mut u8) -> Self {
279        GString::new(ptr as *mut _)
280    }
281}
282
283#[doc(hidden)]
284impl FromGlibPtrFull<*mut i8> for GString {
285    #[inline]
286    unsafe fn from_glib_full(ptr: *mut i8) -> Self {
287        GString::new(ptr as *mut _)
288    }
289}
290
291#[doc(hidden)]
292impl FromGlibPtrNone<*const c_char> for GString {
293    #[inline]
294    unsafe fn from_glib_none(ptr: *const c_char) -> Self {
295        let cstr = CStr::from_ptr(ptr);
296        cstr.into()
297    }
298}
299
300#[doc(hidden)]
301impl FromGlibPtrNone<*mut u8> for GString {
302    #[inline]
303    unsafe fn from_glib_none(ptr: *mut u8) -> Self {
304        let cstr = CStr::from_ptr(ptr as *mut _);
305        cstr.into()
306    }
307}
308
309#[doc(hidden)]
310impl FromGlibPtrNone<*mut i8> for GString {
311    #[inline]
312    unsafe fn from_glib_none(ptr: *mut i8) -> Self {
313        let cstr = CStr::from_ptr(ptr as *mut _);
314        cstr.into()
315    }
316}
317
318#[doc(hidden)]
319impl FromGlibPtrBorrow<*const c_char> for GString {
320    #[inline]
321    unsafe fn from_glib_borrow(ptr: *const c_char) -> Self {
322        GString::new_borrowed(ptr)
323    }
324}
325
326#[doc(hidden)]
327impl FromGlibPtrBorrow<*mut u8> for GString {
328    #[inline]
329    unsafe fn from_glib_borrow(ptr: *mut u8) -> Self {
330        GString::new_borrowed(ptr as *const c_char)
331    }
332}
333
334#[doc(hidden)]
335impl FromGlibPtrBorrow<*mut i8> for GString {
336    #[inline]
337    unsafe fn from_glib_borrow(ptr: *mut i8) -> Self {
338        GString::new_borrowed(ptr as *const c_char)
339    }
340}
341
342#[doc(hidden)]
343impl<'a> ToGlibPtr<'a, *const c_char> for GString {
344    type Storage = &'a Self;
345
346    #[inline]
347    fn to_glib_none(&'a self) -> Stash<'a, *const c_char, Self> {
348        Stash(self.as_ptr() as *const _, self)
349    }
350
351    #[inline]
352    fn to_glib_full(&self) -> *const c_char {
353        unsafe {
354            glib_sys::g_strndup(self.as_ptr() as *const c_char, self.len() as libc::size_t)
355                as *const c_char
356        }
357    }
358}
359
360#[doc(hidden)]
361impl<'a> ToGlibPtr<'a, *mut c_char> for GString {
362    type Storage = &'a Self;
363
364    #[inline]
365    fn to_glib_none(&'a self) -> Stash<'a, *mut c_char, Self> {
366        Stash(self.as_ptr() as *mut _, self)
367    }
368
369    #[inline]
370    fn to_glib_full(&self) -> *mut c_char {
371        unsafe {
372            glib_sys::g_strndup(self.as_ptr() as *const c_char, self.len() as libc::size_t)
373                as *mut c_char
374        }
375    }
376}
377
378impl GlibPtrDefault for GString {
379    type GlibType = *const c_char;
380}
381
382impl StaticType for GString {
383    fn static_type() -> Type {
384        String::static_type()
385    }
386}
387
388impl StaticType for Vec<GString> {
389    fn static_type() -> Type {
390        unsafe { from_glib(glib_sys::g_strv_get_type()) }
391    }
392}
393
394impl<'a> FromValueOptional<'a> for GString {
395    unsafe fn from_value_optional(value: &'a Value) -> Option<Self> {
396        let val = value.to_glib_none().0;
397        if val.is_null() {
398            None
399        } else {
400            let ptr = gobject_sys::g_value_dup_string(val);
401            Some(GString::new(ptr))
402        }
403    }
404}
405
406impl SetValue for GString {
407    unsafe fn set_value(value: &mut Value, this: &Self) {
408        gobject_sys::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
409    }
410}
411
412impl SetValueOptional for GString {
413    unsafe fn set_value_optional(value: &mut Value, this: Option<&Self>) {
414        gobject_sys::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
415    }
416}
417
418impl_from_glib_container_as_vec_string!(GString, *const c_char);
419impl_from_glib_container_as_vec_string!(GString, *mut c_char);
420
421#[cfg(test)]
422mod tests {
423    use glib_sys;
424    use gstring::GString;
425    use std::ffi::CString;
426
427    #[test]
428    fn test_gstring() {
429        let data = CString::new("foo").unwrap();
430        let ptr = data.into_raw();
431
432        unsafe {
433            let ptr_copy = glib_sys::g_strdup(ptr);
434            let gstring = GString::new(ptr_copy);
435            assert_eq!(gstring.as_str(), "foo");
436            let foo: Box<str> = gstring.into();
437            assert_eq!(foo.as_ref(), "foo");
438        }
439    }
440
441    #[test]
442    fn test_owned_glib_string() {
443        let data = CString::new("foo").unwrap();
444        let ptr = data.into_raw();
445        unsafe {
446            let ptr_copy = glib_sys::g_strdup(ptr);
447            let gstr = GString::new(ptr_copy);
448            assert_eq!(gstr, "foo");
449        }
450    }
451
452    #[test]
453    fn test_gstring_from_str() {
454        let gstring: GString = "foo".into();
455        assert_eq!(gstring.as_str(), "foo");
456        let foo: Box<str> = gstring.into();
457        assert_eq!(foo.as_ref(), "foo");
458    }
459
460    #[test]
461    fn test_gstring_from_cstring() {
462        let cstr = CString::new("foo").unwrap();
463        let gstring = GString::from(cstr);
464        assert_eq!(gstring.as_str(), "foo");
465        let foo: Box<str> = gstring.into();
466        assert_eq!(foo.as_ref(), "foo");
467    }
468
469    #[test]
470    fn test_string_from_gstring() {
471        let cstr = CString::new("foo").unwrap();
472        let gstring = GString::from(cstr);
473        assert_eq!(gstring.as_str(), "foo");
474        let s = String::from(gstring);
475        assert_eq!(s, "foo");
476    }
477
478    #[test]
479    fn test_vec_u8_to_gstring() {
480        let v = "foo".as_bytes();
481        let s: GString = Vec::from(v).into();
482        assert_eq!(s.as_str(), "foo");
483    }
484
485    #[test]
486    fn test_hashmap() {
487        use std::collections::HashMap;
488
489        let cstr = CString::new("foo").unwrap();
490        let gstring = GString::from(cstr);
491        assert_eq!(gstring.as_str(), "foo");
492        let mut h: HashMap<GString, i32> = HashMap::new();
493        h.insert(gstring, 42);
494        let gstring: GString = "foo".into();
495        assert!(h.contains_key(&gstring));
496    }
497}