glib/char.rs
1use libc::{c_char, c_uchar};
2use translate::FromGlib;
3use translate::ToGlib;
4
5/// Wrapper for values where C functions expect a plain C `char`
6///
7/// Consider the following C function prototype from glib:
8///
9/// ```C
10/// void g_key_file_set_list_separator (GKeyFile *key_file, gchar separator);
11/// ```
12///
13/// This function plainly expects a byte as the `separator` argument. However,
14/// having this function exposed to Rust as the following would be inconvenient:
15///
16/// ```ignore
17/// impl KeyFile {
18/// pub fn set_list_separator(&self, separator: libc:c_char) { }
19/// }
20/// ```
21///
22/// This would be inconvenient because users would have to do the conversion from a Rust `char` to an `libc::c_char` by hand, which is just a type alias
23/// for `i8` on most system.
24///
25/// This `Char` type is a wrapper over an `libc::c_char`, so that we can pass it to Glib or C functions.
26/// The check for whether a Rust `char` (a Unicode scalar value) actually fits in a `libc::c_char` is
27/// done in the `new` function; see its documentation for details.
28///
29/// The inner `libc::c_char` (which is equivalent to `i8` can be extracted with `.0`, or
30/// by calling `my_char.to_glib()`.
31#[derive(Debug, Copy, Clone, Eq, PartialEq)]
32pub struct Char(pub c_char);
33
34impl Char {
35 /// Creates a `Some(Char)` if the given `char` is representable as an `libc::c_char`
36 ///
37 /// # Example
38 /// ```ignore
39 /// extern "C" fn have_a_byte(b: libc::c_char);
40 ///
41 /// let a = Char::new('a').unwrap();
42 /// assert!(a.0 == 65);
43 /// have_a_byte(a.to_glib());
44 ///
45 /// let not_representable = Char::new('☔');
46 /// assert!(not_representable.is_none());
47 /// ```
48 pub fn new(c: char) -> Option<Char> {
49 if c as u32 > 255 {
50 None
51 } else {
52 Some(Char(c as c_char))
53 }
54 }
55}
56
57impl From<Char> for char {
58 fn from(c: Char) -> char {
59 c.0 as u8 as char
60 }
61}
62
63#[doc(hidden)]
64impl FromGlib<c_char> for Char {
65 fn from_glib(value: c_char) -> Self {
66 Char(value)
67 }
68}
69
70#[doc(hidden)]
71impl ToGlib for Char {
72 type GlibType = c_char;
73
74 fn to_glib(&self) -> c_char {
75 self.0
76 }
77}
78
79/// Wrapper for values where C functions expect a plain C `unsigned char`
80///
81/// This `UChar` type is a wrapper over an `libc::c_uchar`, so that we can pass it to Glib or C functions.
82/// The check for whether a Rust `char` (a Unicode scalar value) actually fits in a `libc::c_uchar` is
83/// done in the `new` function; see its documentation for details.
84///
85/// The inner `libc::c_uchar` (which is equivalent to `u8` can be extracted with `.0`, or
86/// by calling `my_char.to_glib()`.
87#[derive(Debug, Copy, Clone, Eq, PartialEq)]
88pub struct UChar(pub c_uchar);
89
90impl UChar {
91 /// Creates a `Some(UChar)` if the given `char` is representable as an `libc::c_uchar`
92 ///
93 /// # Example
94 /// ```ignore
95 /// extern "C" fn have_a_byte(b: libc::c_uchar);
96 ///
97 /// let a = Char::new('a').unwrap();
98 /// assert!(a.0 == 65);
99 /// have_a_byte(a.to_glib());
100 ///
101 /// let not_representable = Char::new('☔');
102 /// assert!(not_representable.is_none());
103 /// ```
104 pub fn new(c: char) -> Option<UChar> {
105 if c as u32 > 255 {
106 None
107 } else {
108 Some(UChar(c as c_uchar))
109 }
110 }
111}
112
113impl From<UChar> for char {
114 fn from(c: UChar) -> char {
115 c.0 as char
116 }
117}
118
119#[doc(hidden)]
120impl FromGlib<c_uchar> for UChar {
121 fn from_glib(value: c_uchar) -> Self {
122 UChar(value)
123 }
124}
125
126#[doc(hidden)]
127impl ToGlib for UChar {
128 type GlibType = c_uchar;
129
130 fn to_glib(&self) -> c_uchar {
131 self.0
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use translate::from_glib;
139
140 #[test]
141 fn converts_single_byte_chars() {
142 assert_eq!(Char::new(0 as char), Some(Char(0 as c_char)));
143 assert_eq!(Char::new(255 as char), Some(Char(-1 as i8 as c_char)));
144 assert_eq!(Char::new('ñ'), Some(Char(241 as u8 as c_char)));
145 assert_eq!(UChar::new(0 as char), Some(UChar(0 as c_uchar)));
146 assert_eq!(UChar::new(255 as char), Some(UChar(255 as c_uchar)));
147 assert_eq!(UChar::new('ñ'), Some(UChar(241 as c_uchar)));
148 }
149
150 #[test]
151 fn refuses_multibyte_chars() {
152 assert_eq!(Char::new('☔'), None); // no umbrella for you
153 assert_eq!(UChar::new('☔'), None);
154 }
155
156 #[test]
157 fn into_i8() {
158 assert_eq!(Char::new('A').unwrap().to_glib(), 65 as c_char);
159 }
160
161 #[test]
162 fn into_u8() {
163 assert_eq!(UChar::new('A').unwrap().to_glib(), 65 as c_uchar);
164 }
165
166 #[test]
167 fn into_char() {
168 assert_eq!(char::from(Char(65 as c_char)), 'A');
169 assert_eq!('ñ', UChar(241 as c_uchar).into());
170 }
171
172 #[test]
173 fn convert_from_glib() {
174 assert_eq!(Char(65 as c_char), from_glib(65 as c_char));
175 assert_eq!(UChar(241 as c_uchar), from_glib(241 as u8 as c_uchar));
176 }
177}