1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{convert::TryFrom, num::TryFromIntError}; |
4 | |
5 | use libc::{c_char, c_uchar}; |
6 | |
7 | use 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)] |
53 | pub struct Char(pub c_char); |
54 | |
55 | impl 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 | |
64 | impl From<Char> for char { |
65 | fn from(c: Char) -> char { |
66 | c.0 as u8 as char |
67 | } |
68 | } |
69 | |
70 | impl From<u8> for Char { |
71 | #[allow (clippy::unnecessary_cast)] |
72 | fn from(c: u8) -> Char { |
73 | Char(c as c_char) |
74 | } |
75 | } |
76 | |
77 | impl 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)] |
85 | impl 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)] |
93 | impl 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)] |
128 | pub struct UChar(pub c_uchar); |
129 | |
130 | impl 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 | |
139 | impl From<UChar> for char { |
140 | fn from(c: UChar) -> char { |
141 | c.0 as _ |
142 | } |
143 | } |
144 | |
145 | impl From<u8> for UChar { |
146 | #[allow (clippy::unnecessary_cast)] |
147 | fn from(c: u8) -> UChar { |
148 | UChar(c as _) |
149 | } |
150 | } |
151 | |
152 | impl From<UChar> for u8 { |
153 | fn from(c: UChar) -> u8 { |
154 | c.0 as _ |
155 | } |
156 | } |
157 | |
158 | #[doc (hidden)] |
159 | impl 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)] |
167 | impl 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)] |
177 | mod 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 | } |
221 | |