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