glib/
string.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 glib_sys;
6use gobject_sys;
7use std::borrow;
8use std::cmp;
9use std::convert;
10use std::fmt;
11use std::hash;
12use std::ops;
13use std::ptr;
14use std::slice;
15use std::str;
16use translate::*;
17
18glib_wrapper! {
19    /// A mutable text buffer that grows automatically.
20    pub struct String(Boxed<glib_sys::GString>);
21
22    match fn {
23        copy => |ptr| gobject_sys::g_boxed_copy(glib_sys::g_gstring_get_type(), ptr as *mut _) as *mut glib_sys::GString,
24        free => |ptr| gobject_sys::g_boxed_free(glib_sys::g_gstring_get_type(), ptr as *mut _),
25        get_type => || glib_sys::g_gstring_get_type(),
26    }
27}
28
29unsafe impl Send for String {}
30unsafe impl Sync for String {}
31
32impl String {
33    pub fn new<T: AsRef<[u8]>>(data: T) -> String {
34        let bytes = data.as_ref();
35        unsafe {
36            from_glib_full(glib_sys::g_string_new_len(
37                bytes.as_ptr() as *const _,
38                bytes.len() as isize,
39            ))
40        }
41    }
42
43    pub fn append(&mut self, val: &str) -> &mut Self {
44        unsafe {
45            glib_sys::g_string_append_len(
46                self.to_glib_none_mut().0,
47                val.to_glib_none().0,
48                val.len() as isize,
49            );
50        }
51        self
52    }
53
54    pub fn insert(&mut self, pos: isize, val: &str) -> &mut Self {
55        unsafe {
56            glib_sys::g_string_insert_len(
57                self.to_glib_none_mut().0,
58                pos,
59                val.to_glib_none().0,
60                val.len() as isize,
61            );
62        }
63        self
64    }
65
66    pub fn overwrite(&mut self, pos: usize, val: &str) -> &mut Self {
67        unsafe {
68            glib_sys::g_string_overwrite_len(
69                self.to_glib_none_mut().0,
70                pos,
71                val.to_glib_none().0,
72                val.len() as isize,
73            );
74        }
75        self
76    }
77
78    pub fn prepend(&mut self, val: &str) -> &mut Self {
79        unsafe {
80            glib_sys::g_string_prepend_len(
81                self.to_glib_none_mut().0,
82                val.to_glib_none().0,
83                val.len() as isize,
84            );
85        }
86        self
87    }
88
89    pub fn truncate(&mut self, len: usize) -> &mut Self {
90        unsafe {
91            glib_sys::g_string_truncate(self.to_glib_none_mut().0, len);
92        }
93        self
94    }
95
96    /// Returns `&str` slice when contained data is valid UTF-8 string, or an error otherwise.
97    pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
98        str::from_utf8(self.as_ref())
99    }
100
101    /// Returns `Cow<str>` containing UTF-8 data. Invalid UTF-8 sequences are replaced with
102    /// replacement character.
103    pub fn to_string_lossy(&self) -> borrow::Cow<str> {
104        ::std::string::String::from_utf8_lossy(self.as_ref())
105    }
106}
107
108impl Default for String {
109    /// Creates a new empty string.
110    fn default() -> String {
111        unsafe { from_glib_full(glib_sys::g_string_new(ptr::null())) }
112    }
113}
114
115impl fmt::Debug for String {
116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117        write!(f, "{}", self.to_string_lossy())
118    }
119}
120
121impl fmt::Display for String {
122    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123        write!(f, "{}", self.to_string_lossy())
124    }
125}
126
127impl PartialEq for String {
128    fn eq(&self, other: &Self) -> bool {
129        unsafe {
130            from_glib(glib_sys::g_string_equal(
131                self.to_glib_none().0,
132                other.to_glib_none().0,
133            ))
134        }
135    }
136}
137
138impl Eq for String {}
139
140impl cmp::PartialOrd for String {
141    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
142        Some(self.cmp(other))
143    }
144}
145
146impl cmp::Ord for String {
147    fn cmp(&self, other: &Self) -> cmp::Ordering {
148        self.as_ref().cmp(other.as_ref())
149    }
150}
151
152impl hash::Hash for String {
153    fn hash<H>(&self, state: &mut H)
154    where
155        H: hash::Hasher,
156    {
157        hash::Hash::hash_slice(self.as_ref(), state)
158    }
159}
160
161impl convert::AsRef<[u8]> for String {
162    fn as_ref(&self) -> &[u8] {
163        let ptr: *const u8 = (*self.0).str as _;
164        let len: usize = (*self.0).len;
165        unsafe { slice::from_raw_parts(ptr, len) }
166    }
167}
168
169impl ops::Deref for String {
170    type Target = [u8];
171
172    fn deref(&self) -> &[u8] {
173        let ptr: *const u8 = (*self.0).str as _;
174        let len: usize = (*self.0).len;
175        unsafe { slice::from_raw_parts(ptr, len) }
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    #[test]
182    fn append() {
183        let mut s = ::String::new("");
184        s.append("Hello").append(" ").append("there!");
185        assert_eq!(&*s, b"Hello there!");
186    }
187
188    #[test]
189    fn insert() {
190        let mut s = ::String::new("foobaz");
191        s.insert(3, "bar");
192        assert_eq!(&*s, b"foobarbaz");
193    }
194
195    #[test]
196    fn overwrite() {
197        let mut s = ::String::new("abc");
198        s.overwrite(2, "de");
199        assert_eq!(&*s, b"abde");
200    }
201
202    #[test]
203    fn prepend() {
204        let mut s = ::String::new("456");
205        s.prepend("123");
206        assert_eq!(&*s, b"123456");
207    }
208
209    #[test]
210    fn truncate() {
211        let mut s = ::String::new("12345");
212        s.truncate(10);
213        assert_eq!(&*s, b"12345");
214        s.truncate(2);
215        assert_eq!(&*s, b"12");
216    }
217
218    #[test]
219    fn default() {
220        let s1: ::String = Default::default();
221        assert_eq!(&*s1, b"");
222    }
223
224    #[test]
225    fn display() {
226        let s: ::String = ::String::new("This is a string.");
227        assert_eq!(&format!("{}", s), "This is a string.");
228    }
229
230    #[test]
231    fn eq() {
232        let a1 = ::String::new("a");
233        let a2 = ::String::new("a");
234        let b = ::String::new("b");
235        assert_eq!(a1, a1);
236        assert_eq!(a1, a2);
237        assert_ne!(a1, b);
238        assert_ne!(a2, b);
239    }
240
241    #[test]
242    fn invalid_utf8() {
243        let s = ::String::new(b"Hello \xF0\x90\x80World");
244        assert!(s.to_str().is_err());
245        assert_eq!(s.to_string_lossy(), "Hello �World");
246    }
247}