glib/
char.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::num::TryFromIntError;
4
5use libc::{c_char, c_uchar};
6
7use crate::translate::*;
8
9// rustdoc-stripper-ignore-next
10/// Wrapper for values where C functions expect a plain C `char`
11///
12/// Consider the following C function prototype from glib:
13///
14/// ```C
15/// void g_key_file_set_list_separator (GKeyFile *key_file, gchar separator);
16/// ```
17///
18/// This function plainly expects a byte as the `separator` argument.  However,
19/// having this function exposed to Rust as the following would be inconvenient:
20///
21/// ```ignore
22/// impl KeyFile {
23///     pub fn set_list_separator(&self, separator: libc:c_char) { }
24/// }
25/// ```
26///
27/// 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
28/// for `i8` on most system.
29///
30/// This `Char` type is a wrapper over an `libc::c_char`, so that we can pass it to Glib or C functions.
31/// The check for whether a Rust `char` (a Unicode scalar value) actually fits in a `libc::c_char` is
32/// done in the `new` function; see its documentation for details.
33///
34/// The inner `libc::c_char` (which is equivalent to `i8`) can be extracted with `.0`, or
35/// by calling `my_char.into_glib()`.
36///
37/// # Examples
38/// ```
39/// use glib::Char;
40/// use std::convert::TryFrom;
41///
42/// Char::from(b'a');
43/// Char::try_from('a').unwrap();
44/// assert!(Char::try_from('☔').is_err());
45/// ```
46///
47/// ```ignore
48/// extern "C" fn have_a_byte(b: libc::c_char);
49///
50/// have_a_byte(Char::from(b'a').into_glib());
51/// ```
52#[derive(Debug, Copy, Clone, Eq, PartialEq)]
53pub struct Char(pub c_char);
54
55impl TryFrom<char> for Char {
56    type Error = TryFromIntError;
57
58    #[allow(clippy::unnecessary_cast)]
59    fn try_from(c: char) -> Result<Char, Self::Error> {
60        Ok(Self(u8::try_from(u32::from(c))? as c_char))
61    }
62}
63
64impl From<Char> for char {
65    fn from(c: Char) -> char {
66        c.0 as u8 as char
67    }
68}
69
70impl From<u8> for Char {
71    #[allow(clippy::unnecessary_cast)]
72    fn from(c: u8) -> Char {
73        Char(c as c_char)
74    }
75}
76
77impl From<Char> for u8 {
78    #[allow(clippy::unnecessary_cast)]
79    fn from(c: Char) -> u8 {
80        c.0 as u8
81    }
82}
83
84#[doc(hidden)]
85impl FromGlib<c_char> for Char {
86    #[inline]
87    unsafe fn from_glib(value: c_char) -> Self {
88        Self(value)
89    }
90}
91
92#[doc(hidden)]
93impl IntoGlib for Char {
94    type GlibType = c_char;
95
96    #[inline]
97    fn into_glib(self) -> c_char {
98        self.0
99    }
100}
101
102// rustdoc-stripper-ignore-next
103/// Wrapper for values where C functions expect a plain C `unsigned char`
104///
105/// This `UChar` type is a wrapper over an `libc::c_uchar`, so that we can pass it to Glib or C functions.
106/// The check for whether a Rust `char` (a Unicode scalar value) actually fits in a `libc::c_uchar` is
107/// done in the `new` function; see its documentation for details.
108///
109/// The inner `libc::c_uchar` (which is equivalent to `u8`) can be extracted with `.0`, or
110/// by calling `my_char.into_glib()`.
111///
112/// # Examples
113/// ```
114/// use glib::UChar;
115/// use std::convert::TryFrom;
116///
117/// UChar::from(b'a');
118/// UChar::try_from('a').unwrap();
119/// assert!(UChar::try_from('☔').is_err());
120/// ```
121///
122/// ```ignore
123/// extern "C" fn have_a_byte(b: libc::c_uchar);
124///
125/// have_a_byte(UChar::from(b'a').into_glib());
126/// ```
127#[derive(Debug, Copy, Clone, Eq, PartialEq)]
128pub struct UChar(pub c_uchar);
129
130impl TryFrom<char> for UChar {
131    type Error = TryFromIntError;
132
133    #[allow(clippy::unnecessary_cast)]
134    fn try_from(c: char) -> Result<UChar, Self::Error> {
135        Ok(Self(u8::try_from(u32::from(c))? as c_uchar))
136    }
137}
138
139impl From<UChar> for char {
140    fn from(c: UChar) -> char {
141        c.0 as _
142    }
143}
144
145impl From<u8> for UChar {
146    #[allow(clippy::unnecessary_cast)]
147    fn from(c: u8) -> UChar {
148        UChar(c as _)
149    }
150}
151
152impl From<UChar> for u8 {
153    fn from(c: UChar) -> u8 {
154        c.0 as _
155    }
156}
157
158#[doc(hidden)]
159impl FromGlib<c_uchar> for UChar {
160    #[inline]
161    unsafe fn from_glib(value: c_uchar) -> Self {
162        Self(value)
163    }
164}
165
166#[doc(hidden)]
167impl IntoGlib for UChar {
168    type GlibType = c_uchar;
169
170    #[inline]
171    fn into_glib(self) -> c_uchar {
172        self.0
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    #[allow(clippy::unnecessary_cast)]
182    fn converts_single_byte_chars() {
183        assert_eq!(Char::try_from(0 as char), Ok(Char(0 as c_char)));
184        assert_eq!(UChar::try_from(0 as char), Ok(UChar(0 as c_uchar)));
185        assert_eq!(UChar::try_from(255 as char), Ok(UChar(255 as c_uchar)));
186        assert_eq!(UChar::try_from('ñ'), Ok(UChar(241 as c_uchar)));
187    }
188
189    #[test]
190    fn refuses_multibyte_chars() {
191        assert!(Char::try_from('☔').is_err()); // no umbrella for you
192        assert!(UChar::try_from('☔').is_err());
193    }
194
195    #[test]
196    #[allow(clippy::unnecessary_cast)]
197    fn into_i8() {
198        assert_eq!(Char::from(b'A').into_glib(), 65 as c_char);
199    }
200
201    #[test]
202    #[allow(clippy::unnecessary_cast)]
203    fn into_u8() {
204        assert_eq!(UChar::from(b'A').into_glib(), 65 as c_uchar);
205    }
206
207    #[test]
208    #[allow(clippy::unnecessary_cast)]
209    fn into_char() {
210        assert_eq!(char::from(Char(65 as c_char)), 'A');
211        assert_eq!('ñ', UChar(241 as c_uchar).into());
212    }
213
214    #[test]
215    #[allow(clippy::unnecessary_cast)]
216    fn convert_from_glib() {
217        assert_eq!(Char(65 as c_char), unsafe { from_glib(65 as c_char) });
218        assert_eq!(UChar(241 as c_uchar), unsafe { from_glib(241 as c_uchar) });
219    }
220}