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}