1 use libc::{c_char, c_uchar};
2 use translate::FromGlib;
3 use 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)]
32 pub struct Char(pub c_char);
33 
34 impl 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     /// ```
new(c: char) -> Option<Char>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 
57 impl From<Char> for char {
from(c: Char) -> char58     fn from(c: Char) -> char {
59         c.0 as u8 as char
60     }
61 }
62 
63 #[doc(hidden)]
64 impl FromGlib<c_char> for Char {
from_glib(value: c_char) -> Self65     fn from_glib(value: c_char) -> Self {
66         Char(value)
67     }
68 }
69 
70 #[doc(hidden)]
71 impl ToGlib for Char {
72     type GlibType = c_char;
73 
to_glib(&self) -> c_char74     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)]
88 pub struct UChar(pub c_uchar);
89 
90 impl 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     /// ```
new(c: char) -> Option<UChar>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 
113 impl From<UChar> for char {
from(c: UChar) -> char114     fn from(c: UChar) -> char {
115         c.0 as char
116     }
117 }
118 
119 #[doc(hidden)]
120 impl FromGlib<c_uchar> for UChar {
from_glib(value: c_uchar) -> Self121     fn from_glib(value: c_uchar) -> Self {
122         UChar(value)
123     }
124 }
125 
126 #[doc(hidden)]
127 impl ToGlib for UChar {
128     type GlibType = c_uchar;
129 
to_glib(&self) -> c_uchar130     fn to_glib(&self) -> c_uchar {
131         self.0
132     }
133 }
134 
135 #[cfg(test)]
136 mod tests {
137     use super::*;
138     use translate::from_glib;
139 
140     #[test]
converts_single_byte_chars()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]
refuses_multibyte_chars()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]
into_i8()157     fn into_i8() {
158         assert_eq!(Char::new('A').unwrap().to_glib(), 65 as c_char);
159     }
160 
161     #[test]
into_u8()162     fn into_u8() {
163         assert_eq!(UChar::new('A').unwrap().to_glib(), 65 as c_uchar);
164     }
165 
166     #[test]
into_char()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]
convert_from_glib()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 }
178