| 1 | // Take a look at the license at the top of the repository in the LICENSE file. |
| 2 | |
| 3 | use std::{ |
| 4 | borrow::{Borrow, Cow}, |
| 5 | cmp::Ordering, |
| 6 | ffi::{CStr, CString, OsStr, OsString}, |
| 7 | fmt, hash, |
| 8 | marker::PhantomData, |
| 9 | mem, |
| 10 | ops::Deref, |
| 11 | os::raw::{c_char, c_void}, |
| 12 | path::{Path, PathBuf}, |
| 13 | ptr, slice, |
| 14 | }; |
| 15 | |
| 16 | use crate::{ffi, gobject_ffi, prelude::*, translate::*, Type, Value}; |
| 17 | |
| 18 | // rustdoc-stripper-ignore-next |
| 19 | /// Representation of a borrowed [`GString`]. |
| 20 | /// |
| 21 | /// This type is very similar to [`std::ffi::CStr`], but with one added constraint: the string |
| 22 | /// must also be valid UTF-8. |
| 23 | #[repr (transparent)] |
| 24 | pub struct GStr(str); |
| 25 | |
| 26 | impl GStr { |
| 27 | // rustdoc-stripper-ignore-next |
| 28 | /// Creates a GLib string wrapper from a byte slice. |
| 29 | /// |
| 30 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring that the byte |
| 31 | /// slice is valid UTF-8 and is nul-terminated. |
| 32 | #[inline ] |
| 33 | pub fn from_utf8_with_nul(bytes: &[u8]) -> Result<&Self, GStrError> { |
| 34 | Self::check_trailing_nul(bytes)?; |
| 35 | std::str::from_utf8(bytes)?; |
| 36 | Ok(unsafe { mem::transmute::<&[u8], &GStr>(bytes) }) |
| 37 | } |
| 38 | // rustdoc-stripper-ignore-next |
| 39 | /// Creates a GLib string wrapper from a byte slice, checking for interior nul-bytes. |
| 40 | /// |
| 41 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring that the byte |
| 42 | /// slice is valid UTF-8, is nul-terminated, and does not contain any interior nul-bytes. |
| 43 | #[inline ] |
| 44 | pub fn from_utf8_with_nul_checked(bytes: &[u8]) -> Result<&Self, GStrError> { |
| 45 | Self::check_nuls(bytes)?; |
| 46 | std::str::from_utf8(bytes)?; |
| 47 | Ok(unsafe { mem::transmute::<&[u8], &GStr>(bytes) }) |
| 48 | } |
| 49 | // rustdoc-stripper-ignore-next |
| 50 | /// Unsafely creates a GLib string wrapper from a byte slice. |
| 51 | /// |
| 52 | /// This function will cast the provided `bytes` to a `GStr` wrapper without performing any |
| 53 | /// sanity checks. |
| 54 | /// |
| 55 | /// # Safety |
| 56 | /// |
| 57 | /// The provided slice **must** be valid UTF-8 and nul-terminated. It is undefined behavior to |
| 58 | /// pass a slice that does not uphold those conditions. |
| 59 | #[inline ] |
| 60 | pub const unsafe fn from_utf8_with_nul_unchecked(bytes: &[u8]) -> &Self { |
| 61 | debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0); |
| 62 | debug_assert!(std::str::from_utf8(bytes).is_ok()); |
| 63 | mem::transmute::<&[u8], &GStr>(bytes) |
| 64 | } |
| 65 | // rustdoc-stripper-ignore-next |
| 66 | /// Creates a GLib string wrapper from a byte slice, truncating it at the first nul-byte. |
| 67 | /// |
| 68 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring that the byte |
| 69 | /// slice is valid UTF-8 and contains at least one nul-byte. |
| 70 | #[inline ] |
| 71 | pub fn from_utf8_until_nul(bytes: &[u8]) -> Result<&Self, GStrError> { |
| 72 | let nul_pos = memchr::memchr(0, bytes).ok_or(GStrError::NoTrailingNul)?; |
| 73 | let bytes = unsafe { bytes.get_unchecked(..nul_pos + 1) }; |
| 74 | std::str::from_utf8(bytes)?; |
| 75 | Ok(unsafe { mem::transmute::<&[u8], &GStr>(bytes) }) |
| 76 | } |
| 77 | // rustdoc-stripper-ignore-next |
| 78 | /// Creates a GLib string wrapper from a string slice. |
| 79 | /// |
| 80 | /// The string slice must be terminated with a nul-byte. |
| 81 | /// |
| 82 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring |
| 83 | /// that the string slice is nul-terminated. |
| 84 | #[inline ] |
| 85 | pub fn from_str_with_nul(s: &str) -> Result<&Self, GStrError> { |
| 86 | Self::check_trailing_nul(s)?; |
| 87 | Ok(unsafe { mem::transmute::<&str, &GStr>(s) }) |
| 88 | } |
| 89 | // rustdoc-stripper-ignore-next |
| 90 | /// Creates a GLib string wrapper from a string slice, checking for interior nul-bytes. |
| 91 | /// |
| 92 | /// The string slice must be terminated with a nul-byte. |
| 93 | /// |
| 94 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring |
| 95 | /// that the string slice is nul-terminated and does not contain any interior nul-bytes. |
| 96 | #[inline ] |
| 97 | pub fn from_str_with_nul_checked(s: &str) -> Result<&Self, GStrError> { |
| 98 | Self::check_nuls(s)?; |
| 99 | Ok(unsafe { mem::transmute::<&str, &GStr>(s) }) |
| 100 | } |
| 101 | // rustdoc-stripper-ignore-next |
| 102 | /// Unsafely creates a GLib string wrapper from a string slice. The string slice must be |
| 103 | /// terminated with a nul-byte. |
| 104 | /// |
| 105 | /// This function will cast the provided string slice to a `GStr` without performing any sanity |
| 106 | /// checks. |
| 107 | /// |
| 108 | /// # Safety |
| 109 | /// |
| 110 | /// The provided string slice **must** be nul-terminated. It is undefined behavior to pass a |
| 111 | /// slice that does not uphold those conditions. |
| 112 | #[inline ] |
| 113 | pub const unsafe fn from_str_with_nul_unchecked(s: &str) -> &Self { |
| 114 | debug_assert!(!s.is_empty() && s.as_bytes()[s.len() - 1] == 0); |
| 115 | mem::transmute::<&str, &GStr>(s) |
| 116 | } |
| 117 | // rustdoc-stripper-ignore-next |
| 118 | /// Creates a GLib string wrapper from a string slice, truncating it at the first nul-byte. |
| 119 | /// |
| 120 | /// The string slice must contain at least one nul-byte. |
| 121 | /// |
| 122 | /// This function will cast the provided bytes to a `GStr` wrapper after ensuring |
| 123 | /// that the string slice contains at least one nul-byte. |
| 124 | #[inline ] |
| 125 | pub fn from_str_until_nul(s: &str) -> Result<&Self, GStrError> { |
| 126 | let b = s.as_bytes(); |
| 127 | let nul_pos = memchr::memchr(0, b).ok_or(GStrError::NoTrailingNul)?; |
| 128 | let s = unsafe { std::str::from_utf8_unchecked(b.get_unchecked(..nul_pos + 1)) }; |
| 129 | Ok(unsafe { mem::transmute::<&str, &GStr>(s) }) |
| 130 | } |
| 131 | // rustdoc-stripper-ignore-next |
| 132 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
| 133 | /// valid UTF-8 and nul-terminated. All constraints from [`CStr::from_ptr`] also apply here. |
| 134 | /// |
| 135 | /// # Safety |
| 136 | /// |
| 137 | /// See [`CStr::from_ptr`](std::ffi::CStr#safety). |
| 138 | #[inline ] |
| 139 | pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self { |
| 140 | let cstr = CStr::from_ptr(ptr); |
| 141 | Self::from_utf8_with_nul_unchecked(cstr.to_bytes_with_nul()) |
| 142 | } |
| 143 | // rustdoc-stripper-ignore-next |
| 144 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
| 145 | /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here. |
| 146 | /// |
| 147 | /// If the string is valid UTF-8 then it is directly returned, otherwise `None` is returned. |
| 148 | #[inline ] |
| 149 | pub unsafe fn from_ptr_checked<'a>(ptr: *const c_char) -> Option<&'a Self> { |
| 150 | let mut end_ptr = ptr::null(); |
| 151 | if ffi::g_utf8_validate(ptr as *const _, -1, &mut end_ptr) != ffi::GFALSE { |
| 152 | Some(Self::from_utf8_with_nul_unchecked(slice::from_raw_parts( |
| 153 | ptr as *const u8, |
| 154 | end_ptr.offset_from(ptr) as usize + 1, |
| 155 | ))) |
| 156 | } else { |
| 157 | None |
| 158 | } |
| 159 | } |
| 160 | // rustdoc-stripper-ignore-next |
| 161 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
| 162 | /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here. |
| 163 | /// |
| 164 | /// If the string is valid UTF-8 then it is directly returned otherwise a copy is created with |
| 165 | /// every invalid character replaced by the Unicode replacement character (U+FFFD). |
| 166 | #[inline ] |
| 167 | pub unsafe fn from_ptr_lossy<'a>(ptr: *const c_char) -> Cow<'a, Self> { |
| 168 | if let Some(gs) = Self::from_ptr_checked(ptr) { |
| 169 | Cow::Borrowed(gs) |
| 170 | } else { |
| 171 | Cow::Owned(GString::from_glib_full(ffi::g_utf8_make_valid( |
| 172 | ptr as *const _, |
| 173 | -1, |
| 174 | ))) |
| 175 | } |
| 176 | } |
| 177 | // rustdoc-stripper-ignore-next |
| 178 | /// Converts this GLib string to a byte slice containing the trailing 0 byte. |
| 179 | /// |
| 180 | /// This function is the equivalent of [`GStr::as_bytes`] except that it will retain the |
| 181 | /// trailing nul terminator instead of chopping it off. |
| 182 | #[inline ] |
| 183 | pub const fn as_bytes_with_nul(&self) -> &[u8] { |
| 184 | self.0.as_bytes() |
| 185 | } |
| 186 | // rustdoc-stripper-ignore-next |
| 187 | /// Converts this GLib string to a byte slice. |
| 188 | /// |
| 189 | /// The returned slice will **not** contain the trailing nul terminator that this GLib |
| 190 | /// string has. |
| 191 | #[inline ] |
| 192 | pub const fn as_bytes(&self) -> &[u8] { |
| 193 | self.as_str().as_bytes() |
| 194 | } |
| 195 | // rustdoc-stripper-ignore-next |
| 196 | /// Returns the inner pointer to this GLib string. |
| 197 | /// |
| 198 | /// The returned pointer will be valid for as long as `self` is, and points to a contiguous |
| 199 | /// region of memory terminated with a 0 byte to represent the end of the string. |
| 200 | /// |
| 201 | /// **WARNING** |
| 202 | /// |
| 203 | /// The returned pointer is read-only; writing to it (including passing it to C code that |
| 204 | /// writes to it) causes undefined behavior. It is your responsibility to make |
| 205 | /// sure that the underlying memory is not freed too early. |
| 206 | #[inline ] |
| 207 | pub const fn as_ptr(&self) -> *const c_char { |
| 208 | self.0.as_ptr() as *const _ |
| 209 | } |
| 210 | // rustdoc-stripper-ignore-next |
| 211 | /// Converts this GLib string to a string slice. |
| 212 | #[inline ] |
| 213 | pub const fn as_str(&self) -> &str { |
| 214 | // Clip off the nul-byte |
| 215 | unsafe { |
| 216 | std::str::from_utf8_unchecked(std::slice::from_raw_parts( |
| 217 | self.as_ptr() as *const _, |
| 218 | self.0.len() - 1, |
| 219 | )) |
| 220 | } |
| 221 | } |
| 222 | // rustdoc-stripper-ignore-next |
| 223 | /// Converts this GLib string to a C string slice, checking for interior nul-bytes. |
| 224 | /// |
| 225 | /// Returns `Err` if the string contains any interior nul-bytes. |
| 226 | #[inline ] |
| 227 | pub fn to_cstr(&self) -> Result<&CStr, GStrInteriorNulError> { |
| 228 | Self::check_interior_nuls(self.as_bytes())?; |
| 229 | Ok(unsafe { self.to_cstr_unchecked() }) |
| 230 | } |
| 231 | // rustdoc-stripper-ignore-next |
| 232 | /// Converts this GLib string to a C string slice, truncating it at the first nul-byte. |
| 233 | #[inline ] |
| 234 | pub fn to_cstr_until_nul(&self) -> &CStr { |
| 235 | let b = self.as_bytes_with_nul(); |
| 236 | let nul_pos = memchr::memchr(0, b).unwrap(); |
| 237 | unsafe { CStr::from_bytes_with_nul_unchecked(b.get_unchecked(..nul_pos + 1)) } |
| 238 | } |
| 239 | // rustdoc-stripper-ignore-next |
| 240 | /// Converts this GLib string to a C string slice, without checking for interior nul-bytes. |
| 241 | /// |
| 242 | /// # Safety |
| 243 | /// |
| 244 | /// `self` **must** not contain any interior nul-bytes besides the final terminating nul-byte. |
| 245 | /// It is undefined behavior to call this on a string that contains interior nul-bytes. |
| 246 | #[inline ] |
| 247 | pub const unsafe fn to_cstr_unchecked(&self) -> &CStr { |
| 248 | CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) |
| 249 | } |
| 250 | |
| 251 | #[doc (alias = "g_utf8_collate" )] |
| 252 | #[doc (alias = "utf8_collate" )] |
| 253 | pub fn collate(&self, other: impl IntoGStr) -> Ordering { |
| 254 | other.run_with_gstr(|other| { |
| 255 | unsafe { ffi::g_utf8_collate(self.to_glib_none().0, other.to_glib_none().0) }.cmp(&0) |
| 256 | }) |
| 257 | } |
| 258 | |
| 259 | #[inline ] |
| 260 | fn check_nuls(s: impl AsRef<[u8]>) -> Result<(), GStrError> { |
| 261 | let s = s.as_ref(); |
| 262 | if let Some(nul_pos) = memchr::memchr(0, s) { |
| 263 | if s.len() == nul_pos + 1 { |
| 264 | Ok(()) |
| 265 | } else { |
| 266 | Err(GStrInteriorNulError(nul_pos).into()) |
| 267 | } |
| 268 | } else { |
| 269 | Err(GStrError::NoTrailingNul) |
| 270 | } |
| 271 | } |
| 272 | #[inline ] |
| 273 | fn check_trailing_nul(s: impl AsRef<[u8]>) -> Result<(), GStrError> { |
| 274 | if let Some(c) = s.as_ref().last().copied() { |
| 275 | if c == 0 { |
| 276 | return Ok(()); |
| 277 | } |
| 278 | } |
| 279 | Err(GStrError::NoTrailingNul) |
| 280 | } |
| 281 | // rustdoc-stripper-ignore-next |
| 282 | /// Returns `Err` if the string slice contains any nul-bytes. |
| 283 | #[inline ] |
| 284 | pub(crate) fn check_interior_nuls(s: impl AsRef<[u8]>) -> Result<(), GStrInteriorNulError> { |
| 285 | if let Some(nul_pos) = memchr::memchr(0, s.as_ref()) { |
| 286 | Err(GStrInteriorNulError(nul_pos)) |
| 287 | } else { |
| 288 | Ok(()) |
| 289 | } |
| 290 | } |
| 291 | pub const NONE: Option<&'static GStr> = None; |
| 292 | } |
| 293 | |
| 294 | // rustdoc-stripper-ignore-next |
| 295 | /// Error type holding all possible failures when creating a [`GStr`] reference. |
| 296 | #[derive (Debug)] |
| 297 | pub enum GStrError { |
| 298 | InvalidUtf8(std::str::Utf8Error), |
| 299 | InteriorNul(GStrInteriorNulError), |
| 300 | NoTrailingNul, |
| 301 | } |
| 302 | |
| 303 | impl std::error::Error for GStrError { |
| 304 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
| 305 | match self { |
| 306 | Self::InvalidUtf8(err: &Utf8Error) => std::error::Error::source(self:err), |
| 307 | Self::InteriorNul(err: &GStrInteriorNulError) => std::error::Error::source(self:err), |
| 308 | Self::NoTrailingNul => None, |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | impl fmt::Display for GStrError { |
| 314 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| 315 | match self { |
| 316 | Self::InvalidUtf8(err: &Utf8Error) => fmt::Display::fmt(self:err, f:fmt), |
| 317 | Self::InteriorNul(err: &GStrInteriorNulError) => fmt::Display::fmt(self:err, f:fmt), |
| 318 | Self::NoTrailingNul => fmt.write_str(data:"data provided is not nul terminated" ), |
| 319 | } |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | impl std::convert::From<std::str::Utf8Error> for GStrError { |
| 324 | fn from(err: std::str::Utf8Error) -> Self { |
| 325 | Self::InvalidUtf8(err) |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | impl std::convert::From<GStrInteriorNulError> for GStrError { |
| 330 | fn from(err: GStrInteriorNulError) -> Self { |
| 331 | Self::InteriorNul(err) |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | // rustdoc-stripper-ignore-next |
| 336 | /// Error type indicating that a buffer had unexpected nul-bytes. |
| 337 | #[derive (Copy, Clone, PartialEq, Eq, Debug)] |
| 338 | pub struct GStrInteriorNulError(usize); |
| 339 | |
| 340 | impl std::error::Error for GStrInteriorNulError {} |
| 341 | |
| 342 | impl fmt::Display for GStrInteriorNulError { |
| 343 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| 344 | write!( |
| 345 | fmt, |
| 346 | "data provided contains an interior nul-byte at byte pos {}" , |
| 347 | self.0 |
| 348 | ) |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | impl GStrInteriorNulError { |
| 353 | // rustdoc-stripper-ignore-next |
| 354 | /// Returns the position of the nul-byte in the slice that caused the conversion to fail. |
| 355 | #[inline ] |
| 356 | pub fn nul_position(&self) -> usize { |
| 357 | self.0 |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | // rustdoc-stripper-ignore-next |
| 362 | /// Converts a static string literal into a static nul-terminated string. |
| 363 | /// |
| 364 | /// The expanded expression has type [`&'static GStr`]. This macro will panic if the |
| 365 | /// string literal contains any interior nul-bytes. |
| 366 | /// |
| 367 | /// # Examples |
| 368 | /// |
| 369 | /// ``` |
| 370 | /// # fn main() { |
| 371 | /// use glib::{gstr, GStr, GString}; |
| 372 | /// |
| 373 | /// const MY_STRING: &GStr = gstr!("Hello" ); |
| 374 | /// assert_eq!(MY_STRING.as_bytes_with_nul()[5], 0u8); |
| 375 | /// let owned: GString = MY_STRING.to_owned(); |
| 376 | /// assert_eq!(MY_STRING, owned); |
| 377 | /// # } |
| 378 | /// ``` |
| 379 | /// |
| 380 | /// [`&'static GStr`]: crate::GStr |
| 381 | #[macro_export ] |
| 382 | macro_rules! gstr { |
| 383 | ($s:literal) => { |
| 384 | unsafe { $crate::GStr::from_utf8_with_nul_unchecked($crate::cstr_bytes!($s)) } |
| 385 | }; |
| 386 | } |
| 387 | |
| 388 | impl fmt::Debug for GStr { |
| 389 | #[inline ] |
| 390 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 391 | <&str as fmt::Debug>::fmt(&self.as_str(), f) |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | impl PartialEq for GStr { |
| 396 | #[inline ] |
| 397 | fn eq(&self, other: &Self) -> bool { |
| 398 | self.as_str().eq(other.as_str()) |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | impl Eq for GStr {} |
| 403 | |
| 404 | impl PartialOrd for GStr { |
| 405 | #[inline ] |
| 406 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
| 407 | Some(self.cmp(other)) |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | impl Ord for GStr { |
| 412 | #[inline ] |
| 413 | fn cmp(&self, other: &Self) -> Ordering { |
| 414 | self.as_str().cmp(other.as_str()) |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | impl hash::Hash for GStr { |
| 419 | #[inline ] |
| 420 | fn hash<H: hash::Hasher>(&self, state: &mut H) { |
| 421 | self.as_str().hash(state) |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | impl Default for &GStr { |
| 426 | #[inline ] |
| 427 | fn default() -> Self { |
| 428 | const SLICE: &[c_char] = &[0]; |
| 429 | unsafe { GStr::from_ptr(SLICE.as_ptr()) } |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | impl fmt::Display for GStr { |
| 434 | #[inline ] |
| 435 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 436 | f.write_str(self.as_str()) |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | impl<'a> TryFrom<&'a CStr> for &'a GStr { |
| 441 | type Error = std::str::Utf8Error; |
| 442 | #[inline ] |
| 443 | fn try_from(s: &'a CStr) -> Result<Self, Self::Error> { |
| 444 | s.to_str()?; |
| 445 | Ok(unsafe { GStr::from_utf8_with_nul_unchecked(bytes:s.to_bytes_with_nul()) }) |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | impl PartialEq<GStr> for String { |
| 450 | #[inline ] |
| 451 | fn eq(&self, other: &GStr) -> bool { |
| 452 | self.as_str() == other.as_str() |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | impl PartialEq<str> for GStr { |
| 457 | #[inline ] |
| 458 | fn eq(&self, other: &str) -> bool { |
| 459 | self.as_str() == other |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | impl PartialEq<&str> for GStr { |
| 464 | #[inline ] |
| 465 | fn eq(&self, other: &&str) -> bool { |
| 466 | self.as_str() == *other |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | impl PartialEq<GStr> for &str { |
| 471 | #[inline ] |
| 472 | fn eq(&self, other: &GStr) -> bool { |
| 473 | *self == other.as_str() |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | impl PartialEq<String> for GStr { |
| 478 | #[inline ] |
| 479 | fn eq(&self, other: &String) -> bool { |
| 480 | self.as_str() == other.as_str() |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | impl PartialEq<GStr> for str { |
| 485 | #[inline ] |
| 486 | fn eq(&self, other: &GStr) -> bool { |
| 487 | self == other.as_str() |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | impl PartialOrd<GStr> for String { |
| 492 | #[inline ] |
| 493 | fn partial_cmp(&self, other: &GStr) -> Option<Ordering> { |
| 494 | Some(self.cmp(&String::from(other.as_str()))) |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | impl PartialOrd<String> for GStr { |
| 499 | #[inline ] |
| 500 | fn partial_cmp(&self, other: &String) -> Option<Ordering> { |
| 501 | Some(self.as_str().cmp(other.as_str())) |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | impl PartialOrd<GStr> for str { |
| 506 | #[inline ] |
| 507 | fn partial_cmp(&self, other: &GStr) -> Option<Ordering> { |
| 508 | Some(self.cmp(other.as_str())) |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | impl PartialOrd<str> for GStr { |
| 513 | #[inline ] |
| 514 | fn partial_cmp(&self, other: &str) -> Option<Ordering> { |
| 515 | Some(self.as_str().cmp(other)) |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | impl AsRef<GStr> for GStr { |
| 520 | #[inline ] |
| 521 | fn as_ref(&self) -> &GStr { |
| 522 | self |
| 523 | } |
| 524 | } |
| 525 | |
| 526 | impl AsRef<str> for GStr { |
| 527 | #[inline ] |
| 528 | fn as_ref(&self) -> &str { |
| 529 | self.as_str() |
| 530 | } |
| 531 | } |
| 532 | |
| 533 | impl AsRef<OsStr> for GStr { |
| 534 | #[inline ] |
| 535 | fn as_ref(&self) -> &OsStr { |
| 536 | OsStr::new(self.as_str()) |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | impl AsRef<Path> for GStr { |
| 541 | #[inline ] |
| 542 | fn as_ref(&self) -> &Path { |
| 543 | Path::new(self.as_str()) |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | impl AsRef<[u8]> for GStr { |
| 548 | #[inline ] |
| 549 | fn as_ref(&self) -> &[u8] { |
| 550 | self.as_bytes() |
| 551 | } |
| 552 | } |
| 553 | |
| 554 | impl Deref for GStr { |
| 555 | type Target = str; |
| 556 | |
| 557 | #[inline ] |
| 558 | fn deref(&self) -> &str { |
| 559 | self.as_str() |
| 560 | } |
| 561 | } |
| 562 | |
| 563 | impl ToOwned for GStr { |
| 564 | type Owned = GString; |
| 565 | |
| 566 | #[inline ] |
| 567 | fn to_owned(&self) -> Self::Owned { |
| 568 | let b = self.as_bytes_with_nul(); |
| 569 | if self.len() < INLINE_LEN { |
| 570 | let mut data = <[u8; INLINE_LEN]>::default(); |
| 571 | let b = self.as_bytes(); |
| 572 | unsafe { data.get_unchecked_mut(..b.len()) }.copy_from_slice(b); |
| 573 | return GString(Inner::Inline { |
| 574 | len: self.len() as u8, |
| 575 | data, |
| 576 | }); |
| 577 | } |
| 578 | let inner = unsafe { |
| 579 | let copy = ffi::g_strndup(b.as_ptr() as *const c_char, b.len()); |
| 580 | Inner::Foreign { |
| 581 | ptr: ptr::NonNull::new_unchecked(copy), |
| 582 | len: b.len() - 1, |
| 583 | } |
| 584 | }; |
| 585 | GString(inner) |
| 586 | } |
| 587 | } |
| 588 | |
| 589 | impl GlibPtrDefault for GStr { |
| 590 | type GlibType = *mut c_char; |
| 591 | } |
| 592 | |
| 593 | impl StaticType for GStr { |
| 594 | #[inline ] |
| 595 | fn static_type() -> Type { |
| 596 | str::static_type() |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | #[doc (hidden)] |
| 601 | impl FromGlibPtrNone<*const u8> for &GStr { |
| 602 | #[inline ] |
| 603 | unsafe fn from_glib_none(ptr: *const u8) -> Self { |
| 604 | debug_assert!(!ptr.is_null()); |
| 605 | let cstr: &CStr = CStr::from_ptr(ptr as *const _); |
| 606 | debug_assert!(cstr.to_str().is_ok(), "C string is not valid utf-8" ); |
| 607 | GStr::from_utf8_with_nul_unchecked(bytes:cstr.to_bytes_with_nul()) |
| 608 | } |
| 609 | } |
| 610 | |
| 611 | #[doc (hidden)] |
| 612 | impl FromGlibPtrNone<*const i8> for &GStr { |
| 613 | #[inline ] |
| 614 | unsafe fn from_glib_none(ptr: *const i8) -> Self { |
| 615 | from_glib_none(ptr as *const u8) |
| 616 | } |
| 617 | } |
| 618 | |
| 619 | #[doc (hidden)] |
| 620 | impl FromGlibPtrNone<*mut u8> for &GStr { |
| 621 | #[inline ] |
| 622 | unsafe fn from_glib_none(ptr: *mut u8) -> Self { |
| 623 | from_glib_none(ptr as *const u8) |
| 624 | } |
| 625 | } |
| 626 | |
| 627 | #[doc (hidden)] |
| 628 | impl FromGlibPtrNone<*mut i8> for &GStr { |
| 629 | #[inline ] |
| 630 | unsafe fn from_glib_none(ptr: *mut i8) -> Self { |
| 631 | from_glib_none(ptr as *const u8) |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | unsafe impl<'a> crate::value::FromValue<'a> for &'a GStr { |
| 636 | type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>; |
| 637 | |
| 638 | #[inline ] |
| 639 | unsafe fn from_value(value: &'a Value) -> Self { |
| 640 | let ptr: *const {unknown} = gobject_ffi::g_value_get_string(value.to_glib_none().0); |
| 641 | let cstr: &CStr = CStr::from_ptr(ptr); |
| 642 | debug_assert!( |
| 643 | cstr.to_str().is_ok(), |
| 644 | "C string in glib::Value is not valid utf-8" |
| 645 | ); |
| 646 | GStr::from_utf8_with_nul_unchecked(bytes:cstr.to_bytes_with_nul()) |
| 647 | } |
| 648 | } |
| 649 | |
| 650 | impl ToValue for GStr { |
| 651 | #[inline ] |
| 652 | fn to_value(&self) -> Value { |
| 653 | self.as_str().to_value() |
| 654 | } |
| 655 | |
| 656 | #[inline ] |
| 657 | fn value_type(&self) -> Type { |
| 658 | str::static_type() |
| 659 | } |
| 660 | } |
| 661 | |
| 662 | impl ToValue for &GStr { |
| 663 | #[inline ] |
| 664 | fn to_value(&self) -> Value { |
| 665 | (*self).to_value() |
| 666 | } |
| 667 | |
| 668 | #[inline ] |
| 669 | fn value_type(&self) -> Type { |
| 670 | str::static_type() |
| 671 | } |
| 672 | } |
| 673 | |
| 674 | impl crate::value::ToValueOptional for GStr { |
| 675 | #[inline ] |
| 676 | fn to_value_optional(s: Option<&Self>) -> Value { |
| 677 | crate::value::ToValueOptional::to_value_optional(s.map(|s: &GStr| s.as_str())) |
| 678 | } |
| 679 | } |
| 680 | |
| 681 | #[doc (hidden)] |
| 682 | impl<'a> ToGlibPtr<'a, *const c_char> for GStr { |
| 683 | type Storage = PhantomData<&'a Self>; |
| 684 | |
| 685 | #[inline ] |
| 686 | fn to_glib_none(&'a self) -> Stash<'a, *const c_char, Self> { |
| 687 | Stash(self.as_ptr(), PhantomData) |
| 688 | } |
| 689 | |
| 690 | #[inline ] |
| 691 | fn to_glib_full(&self) -> *const c_char { |
| 692 | self.as_str().to_glib_full() |
| 693 | } |
| 694 | } |
| 695 | |
| 696 | #[doc (hidden)] |
| 697 | impl<'a> ToGlibPtr<'a, *mut c_char> for GStr { |
| 698 | type Storage = PhantomData<&'a Self>; |
| 699 | |
| 700 | #[inline ] |
| 701 | fn to_glib_none(&'a self) -> Stash<'a, *mut c_char, Self> { |
| 702 | Stash(self.as_ptr() as *mut c_char, PhantomData) |
| 703 | } |
| 704 | |
| 705 | #[inline ] |
| 706 | fn to_glib_full(&self) -> *mut c_char { |
| 707 | self.as_str().to_glib_full() |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | // rustdoc-stripper-ignore-next |
| 712 | /// `NULL`-terminated UTF-8 string as stored in [`StrV`](crate::StrV). |
| 713 | /// |
| 714 | /// Unlike [`&GStr`](crate::GStr) this does not have its length stored. |
| 715 | #[repr (transparent)] |
| 716 | pub struct GStringPtr(ptr::NonNull<c_char>); |
| 717 | |
| 718 | #[doc (hidden)] |
| 719 | unsafe impl TransparentPtrType for GStringPtr {} |
| 720 | |
| 721 | #[doc (hidden)] |
| 722 | impl GlibPtrDefault for GStringPtr { |
| 723 | type GlibType = *mut c_char; |
| 724 | } |
| 725 | |
| 726 | impl GStringPtr { |
| 727 | // rustdoc-stripper-ignore-next |
| 728 | /// Returns the corresponding [`&GStr`](crate::GStr). |
| 729 | #[inline ] |
| 730 | pub fn to_gstr(&self) -> &GStr { |
| 731 | unsafe { GStr::from_ptr(self.0.as_ptr()) } |
| 732 | } |
| 733 | |
| 734 | // rustdoc-stripper-ignore-next |
| 735 | /// Returns the corresponding [`&str`]. |
| 736 | #[inline ] |
| 737 | pub fn as_str(&self) -> &str { |
| 738 | self.to_gstr().as_str() |
| 739 | } |
| 740 | |
| 741 | // rustdoc-stripper-ignore-next |
| 742 | /// This is just an alias for [`as_str`](GStringPtr::as_str). |
| 743 | #[inline ] |
| 744 | #[deprecated = "Use as_str instead" ] |
| 745 | pub fn to_str(&self) -> &str { |
| 746 | self |
| 747 | } |
| 748 | |
| 749 | // rustdoc-stripper-ignore-next |
| 750 | /// Returns the string's C pointer. |
| 751 | #[inline ] |
| 752 | pub const fn as_ptr(&self) -> *const c_char { |
| 753 | self.0.as_ptr() |
| 754 | } |
| 755 | |
| 756 | // rustdoc-stripper-ignore-next |
| 757 | /// Wrapper around `libc::strcmp` returning `Ordering`. |
| 758 | /// |
| 759 | /// # Safety |
| 760 | /// |
| 761 | /// `a` and `b` must be non-null pointers to nul-terminated C strings. |
| 762 | #[inline ] |
| 763 | unsafe fn strcmp(a: *const c_char, b: *const c_char) -> Ordering { |
| 764 | from_glib(libc::strcmp(a, b)) |
| 765 | } |
| 766 | } |
| 767 | |
| 768 | impl Clone for GStringPtr { |
| 769 | #[inline ] |
| 770 | fn clone(&self) -> GStringPtr { |
| 771 | unsafe { GStringPtr(ptr::NonNull::new_unchecked(ptr:ffi::g_strdup(self.0.as_ptr()))) } |
| 772 | } |
| 773 | } |
| 774 | |
| 775 | impl Deref for GStringPtr { |
| 776 | type Target = str; |
| 777 | |
| 778 | #[inline ] |
| 779 | fn deref(&self) -> &str { |
| 780 | self.as_str() |
| 781 | } |
| 782 | } |
| 783 | |
| 784 | impl IntoGlibPtr<*mut c_char> for GStringPtr { |
| 785 | #[inline ] |
| 786 | unsafe fn into_glib_ptr(self) -> *mut c_char { |
| 787 | self.0.as_ptr() |
| 788 | } |
| 789 | } |
| 790 | |
| 791 | impl Drop for GStringPtr { |
| 792 | #[inline ] |
| 793 | fn drop(&mut self) { |
| 794 | unsafe { |
| 795 | ffi::g_free(self.0.as_ptr() as *mut _); |
| 796 | } |
| 797 | } |
| 798 | } |
| 799 | |
| 800 | impl fmt::Debug for GStringPtr { |
| 801 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 802 | <&GStr as fmt::Debug>::fmt(&self.to_gstr(), f) |
| 803 | } |
| 804 | } |
| 805 | |
| 806 | impl fmt::Display for GStringPtr { |
| 807 | #[inline ] |
| 808 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 809 | f.write_str(self.as_str()) |
| 810 | } |
| 811 | } |
| 812 | |
| 813 | impl Eq for GStringPtr {} |
| 814 | |
| 815 | impl PartialEq for GStringPtr { |
| 816 | #[inline ] |
| 817 | fn eq(&self, other: &GStringPtr) -> bool { |
| 818 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 819 | } |
| 820 | } |
| 821 | |
| 822 | impl PartialEq<GStringPtr> for String { |
| 823 | #[inline ] |
| 824 | fn eq(&self, other: &GStringPtr) -> bool { |
| 825 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 826 | } |
| 827 | } |
| 828 | |
| 829 | impl PartialEq<GStringPtr> for GString { |
| 830 | #[inline ] |
| 831 | fn eq(&self, other: &GStringPtr) -> bool { |
| 832 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 833 | } |
| 834 | } |
| 835 | |
| 836 | impl PartialEq<str> for GStringPtr { |
| 837 | #[inline ] |
| 838 | fn eq(&self, other: &str) -> bool { |
| 839 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 840 | } |
| 841 | } |
| 842 | |
| 843 | impl PartialEq<&str> for GStringPtr { |
| 844 | #[inline ] |
| 845 | fn eq(&self, other: &&str) -> bool { |
| 846 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 847 | } |
| 848 | } |
| 849 | |
| 850 | impl PartialEq<GStr> for GStringPtr { |
| 851 | #[inline ] |
| 852 | fn eq(&self, other: &GStr) -> bool { |
| 853 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 854 | } |
| 855 | } |
| 856 | |
| 857 | impl PartialEq<&GStr> for GStringPtr { |
| 858 | #[inline ] |
| 859 | fn eq(&self, other: &&GStr) -> bool { |
| 860 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 861 | } |
| 862 | } |
| 863 | |
| 864 | impl PartialEq<GStringPtr> for &str { |
| 865 | #[inline ] |
| 866 | fn eq(&self, other: &GStringPtr) -> bool { |
| 867 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 868 | } |
| 869 | } |
| 870 | |
| 871 | impl PartialEq<GStringPtr> for &GStr { |
| 872 | #[inline ] |
| 873 | fn eq(&self, other: &GStringPtr) -> bool { |
| 874 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 875 | } |
| 876 | } |
| 877 | |
| 878 | impl PartialEq<String> for GStringPtr { |
| 879 | #[inline ] |
| 880 | fn eq(&self, other: &String) -> bool { |
| 881 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 882 | } |
| 883 | } |
| 884 | |
| 885 | impl PartialEq<GString> for GStringPtr { |
| 886 | #[inline ] |
| 887 | fn eq(&self, other: &GString) -> bool { |
| 888 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 889 | } |
| 890 | } |
| 891 | |
| 892 | impl PartialEq<GStringPtr> for str { |
| 893 | #[inline ] |
| 894 | fn eq(&self, other: &GStringPtr) -> bool { |
| 895 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 896 | } |
| 897 | } |
| 898 | |
| 899 | impl PartialEq<GStringPtr> for GStr { |
| 900 | #[inline ] |
| 901 | fn eq(&self, other: &GStringPtr) -> bool { |
| 902 | self.partial_cmp(other) == Some(Ordering::Equal) |
| 903 | } |
| 904 | } |
| 905 | |
| 906 | impl PartialOrd<GStringPtr> for GStringPtr { |
| 907 | #[inline ] |
| 908 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
| 909 | Some(self.cmp(other)) |
| 910 | } |
| 911 | } |
| 912 | |
| 913 | impl Ord for GStringPtr { |
| 914 | #[inline ] |
| 915 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
| 916 | unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) } |
| 917 | } |
| 918 | } |
| 919 | |
| 920 | impl PartialOrd<GStringPtr> for String { |
| 921 | #[inline ] |
| 922 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
| 923 | Some(self.as_str().cmp(other)) |
| 924 | } |
| 925 | } |
| 926 | |
| 927 | impl PartialOrd<GStringPtr> for GString { |
| 928 | #[inline ] |
| 929 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
| 930 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
| 931 | } |
| 932 | } |
| 933 | |
| 934 | impl PartialOrd<String> for GStringPtr { |
| 935 | #[inline ] |
| 936 | fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> { |
| 937 | Some(self.as_str().cmp(other)) |
| 938 | } |
| 939 | } |
| 940 | |
| 941 | impl PartialOrd<GString> for GStringPtr { |
| 942 | #[inline ] |
| 943 | fn partial_cmp(&self, other: &GString) -> Option<std::cmp::Ordering> { |
| 944 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
| 945 | } |
| 946 | } |
| 947 | |
| 948 | impl PartialOrd<GStringPtr> for str { |
| 949 | #[inline ] |
| 950 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
| 951 | Some(self.cmp(other.as_str())) |
| 952 | } |
| 953 | } |
| 954 | |
| 955 | impl PartialOrd<GStringPtr> for GStr { |
| 956 | #[inline ] |
| 957 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
| 958 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
| 959 | } |
| 960 | } |
| 961 | |
| 962 | impl PartialOrd<str> for GStringPtr { |
| 963 | #[inline ] |
| 964 | fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> { |
| 965 | Some(self.as_str().cmp(other)) |
| 966 | } |
| 967 | } |
| 968 | |
| 969 | impl PartialOrd<&str> for GStringPtr { |
| 970 | #[inline ] |
| 971 | fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> { |
| 972 | Some(self.as_str().cmp(other)) |
| 973 | } |
| 974 | } |
| 975 | |
| 976 | impl PartialOrd<GStr> for GStringPtr { |
| 977 | #[inline ] |
| 978 | fn partial_cmp(&self, other: &GStr) -> Option<std::cmp::Ordering> { |
| 979 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
| 980 | } |
| 981 | } |
| 982 | |
| 983 | impl PartialOrd<&GStr> for GStringPtr { |
| 984 | #[inline ] |
| 985 | fn partial_cmp(&self, other: &&GStr) -> Option<std::cmp::Ordering> { |
| 986 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
| 987 | } |
| 988 | } |
| 989 | |
| 990 | impl PartialOrd<GStringPtr> for &str { |
| 991 | #[inline ] |
| 992 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
| 993 | Some(self.cmp(&other.as_str())) |
| 994 | } |
| 995 | } |
| 996 | |
| 997 | impl PartialOrd<GStringPtr> for &GStr { |
| 998 | #[inline ] |
| 999 | fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> { |
| 1000 | Some(unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) }) |
| 1001 | } |
| 1002 | } |
| 1003 | |
| 1004 | impl AsRef<GStringPtr> for GStringPtr { |
| 1005 | #[inline ] |
| 1006 | fn as_ref(&self) -> &GStringPtr { |
| 1007 | self |
| 1008 | } |
| 1009 | } |
| 1010 | |
| 1011 | impl std::hash::Hash for GStringPtr { |
| 1012 | #[inline ] |
| 1013 | fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
| 1014 | self.as_str().hash(state); |
| 1015 | } |
| 1016 | } |
| 1017 | |
| 1018 | // size_of::<Inner>() minus two bytes for length and enum discriminant |
| 1019 | const INLINE_LEN: usize = |
| 1020 | mem::size_of::<Option<Box<str>>>() + mem::size_of::<usize>() - mem::size_of::<u8>() * 2; |
| 1021 | |
| 1022 | // rustdoc-stripper-ignore-next |
| 1023 | /// A type representing an owned, C-compatible, nul-terminated UTF-8 string. |
| 1024 | /// |
| 1025 | /// `GString` is to <code>&[GStr]</code> as [`String`] is to <code>&[str]</code>: the former in |
| 1026 | /// each pair are owned strings; the latter are borrowed references. |
| 1027 | /// |
| 1028 | /// This type is similar to [`std::ffi::CString`], but with some special behavior. When debug |
| 1029 | /// assertions are enabled, <code>[From]<[String]></code> will panic if there are interior |
| 1030 | /// nul-bytes. In production builds, no checks will be made for interior nul-bytes, and strings |
| 1031 | /// that contain interior nul-bytes will simply end at first nul-byte when converting to a C |
| 1032 | /// string. |
| 1033 | /// |
| 1034 | /// The constructors beginning with `from_utf8` and `from_string` can also be used to further |
| 1035 | /// control how interior nul-bytes are handled. |
| 1036 | pub struct GString(Inner); |
| 1037 | |
| 1038 | enum Inner { |
| 1039 | Native(Box<str>), |
| 1040 | Foreign { |
| 1041 | ptr: ptr::NonNull<c_char>, |
| 1042 | len: usize, |
| 1043 | }, |
| 1044 | Inline { |
| 1045 | len: u8, |
| 1046 | data: [u8; INLINE_LEN], |
| 1047 | }, |
| 1048 | } |
| 1049 | |
| 1050 | unsafe impl Send for GString {} |
| 1051 | unsafe impl Sync for GString {} |
| 1052 | |
| 1053 | impl GString { |
| 1054 | // rustdoc-stripper-ignore-next |
| 1055 | /// Creates a new empty [`GString`]. |
| 1056 | /// |
| 1057 | /// Does not allocate. |
| 1058 | #[inline ] |
| 1059 | pub fn new() -> Self { |
| 1060 | Self(Inner::Inline { |
| 1061 | len: 0, |
| 1062 | data: Default::default(), |
| 1063 | }) |
| 1064 | } |
| 1065 | // rustdoc-stripper-ignore-next |
| 1066 | /// Formats an [`Arguments`](std::fmt::Arguments) into a [`GString`]. |
| 1067 | /// |
| 1068 | /// This function is the same as [`std::fmt::format`], except it returns a [`GString`]. The |
| 1069 | /// [`Arguments`](std::fmt::Arguments) instance can be created with the |
| 1070 | /// [`format_args!`](std::format_args) macro. |
| 1071 | /// |
| 1072 | /// Please note that using [`gformat!`](crate::gformat) might be preferable. |
| 1073 | pub fn format(args: fmt::Arguments) -> Self { |
| 1074 | if let Some(s) = args.as_str() { |
| 1075 | return Self::from(s); |
| 1076 | } |
| 1077 | |
| 1078 | let mut s = crate::GStringBuilder::default(); |
| 1079 | fmt::Write::write_fmt(&mut s, args).unwrap(); |
| 1080 | s.into_string() |
| 1081 | } |
| 1082 | // rustdoc-stripper-ignore-next |
| 1083 | /// Creates a GLib string by consuming a byte vector. |
| 1084 | /// |
| 1085 | /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8. |
| 1086 | /// |
| 1087 | /// A trailing nul-byte will be appended by this function. |
| 1088 | #[inline ] |
| 1089 | pub fn from_utf8(bytes: Vec<u8>) -> Result<Self, std::string::FromUtf8Error> { |
| 1090 | Ok(Self::from_string_unchecked(String::from_utf8(bytes)?)) |
| 1091 | } |
| 1092 | // rustdoc-stripper-ignore-next |
| 1093 | /// Creates a GLib string by consuming a byte vector, checking for interior nul-bytes. |
| 1094 | /// |
| 1095 | /// Takes ownership of `bytes`, as long as it is valid UTF-8 and does not contain any interior |
| 1096 | /// nul-bytes. Otherwise, `Err` is returned. |
| 1097 | /// |
| 1098 | /// A trailing nul-byte will be appended by this function. |
| 1099 | #[inline ] |
| 1100 | pub fn from_utf8_checked(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> { |
| 1101 | Ok(Self::from_string_checked(String::from_utf8(bytes)?) |
| 1102 | .map_err(|e| GStringInteriorNulError(e.0.into_bytes(), e.1))?) |
| 1103 | } |
| 1104 | // rustdoc-stripper-ignore-next |
| 1105 | /// Unsafely creates a GLib string by consuming a byte vector, without checking for UTF-8 or |
| 1106 | /// interior nul-bytes. |
| 1107 | /// |
| 1108 | /// A trailing nul-byte will be appended by this function. |
| 1109 | /// |
| 1110 | /// # Safety |
| 1111 | /// |
| 1112 | /// The byte vector **must** not contain invalid UTF-8 characters. It is undefined behavior to |
| 1113 | /// pass a vector that contains invalid UTF-8. |
| 1114 | #[inline ] |
| 1115 | pub unsafe fn from_utf8_unchecked(mut v: Vec<u8>) -> Self { |
| 1116 | if v.is_empty() { |
| 1117 | Self::new() |
| 1118 | } else { |
| 1119 | v.reserve_exact(1); |
| 1120 | v.push(0); |
| 1121 | Self(Inner::Native(String::from_utf8_unchecked(v).into())) |
| 1122 | } |
| 1123 | } |
| 1124 | // rustdoc-stripper-ignore-next |
| 1125 | /// Creates a GLib string by consuming a nul-terminated byte vector, without checking for |
| 1126 | /// interior nul-bytes. |
| 1127 | /// |
| 1128 | /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8 or does not have a |
| 1129 | /// trailing nul-byte. |
| 1130 | #[inline ] |
| 1131 | pub fn from_utf8_with_nul(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> { |
| 1132 | let s = String::from_utf8(bytes)?; |
| 1133 | if s.as_bytes().last().copied() != Some(0u8) { |
| 1134 | return Err(GStringNoTrailingNulError(s.into_bytes()).into()); |
| 1135 | } |
| 1136 | if s.len() == 1 { |
| 1137 | Ok(Self::new()) |
| 1138 | } else { |
| 1139 | Ok(Self(Inner::Native(s.into()))) |
| 1140 | } |
| 1141 | } |
| 1142 | // rustdoc-stripper-ignore-next |
| 1143 | /// Creates a GLib string by consuming a nul-terminated byte vector. |
| 1144 | /// |
| 1145 | /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8, does not have a |
| 1146 | /// trailing nul-byte, or contains interior nul-bytes. |
| 1147 | #[inline ] |
| 1148 | pub fn from_utf8_with_nul_checked(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> { |
| 1149 | let s = Self::from_utf8_with_nul(bytes)?; |
| 1150 | if let Err(e) = GStr::check_interior_nuls(&s) { |
| 1151 | return Err(GStringInteriorNulError(s.into_bytes(), e).into()); |
| 1152 | } |
| 1153 | Ok(s) |
| 1154 | } |
| 1155 | // rustdoc-stripper-ignore-next |
| 1156 | /// Creates a GLib string by consuming a byte vector, without checking for UTF-8, a trailing |
| 1157 | /// nul-byte, or interior nul-bytes. |
| 1158 | /// |
| 1159 | /// # Safety |
| 1160 | /// |
| 1161 | /// The byte vector **must** not contain invalid UTF-8 characters, and **must** have a trailing |
| 1162 | /// nul-byte. It is undefined behavior to pass a vector that does not uphold those conditions. |
| 1163 | #[inline ] |
| 1164 | pub unsafe fn from_utf8_with_nul_unchecked(v: Vec<u8>) -> Self { |
| 1165 | debug_assert!(!v.is_empty() && v[v.len() - 1] == 0); |
| 1166 | let s = if cfg!(debug_assertions) { |
| 1167 | let s = String::from_utf8(v).unwrap(); |
| 1168 | GStr::check_interior_nuls(&s[..s.len() - 1]).unwrap(); |
| 1169 | s |
| 1170 | } else { |
| 1171 | String::from_utf8_unchecked(v) |
| 1172 | }; |
| 1173 | if s.len() == 1 { |
| 1174 | Self::new() |
| 1175 | } else { |
| 1176 | Self(Inner::Native(s.into())) |
| 1177 | } |
| 1178 | } |
| 1179 | // rustdoc-stripper-ignore-next |
| 1180 | /// Creates a GLib string by consuming a nul-terminated byte vector, truncating it at the first |
| 1181 | /// nul-byte. |
| 1182 | /// |
| 1183 | /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8 or does not contain |
| 1184 | /// at least one nul-byte. |
| 1185 | #[inline ] |
| 1186 | pub fn from_utf8_until_nul(mut bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> { |
| 1187 | let nul_pos = if let Some(nul_pos) = memchr::memchr(0, &bytes) { |
| 1188 | nul_pos |
| 1189 | } else { |
| 1190 | return Err(GStringNoTrailingNulError(bytes).into()); |
| 1191 | }; |
| 1192 | if nul_pos == 0 { |
| 1193 | Ok(Self::new()) |
| 1194 | } else { |
| 1195 | if let Err(e) = std::str::from_utf8(unsafe { bytes.get_unchecked(..nul_pos) }) { |
| 1196 | return Err(GStringUtf8Error(bytes, e).into()); |
| 1197 | } |
| 1198 | bytes.truncate(nul_pos + 1); |
| 1199 | let s = unsafe { String::from_utf8_unchecked(bytes) }; |
| 1200 | Ok(Self(Inner::Native(s.into()))) |
| 1201 | } |
| 1202 | } |
| 1203 | // rustdoc-stripper-ignore-next |
| 1204 | /// Creates a GLib string by consuming a string, checking for interior nul-bytes. |
| 1205 | /// |
| 1206 | /// Takes ownership of `s`, as long as it does not contain any interior nul-bytes. Otherwise, |
| 1207 | /// `Err` is returned. |
| 1208 | /// |
| 1209 | /// A trailing nul-byte will be appended by this function. |
| 1210 | #[inline ] |
| 1211 | pub fn from_string_checked(s: String) -> Result<Self, GStringInteriorNulError<String>> { |
| 1212 | if let Err(e) = GStr::check_interior_nuls(&s) { |
| 1213 | return Err(GStringInteriorNulError(s, e)); |
| 1214 | } |
| 1215 | Ok(Self::from_string_unchecked(s)) |
| 1216 | } |
| 1217 | // rustdoc-stripper-ignore-next |
| 1218 | /// Creates a GLib string by consuming a string, without checking for interior nul-bytes. |
| 1219 | /// |
| 1220 | /// A trailing nul-byte will be appended by this function. |
| 1221 | #[inline ] |
| 1222 | pub fn from_string_unchecked(mut s: String) -> Self { |
| 1223 | if s.is_empty() { |
| 1224 | Self::new() |
| 1225 | } else { |
| 1226 | s.reserve_exact(1); |
| 1227 | s.push(' \0' ); |
| 1228 | Self(Inner::Native(s.into())) |
| 1229 | } |
| 1230 | } |
| 1231 | // rustdoc-stripper-ignore-next |
| 1232 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
| 1233 | /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here. |
| 1234 | /// |
| 1235 | /// If the string is valid UTF-8 then it is directly returned otherwise a copy is created with |
| 1236 | /// every invalid character replaced by the Unicode replacement character (U+FFFD). |
| 1237 | #[inline ] |
| 1238 | pub unsafe fn from_ptr_lossy<'a>(ptr: *const c_char) -> Cow<'a, GStr> { |
| 1239 | GStr::from_ptr_lossy(ptr) |
| 1240 | } |
| 1241 | |
| 1242 | // rustdoc-stripper-ignore-next |
| 1243 | /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be |
| 1244 | /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here. |
| 1245 | /// |
| 1246 | /// `len` is the length without the nul-terminator, i.e. if `len == 0` is passed then `*ptr` |
| 1247 | /// must be the nul-terminator. |
| 1248 | #[inline ] |
| 1249 | pub unsafe fn from_ptr_and_len_unchecked(ptr: *const c_char, len: usize) -> Self { |
| 1250 | debug_assert!(!ptr.is_null()); |
| 1251 | |
| 1252 | GString(Inner::Foreign { |
| 1253 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
| 1254 | len, |
| 1255 | }) |
| 1256 | } |
| 1257 | |
| 1258 | // rustdoc-stripper-ignore-next |
| 1259 | /// Return the `GString` as string slice. |
| 1260 | #[inline ] |
| 1261 | pub fn as_str(&self) -> &str { |
| 1262 | unsafe { |
| 1263 | let (ptr, len) = match self.0 { |
| 1264 | Inner::Native(ref s) => (s.as_ptr(), s.len() - 1), |
| 1265 | Inner::Foreign { ptr, len } => (ptr.as_ptr() as *const u8, len), |
| 1266 | Inner::Inline { len, ref data } => (data.as_ptr(), len as usize), |
| 1267 | }; |
| 1268 | if len == 0 { |
| 1269 | "" |
| 1270 | } else { |
| 1271 | let slice = slice::from_raw_parts(ptr, len); |
| 1272 | std::str::from_utf8_unchecked(slice) |
| 1273 | } |
| 1274 | } |
| 1275 | } |
| 1276 | |
| 1277 | // rustdoc-stripper-ignore-next |
| 1278 | /// Extracts the [`GStr`] containing the entire string. |
| 1279 | #[inline ] |
| 1280 | pub fn as_gstr(&self) -> &GStr { |
| 1281 | let bytes = match self.0 { |
| 1282 | Inner::Native(ref s) => s.as_bytes(), |
| 1283 | Inner::Foreign { len: 0, .. } => &[0], |
| 1284 | Inner::Foreign { ptr, len } => unsafe { |
| 1285 | slice::from_raw_parts(ptr.as_ptr() as *const _, len + 1) |
| 1286 | }, |
| 1287 | Inner::Inline { len, ref data } => unsafe { data.get_unchecked(..len as usize + 1) }, |
| 1288 | }; |
| 1289 | unsafe { GStr::from_utf8_with_nul_unchecked(bytes) } |
| 1290 | } |
| 1291 | |
| 1292 | // rustdoc-stripper-ignore-next |
| 1293 | /// Return the underlying pointer of the `GString`. |
| 1294 | #[inline ] |
| 1295 | pub fn as_ptr(&self) -> *const c_char { |
| 1296 | match self.0 { |
| 1297 | Inner::Native(ref s) => s.as_ptr() as *const _, |
| 1298 | Inner::Foreign { ptr, .. } => ptr.as_ptr(), |
| 1299 | Inner::Inline { ref data, .. } => data.as_ptr() as *const _, |
| 1300 | } |
| 1301 | } |
| 1302 | |
| 1303 | // rustdoc-stripper-ignore-next |
| 1304 | /// Consumes the `GString` and returns the underlying byte buffer. |
| 1305 | /// |
| 1306 | /// The returned buffer is not guaranteed to contain a trailing nul-byte. |
| 1307 | #[inline ] |
| 1308 | pub fn into_bytes(mut self) -> Vec<u8> { |
| 1309 | match &mut self.0 { |
| 1310 | Inner::Native(s) => { |
| 1311 | let mut s = String::from(mem::replace(s, "" .into())); |
| 1312 | let _nul = s.pop(); |
| 1313 | debug_assert_eq!(_nul, Some(' \0' )); |
| 1314 | s.into_bytes() |
| 1315 | } |
| 1316 | Inner::Foreign { ptr, len } => { |
| 1317 | let bytes = unsafe { slice::from_raw_parts(ptr.as_ptr() as *const u8, *len - 1) }; |
| 1318 | bytes.to_owned() |
| 1319 | } |
| 1320 | Inner::Inline { len, data } => { |
| 1321 | unsafe { data.get_unchecked(..*len as usize) }.to_owned() |
| 1322 | } |
| 1323 | } |
| 1324 | } |
| 1325 | |
| 1326 | // rustdoc-stripper-ignore-next |
| 1327 | /// Consumes the `GString` and returns the underlying byte buffer, with trailing nul-byte. |
| 1328 | #[inline ] |
| 1329 | pub fn into_bytes_with_nul(mut self) -> Vec<u8> { |
| 1330 | match &mut self.0 { |
| 1331 | Inner::Native(s) => str::into_boxed_bytes(mem::replace(s, "" .into())).into(), |
| 1332 | Inner::Foreign { ptr, len } => { |
| 1333 | let bytes = unsafe { slice::from_raw_parts(ptr.as_ptr() as *const u8, *len) }; |
| 1334 | bytes.to_owned() |
| 1335 | } |
| 1336 | Inner::Inline { len, data } => { |
| 1337 | unsafe { data.get_unchecked(..*len as usize + 1) }.to_owned() |
| 1338 | } |
| 1339 | } |
| 1340 | } |
| 1341 | } |
| 1342 | |
| 1343 | // rustdoc-stripper-ignore-next |
| 1344 | /// Creates a [`GString`] using interpolation of runtime expressions. |
| 1345 | /// |
| 1346 | /// This macro is the same as [`std::format!`] except it returns a [`GString`]. It is faster than |
| 1347 | /// creating a [`String`] and then converting it manually to a [`GString`]. |
| 1348 | #[macro_export ] |
| 1349 | macro_rules! gformat { |
| 1350 | ($($arg:tt)*) => { $crate::GString::format(std::format_args!($($arg)*)) }; |
| 1351 | } |
| 1352 | |
| 1353 | // rustdoc-stripper-ignore-next |
| 1354 | /// Error type indicating that a buffer did not have a trailing nul-byte. |
| 1355 | /// |
| 1356 | /// `T` is the type of the value the conversion was attempted from. |
| 1357 | #[derive (Clone, PartialEq, Eq, Debug)] |
| 1358 | pub struct GStringNoTrailingNulError<T>(T); |
| 1359 | |
| 1360 | impl<T> std::error::Error for GStringNoTrailingNulError<T> where T: fmt::Debug {} |
| 1361 | |
| 1362 | impl<T> fmt::Display for GStringNoTrailingNulError<T> { |
| 1363 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| 1364 | fmt.write_str(data:"data provided is not nul terminated" ) |
| 1365 | } |
| 1366 | } |
| 1367 | |
| 1368 | impl<T> GStringNoTrailingNulError<T> { |
| 1369 | // rustdoc-stripper-ignore-next |
| 1370 | /// Returns the original value that was attempted to convert to [`GString`]. |
| 1371 | #[inline ] |
| 1372 | pub fn into_inner(self) -> T { |
| 1373 | self.0 |
| 1374 | } |
| 1375 | } |
| 1376 | |
| 1377 | // rustdoc-stripper-ignore-next |
| 1378 | /// Error type indicating that a buffer had unexpected nul-bytes. |
| 1379 | /// |
| 1380 | /// `T` is the type of the value the conversion was attempted from. |
| 1381 | #[derive (Clone, PartialEq, Eq, Debug)] |
| 1382 | pub struct GStringInteriorNulError<T>(T, GStrInteriorNulError); |
| 1383 | |
| 1384 | impl<T> std::error::Error for GStringInteriorNulError<T> where T: fmt::Debug {} |
| 1385 | |
| 1386 | impl<T> fmt::Display for GStringInteriorNulError<T> { |
| 1387 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| 1388 | fmt::Display::fmt(&self.1, f:fmt) |
| 1389 | } |
| 1390 | } |
| 1391 | |
| 1392 | impl<T> GStringInteriorNulError<T> { |
| 1393 | // rustdoc-stripper-ignore-next |
| 1394 | /// Returns the original value that was attempted to convert to [`GString`]. |
| 1395 | #[inline ] |
| 1396 | pub fn into_inner(self) -> T { |
| 1397 | self.0 |
| 1398 | } |
| 1399 | // rustdoc-stripper-ignore-next |
| 1400 | /// Fetch a [`GStrInteriorNulError`] to get more details about the conversion failure. |
| 1401 | #[inline ] |
| 1402 | pub fn nul_error(&self) -> GStrInteriorNulError { |
| 1403 | self.1 |
| 1404 | } |
| 1405 | } |
| 1406 | |
| 1407 | // rustdoc-stripper-ignore-next |
| 1408 | /// Error type indicating that a buffer had invalid UTF-8. |
| 1409 | /// |
| 1410 | /// `T` is the type of the value the conversion was attempted from. |
| 1411 | #[derive (Clone, PartialEq, Eq, Debug)] |
| 1412 | pub struct GStringUtf8Error<T>(T, std::str::Utf8Error); |
| 1413 | |
| 1414 | impl<T> std::error::Error for GStringUtf8Error<T> where T: fmt::Debug {} |
| 1415 | |
| 1416 | impl<T> fmt::Display for GStringUtf8Error<T> { |
| 1417 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| 1418 | fmt::Display::fmt(&self.1, f:fmt) |
| 1419 | } |
| 1420 | } |
| 1421 | |
| 1422 | impl<T> GStringUtf8Error<T> { |
| 1423 | // rustdoc-stripper-ignore-next |
| 1424 | /// Returns the original value that was attempted to convert to [`GString`]. |
| 1425 | #[inline ] |
| 1426 | pub fn into_inner(self) -> T { |
| 1427 | self.0 |
| 1428 | } |
| 1429 | // rustdoc-stripper-ignore-next |
| 1430 | /// Fetch a [`Utf8Error`](std::str::Utf8Error) to get more details about the conversion |
| 1431 | /// failure. |
| 1432 | #[inline ] |
| 1433 | pub fn utf8_error(&self) -> std::str::Utf8Error { |
| 1434 | self.1 |
| 1435 | } |
| 1436 | } |
| 1437 | |
| 1438 | // rustdoc-stripper-ignore-next |
| 1439 | /// Error type holding all possible failures when creating a [`GString`]. |
| 1440 | #[derive (Debug)] |
| 1441 | pub enum GStringFromError<T> { |
| 1442 | NoTrailingNul(GStringNoTrailingNulError<T>), |
| 1443 | InteriorNul(GStringInteriorNulError<T>), |
| 1444 | InvalidUtf8(GStringUtf8Error<T>), |
| 1445 | Unspecified(T), |
| 1446 | } |
| 1447 | |
| 1448 | impl<T> std::error::Error for GStringFromError<T> |
| 1449 | where |
| 1450 | T: fmt::Debug, |
| 1451 | { |
| 1452 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
| 1453 | match self { |
| 1454 | Self::NoTrailingNul(err: &GStringNoTrailingNulError<…>) => std::error::Error::source(self:err), |
| 1455 | Self::InteriorNul(err: &GStringInteriorNulError) => std::error::Error::source(self:err), |
| 1456 | Self::InvalidUtf8(err: &GStringUtf8Error) => std::error::Error::source(self:err), |
| 1457 | Self::Unspecified { .. } => None, |
| 1458 | } |
| 1459 | } |
| 1460 | } |
| 1461 | |
| 1462 | impl<T> fmt::Display for GStringFromError<T> { |
| 1463 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| 1464 | match self { |
| 1465 | Self::NoTrailingNul(err: &GStringNoTrailingNulError<…>) => fmt::Display::fmt(self:err, f:fmt), |
| 1466 | Self::InteriorNul(err: &GStringInteriorNulError) => fmt::Display::fmt(self:err, f:fmt), |
| 1467 | Self::InvalidUtf8(err: &GStringUtf8Error) => fmt::Display::fmt(self:err, f:fmt), |
| 1468 | Self::Unspecified(_) => fmt.write_str(data:"unable to convert" ), |
| 1469 | } |
| 1470 | } |
| 1471 | } |
| 1472 | |
| 1473 | impl<T> std::convert::From<GStringNoTrailingNulError<T>> for GStringFromError<T> { |
| 1474 | fn from(err: GStringNoTrailingNulError<T>) -> Self { |
| 1475 | GStringFromError::NoTrailingNul(err) |
| 1476 | } |
| 1477 | } |
| 1478 | |
| 1479 | impl<T> std::convert::From<GStringInteriorNulError<T>> for GStringFromError<T> { |
| 1480 | fn from(err: GStringInteriorNulError<T>) -> Self { |
| 1481 | GStringFromError::InteriorNul(err) |
| 1482 | } |
| 1483 | } |
| 1484 | |
| 1485 | impl<T> std::convert::From<GStringUtf8Error<T>> for GStringFromError<T> { |
| 1486 | fn from(err: GStringUtf8Error<T>) -> Self { |
| 1487 | GStringFromError::InvalidUtf8(err) |
| 1488 | } |
| 1489 | } |
| 1490 | |
| 1491 | impl<T> GStringFromError<T> { |
| 1492 | pub fn into_inner(self) -> T { |
| 1493 | match self { |
| 1494 | Self::NoTrailingNul(GStringNoTrailingNulError(t)) => t, |
| 1495 | Self::InteriorNul(GStringInteriorNulError(t, _)) => t, |
| 1496 | Self::InvalidUtf8(GStringUtf8Error(t, _)) => t, |
| 1497 | Self::Unspecified(t) => t, |
| 1498 | } |
| 1499 | } |
| 1500 | #[inline ] |
| 1501 | fn convert<R>(self, func: impl FnOnce(T) -> R) -> GStringFromError<R> { |
| 1502 | match self { |
| 1503 | Self::NoTrailingNul(GStringNoTrailingNulError(t)) => { |
| 1504 | GStringFromError::NoTrailingNul(GStringNoTrailingNulError(func(t))) |
| 1505 | } |
| 1506 | Self::InteriorNul(GStringInteriorNulError(t, e)) => { |
| 1507 | GStringFromError::InteriorNul(GStringInteriorNulError(func(t), e)) |
| 1508 | } |
| 1509 | Self::InvalidUtf8(GStringUtf8Error(t, e)) => { |
| 1510 | GStringFromError::InvalidUtf8(GStringUtf8Error(func(t), e)) |
| 1511 | } |
| 1512 | Self::Unspecified(t) => GStringFromError::Unspecified(func(t)), |
| 1513 | } |
| 1514 | } |
| 1515 | } |
| 1516 | |
| 1517 | impl From<std::string::FromUtf8Error> for GStringFromError<Vec<u8>> { |
| 1518 | #[inline ] |
| 1519 | fn from(e: std::string::FromUtf8Error) -> Self { |
| 1520 | let ue: Utf8Error = e.utf8_error(); |
| 1521 | Self::InvalidUtf8(GStringUtf8Error(e.into_bytes(), ue)) |
| 1522 | } |
| 1523 | } |
| 1524 | |
| 1525 | impl IntoGlibPtr<*mut c_char> for GString { |
| 1526 | // rustdoc-stripper-ignore-next |
| 1527 | /// Transform into a nul-terminated raw C string pointer. |
| 1528 | #[inline ] |
| 1529 | unsafe fn into_glib_ptr(self) -> *mut c_char { |
| 1530 | match self.0 { |
| 1531 | Inner::Native(ref s: &Box) => ffi::g_strndup(str:s.as_ptr() as *const _, n:s.len()), |
| 1532 | Inner::Foreign { ptr: NonNull<{unknown}>, .. } => { |
| 1533 | let _s: ManuallyDrop = mem::ManuallyDrop::new(self); |
| 1534 | ptr.as_ptr() |
| 1535 | } |
| 1536 | Inner::Inline { len: u8, ref data: &[u8; 22] } => { |
| 1537 | ffi::g_strndup(str:data.as_ptr() as *const _, n:len as usize) |
| 1538 | } |
| 1539 | } |
| 1540 | } |
| 1541 | } |
| 1542 | |
| 1543 | impl Default for GString { |
| 1544 | #[inline ] |
| 1545 | fn default() -> Self { |
| 1546 | Self::new() |
| 1547 | } |
| 1548 | } |
| 1549 | |
| 1550 | impl Clone for GString { |
| 1551 | #[inline ] |
| 1552 | fn clone(&self) -> GString { |
| 1553 | self.as_str().into() |
| 1554 | } |
| 1555 | } |
| 1556 | |
| 1557 | impl fmt::Debug for GString { |
| 1558 | #[inline ] |
| 1559 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 1560 | <&str as fmt::Debug>::fmt(&self.as_str(), f) |
| 1561 | } |
| 1562 | } |
| 1563 | |
| 1564 | impl Drop for GString { |
| 1565 | #[inline ] |
| 1566 | fn drop(&mut self) { |
| 1567 | if let Inner::Foreign { ptr: NonNull<{unknown}>, .. } = self.0 { |
| 1568 | unsafe { |
| 1569 | ffi::g_free(mem:ptr.as_ptr() as *mut _); |
| 1570 | } |
| 1571 | } |
| 1572 | } |
| 1573 | } |
| 1574 | |
| 1575 | impl fmt::Display for GString { |
| 1576 | #[inline ] |
| 1577 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 1578 | f.write_str(self.as_str()) |
| 1579 | } |
| 1580 | } |
| 1581 | |
| 1582 | impl hash::Hash for GString { |
| 1583 | #[inline ] |
| 1584 | fn hash<H: hash::Hasher>(&self, state: &mut H) { |
| 1585 | self.as_str().hash(state) |
| 1586 | } |
| 1587 | } |
| 1588 | |
| 1589 | impl Borrow<GStr> for GString { |
| 1590 | #[inline ] |
| 1591 | fn borrow(&self) -> &GStr { |
| 1592 | self.as_gstr() |
| 1593 | } |
| 1594 | } |
| 1595 | |
| 1596 | impl Borrow<str> for GString { |
| 1597 | #[inline ] |
| 1598 | fn borrow(&self) -> &str { |
| 1599 | self.as_str() |
| 1600 | } |
| 1601 | } |
| 1602 | |
| 1603 | impl Ord for GString { |
| 1604 | #[inline ] |
| 1605 | fn cmp(&self, other: &GString) -> Ordering { |
| 1606 | self.as_str().cmp(other.as_str()) |
| 1607 | } |
| 1608 | } |
| 1609 | |
| 1610 | impl PartialOrd for GString { |
| 1611 | #[inline ] |
| 1612 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
| 1613 | Some(self.cmp(other)) |
| 1614 | } |
| 1615 | } |
| 1616 | |
| 1617 | impl PartialEq for GString { |
| 1618 | #[inline ] |
| 1619 | fn eq(&self, other: &GString) -> bool { |
| 1620 | self.as_str() == other.as_str() |
| 1621 | } |
| 1622 | } |
| 1623 | |
| 1624 | impl PartialEq<GString> for String { |
| 1625 | #[inline ] |
| 1626 | fn eq(&self, other: &GString) -> bool { |
| 1627 | self.as_str() == other.as_str() |
| 1628 | } |
| 1629 | } |
| 1630 | |
| 1631 | impl PartialEq<GStr> for GString { |
| 1632 | #[inline ] |
| 1633 | fn eq(&self, other: &GStr) -> bool { |
| 1634 | self.as_str() == other.as_str() |
| 1635 | } |
| 1636 | } |
| 1637 | |
| 1638 | impl PartialEq<&GStr> for GString { |
| 1639 | #[inline ] |
| 1640 | fn eq(&self, other: &&GStr) -> bool { |
| 1641 | self.as_str() == other.as_str() |
| 1642 | } |
| 1643 | } |
| 1644 | |
| 1645 | impl PartialEq<str> for GString { |
| 1646 | #[inline ] |
| 1647 | fn eq(&self, other: &str) -> bool { |
| 1648 | self.as_str() == other |
| 1649 | } |
| 1650 | } |
| 1651 | |
| 1652 | impl PartialEq<&str> for GString { |
| 1653 | #[inline ] |
| 1654 | fn eq(&self, other: &&str) -> bool { |
| 1655 | self.as_str() == *other |
| 1656 | } |
| 1657 | } |
| 1658 | |
| 1659 | impl PartialEq<GString> for &GStr { |
| 1660 | #[inline ] |
| 1661 | fn eq(&self, other: &GString) -> bool { |
| 1662 | self.as_str() == other.as_str() |
| 1663 | } |
| 1664 | } |
| 1665 | |
| 1666 | impl PartialEq<GString> for &str { |
| 1667 | #[inline ] |
| 1668 | fn eq(&self, other: &GString) -> bool { |
| 1669 | *self == other.as_str() |
| 1670 | } |
| 1671 | } |
| 1672 | |
| 1673 | impl PartialEq<String> for GString { |
| 1674 | #[inline ] |
| 1675 | fn eq(&self, other: &String) -> bool { |
| 1676 | self.as_str() == other.as_str() |
| 1677 | } |
| 1678 | } |
| 1679 | |
| 1680 | impl PartialEq<GString> for str { |
| 1681 | #[inline ] |
| 1682 | fn eq(&self, other: &GString) -> bool { |
| 1683 | self == other.as_str() |
| 1684 | } |
| 1685 | } |
| 1686 | |
| 1687 | impl PartialEq<GString> for GStr { |
| 1688 | #[inline ] |
| 1689 | fn eq(&self, other: &GString) -> bool { |
| 1690 | self.as_str() == other.as_str() |
| 1691 | } |
| 1692 | } |
| 1693 | |
| 1694 | impl PartialOrd<GString> for String { |
| 1695 | #[inline ] |
| 1696 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
| 1697 | Some(self.cmp(&String::from(other.as_str()))) |
| 1698 | } |
| 1699 | } |
| 1700 | |
| 1701 | impl PartialOrd<String> for GString { |
| 1702 | #[inline ] |
| 1703 | fn partial_cmp(&self, other: &String) -> Option<Ordering> { |
| 1704 | Some(self.as_str().cmp(other.as_str())) |
| 1705 | } |
| 1706 | } |
| 1707 | |
| 1708 | impl PartialOrd<GString> for GStr { |
| 1709 | #[inline ] |
| 1710 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
| 1711 | Some(self.as_str().cmp(other)) |
| 1712 | } |
| 1713 | } |
| 1714 | |
| 1715 | impl PartialOrd<GStr> for GString { |
| 1716 | #[inline ] |
| 1717 | fn partial_cmp(&self, other: &GStr) -> Option<Ordering> { |
| 1718 | Some(self.as_str().cmp(other.as_str())) |
| 1719 | } |
| 1720 | } |
| 1721 | |
| 1722 | impl PartialOrd<GString> for str { |
| 1723 | #[inline ] |
| 1724 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
| 1725 | Some(self.cmp(other)) |
| 1726 | } |
| 1727 | } |
| 1728 | |
| 1729 | impl PartialOrd<str> for GString { |
| 1730 | #[inline ] |
| 1731 | fn partial_cmp(&self, other: &str) -> Option<Ordering> { |
| 1732 | Some(self.as_str().cmp(other)) |
| 1733 | } |
| 1734 | } |
| 1735 | |
| 1736 | impl Eq for GString {} |
| 1737 | |
| 1738 | impl AsRef<GStr> for GString { |
| 1739 | #[inline ] |
| 1740 | fn as_ref(&self) -> &GStr { |
| 1741 | self.as_gstr() |
| 1742 | } |
| 1743 | } |
| 1744 | |
| 1745 | impl AsRef<str> for GString { |
| 1746 | #[inline ] |
| 1747 | fn as_ref(&self) -> &str { |
| 1748 | self.as_str() |
| 1749 | } |
| 1750 | } |
| 1751 | |
| 1752 | impl AsRef<OsStr> for GString { |
| 1753 | #[inline ] |
| 1754 | fn as_ref(&self) -> &OsStr { |
| 1755 | OsStr::new(self.as_str()) |
| 1756 | } |
| 1757 | } |
| 1758 | |
| 1759 | impl AsRef<Path> for GString { |
| 1760 | #[inline ] |
| 1761 | fn as_ref(&self) -> &Path { |
| 1762 | Path::new(self.as_str()) |
| 1763 | } |
| 1764 | } |
| 1765 | |
| 1766 | impl AsRef<[u8]> for GString { |
| 1767 | #[inline ] |
| 1768 | fn as_ref(&self) -> &[u8] { |
| 1769 | self.as_str().as_bytes() |
| 1770 | } |
| 1771 | } |
| 1772 | |
| 1773 | impl Deref for GString { |
| 1774 | type Target = str; |
| 1775 | |
| 1776 | #[inline ] |
| 1777 | fn deref(&self) -> &str { |
| 1778 | self.as_str() |
| 1779 | } |
| 1780 | } |
| 1781 | |
| 1782 | impl From<GString> for String { |
| 1783 | #[inline ] |
| 1784 | fn from(mut s: GString) -> Self { |
| 1785 | match &mut s.0 { |
| 1786 | Inner::Native(s: &mut Box) => { |
| 1787 | // Moves the underlying string |
| 1788 | let mut s: String = String::from(mem::replace(dest:s, src:"" .into())); |
| 1789 | let _nul: Option = s.pop(); |
| 1790 | debug_assert_eq!(_nul, Some(' \0' )); |
| 1791 | s |
| 1792 | } |
| 1793 | Inner::Foreign { len: &mut usize, .. } if *len == 0 => String::new(), |
| 1794 | Inner::Foreign { ptr: &mut NonNull<{unknown}>, len: &mut usize } => unsafe { |
| 1795 | // Creates a copy |
| 1796 | let slice: &[u8] = slice::from_raw_parts(data:ptr.as_ptr() as *const u8, *len); |
| 1797 | std::str::from_utf8_unchecked(slice).into() |
| 1798 | }, |
| 1799 | Inner::Inline { len: &mut u8, data: &mut [u8; 22] } => unsafe { |
| 1800 | std::str::from_utf8_unchecked(data.get_unchecked(..*len as usize)).to_owned() |
| 1801 | }, |
| 1802 | } |
| 1803 | } |
| 1804 | } |
| 1805 | |
| 1806 | impl From<GString> for Box<str> { |
| 1807 | #[inline ] |
| 1808 | fn from(s: GString) -> Self { |
| 1809 | // Potentially creates a copy |
| 1810 | String::from(s).into() |
| 1811 | } |
| 1812 | } |
| 1813 | |
| 1814 | impl From<GString> for Vec<u8> { |
| 1815 | #[inline ] |
| 1816 | fn from(value: GString) -> Vec<u8> { |
| 1817 | value.into_bytes_with_nul() |
| 1818 | } |
| 1819 | } |
| 1820 | |
| 1821 | impl TryFrom<GString> for CString { |
| 1822 | type Error = GStringInteriorNulError<GString>; |
| 1823 | #[inline ] |
| 1824 | fn try_from(value: GString) -> Result<Self, Self::Error> { |
| 1825 | if let Some(nul_pos: usize) = memchr::memchr(needle:0, haystack:value.as_bytes()) { |
| 1826 | return Err(GStringInteriorNulError( |
| 1827 | value, |
| 1828 | GStrInteriorNulError(nul_pos), |
| 1829 | )); |
| 1830 | } |
| 1831 | let v: Vec = value.into_bytes_with_nul(); |
| 1832 | Ok(unsafe { CString::from_vec_with_nul_unchecked(v) }) |
| 1833 | } |
| 1834 | } |
| 1835 | |
| 1836 | impl From<GString> for OsString { |
| 1837 | #[inline ] |
| 1838 | fn from(s: GString) -> Self { |
| 1839 | OsString::from(String::from(s)) |
| 1840 | } |
| 1841 | } |
| 1842 | |
| 1843 | impl From<GString> for PathBuf { |
| 1844 | #[inline ] |
| 1845 | fn from(s: GString) -> Self { |
| 1846 | PathBuf::from(OsString::from(s)) |
| 1847 | } |
| 1848 | } |
| 1849 | |
| 1850 | impl From<String> for GString { |
| 1851 | #[inline ] |
| 1852 | fn from(mut s: String) -> Self { |
| 1853 | // Moves the content of the String |
| 1854 | if cfg!(debug_assertions) { |
| 1855 | GStr::check_interior_nuls(&s).unwrap(); |
| 1856 | } |
| 1857 | if s.is_empty() { |
| 1858 | Self::new() |
| 1859 | } else { |
| 1860 | s.reserve_exact(additional:1); |
| 1861 | s.push(ch:' \0' ); |
| 1862 | // No check for valid UTF-8 here |
| 1863 | Self(Inner::Native(s.into())) |
| 1864 | } |
| 1865 | } |
| 1866 | } |
| 1867 | |
| 1868 | impl From<Box<str>> for GString { |
| 1869 | #[inline ] |
| 1870 | fn from(s: Box<str>) -> Self { |
| 1871 | // Moves the content of the String |
| 1872 | s.into_string().into() |
| 1873 | } |
| 1874 | } |
| 1875 | |
| 1876 | impl<'a> From<Cow<'a, str>> for GString { |
| 1877 | #[inline ] |
| 1878 | fn from(s: Cow<'a, str>) -> Self { |
| 1879 | match s { |
| 1880 | Cow::Borrowed(s: &str) => Self::from(s), |
| 1881 | Cow::Owned(s: String) => Self::from(s), |
| 1882 | } |
| 1883 | } |
| 1884 | } |
| 1885 | |
| 1886 | impl From<&GStr> for GString { |
| 1887 | #[inline ] |
| 1888 | fn from(s: &GStr) -> GString { |
| 1889 | s.to_owned() |
| 1890 | } |
| 1891 | } |
| 1892 | |
| 1893 | impl From<&str> for GString { |
| 1894 | #[inline ] |
| 1895 | fn from(s: &str) -> Self { |
| 1896 | if cfg!(debug_assertions) { |
| 1897 | GStr::check_interior_nuls(s).unwrap(); |
| 1898 | } |
| 1899 | if s.len() < INLINE_LEN { |
| 1900 | let mut data = <[u8; INLINE_LEN]>::default(); |
| 1901 | let b = s.as_bytes(); |
| 1902 | unsafe { data.get_unchecked_mut(..b.len()) }.copy_from_slice(b); |
| 1903 | return Self(Inner::Inline { |
| 1904 | len: b.len() as u8, |
| 1905 | data, |
| 1906 | }); |
| 1907 | } |
| 1908 | // Allocates with the GLib allocator |
| 1909 | unsafe { |
| 1910 | // No check for valid UTF-8 here |
| 1911 | let copy = ffi::g_strndup(s.as_ptr() as *const c_char, s.len()); |
| 1912 | GString(Inner::Foreign { |
| 1913 | ptr: ptr::NonNull::new_unchecked(copy), |
| 1914 | len: s.len(), |
| 1915 | }) |
| 1916 | } |
| 1917 | } |
| 1918 | } |
| 1919 | |
| 1920 | impl From<&String> for GString { |
| 1921 | #[inline ] |
| 1922 | fn from(s: &String) -> Self { |
| 1923 | GString::from(s.as_str()) |
| 1924 | } |
| 1925 | } |
| 1926 | |
| 1927 | impl From<GStringPtr> for GString { |
| 1928 | #[inline ] |
| 1929 | fn from(s: GStringPtr) -> Self { |
| 1930 | let s: ManuallyDrop = mem::ManuallyDrop::new(s); |
| 1931 | let len: usize = unsafe { GStr::from_ptr(s.0.as_ptr()).len() }; |
| 1932 | GString(Inner::Foreign { ptr: s.0, len }) |
| 1933 | } |
| 1934 | } |
| 1935 | |
| 1936 | impl TryFrom<CString> for GString { |
| 1937 | type Error = GStringUtf8Error<CString>; |
| 1938 | #[inline ] |
| 1939 | fn try_from(value: CString) -> Result<Self, Self::Error> { |
| 1940 | if value.as_bytes().is_empty() { |
| 1941 | Ok(Self::new()) |
| 1942 | } else { |
| 1943 | // Moves the content of the CString |
| 1944 | // Also check if it's valid UTF-8 |
| 1945 | let s: String = String::from_utf8(value.into_bytes_with_nul()).map_err(|e: FromUtf8Error| { |
| 1946 | let err: Utf8Error = e.utf8_error(); |
| 1947 | GStringUtf8Error( |
| 1948 | unsafe { CString::from_vec_with_nul_unchecked(e.into_bytes()) }, |
| 1949 | err, |
| 1950 | ) |
| 1951 | })?; |
| 1952 | Ok(Self(Inner::Native(s.into()))) |
| 1953 | } |
| 1954 | } |
| 1955 | } |
| 1956 | |
| 1957 | impl TryFrom<OsString> for GString { |
| 1958 | type Error = GStringFromError<OsString>; |
| 1959 | #[inline ] |
| 1960 | fn try_from(value: OsString) -> Result<Self, Self::Error> { |
| 1961 | Self::from_string_checked(value.into_string().map_err(GStringFromError::Unspecified)?) |
| 1962 | .map_err(|e: GStringInteriorNulError| GStringFromError::from(e).convert(func:OsString::from)) |
| 1963 | } |
| 1964 | } |
| 1965 | |
| 1966 | impl TryFrom<PathBuf> for GString { |
| 1967 | type Error = GStringFromError<PathBuf>; |
| 1968 | #[inline ] |
| 1969 | fn try_from(value: PathBuf) -> Result<Self, Self::Error> { |
| 1970 | GString::try_from(value.into_os_string()).map_err(|e: GStringFromError| e.convert(func:PathBuf::from)) |
| 1971 | } |
| 1972 | } |
| 1973 | |
| 1974 | impl TryFrom<&CStr> for GString { |
| 1975 | type Error = std::str::Utf8Error; |
| 1976 | #[inline ] |
| 1977 | fn try_from(value: &CStr) -> Result<Self, Self::Error> { |
| 1978 | // Check if it's valid UTF-8 |
| 1979 | value.to_str()?; |
| 1980 | let gstr: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(bytes:value.to_bytes_with_nul()) }; |
| 1981 | Ok(gstr.to_owned()) |
| 1982 | } |
| 1983 | } |
| 1984 | |
| 1985 | impl<'a> From<Cow<'a, GStr>> for GString { |
| 1986 | #[inline ] |
| 1987 | fn from(s: Cow<'a, GStr>) -> Self { |
| 1988 | s.into_owned() |
| 1989 | } |
| 1990 | } |
| 1991 | |
| 1992 | impl<'a> From<&'a GString> for Cow<'a, GStr> { |
| 1993 | #[inline ] |
| 1994 | fn from(s: &'a GString) -> Self { |
| 1995 | Cow::Borrowed(s.as_gstr()) |
| 1996 | } |
| 1997 | } |
| 1998 | |
| 1999 | impl From<GString> for Cow<'_, GStr> { |
| 2000 | #[inline ] |
| 2001 | fn from(v: GString) -> Self { |
| 2002 | Cow::Owned(v) |
| 2003 | } |
| 2004 | } |
| 2005 | |
| 2006 | impl<'a> From<&'a GStr> for Cow<'a, GStr> { |
| 2007 | #[inline ] |
| 2008 | fn from(v: &'a GStr) -> Self { |
| 2009 | Cow::Borrowed(v) |
| 2010 | } |
| 2011 | } |
| 2012 | |
| 2013 | #[doc (hidden)] |
| 2014 | impl FromGlibPtrFull<*mut u8> for GString { |
| 2015 | #[inline ] |
| 2016 | unsafe fn from_glib_full(ptr: *mut u8) -> Self { |
| 2017 | debug_assert!(!ptr.is_null()); |
| 2018 | |
| 2019 | let cstr: &CStr = CStr::from_ptr(ptr as *const _); |
| 2020 | // Check for valid UTF-8 here |
| 2021 | debug_assert!(cstr.to_str().is_ok()); |
| 2022 | Self(Inner::Foreign { |
| 2023 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
| 2024 | len: cstr.to_bytes().len(), |
| 2025 | }) |
| 2026 | } |
| 2027 | } |
| 2028 | |
| 2029 | #[doc (hidden)] |
| 2030 | impl FromGlibPtrFull<*mut i8> for GString { |
| 2031 | #[inline ] |
| 2032 | unsafe fn from_glib_full(ptr: *mut i8) -> Self { |
| 2033 | from_glib_full(ptr as *mut u8) |
| 2034 | } |
| 2035 | } |
| 2036 | |
| 2037 | #[doc (hidden)] |
| 2038 | impl FromGlibPtrFull<*const u8> for GString { |
| 2039 | #[inline ] |
| 2040 | unsafe fn from_glib_full(ptr: *const u8) -> Self { |
| 2041 | from_glib_full(ptr as *mut u8) |
| 2042 | } |
| 2043 | } |
| 2044 | |
| 2045 | #[doc (hidden)] |
| 2046 | impl FromGlibPtrFull<*const i8> for GString { |
| 2047 | #[inline ] |
| 2048 | unsafe fn from_glib_full(ptr: *const i8) -> Self { |
| 2049 | from_glib_full(ptr as *mut u8) |
| 2050 | } |
| 2051 | } |
| 2052 | |
| 2053 | #[doc (hidden)] |
| 2054 | impl FromGlibPtrNone<*const u8> for GString { |
| 2055 | #[inline ] |
| 2056 | unsafe fn from_glib_none(ptr: *const u8) -> Self { |
| 2057 | debug_assert!(!ptr.is_null()); |
| 2058 | <&GStr>::from_glib_none(ptr).into() |
| 2059 | } |
| 2060 | } |
| 2061 | |
| 2062 | #[doc (hidden)] |
| 2063 | impl FromGlibPtrNone<*const i8> for GString { |
| 2064 | #[inline ] |
| 2065 | unsafe fn from_glib_none(ptr: *const i8) -> Self { |
| 2066 | from_glib_none(ptr as *const u8) |
| 2067 | } |
| 2068 | } |
| 2069 | |
| 2070 | #[doc (hidden)] |
| 2071 | impl FromGlibPtrNone<*mut u8> for GString { |
| 2072 | #[inline ] |
| 2073 | unsafe fn from_glib_none(ptr: *mut u8) -> Self { |
| 2074 | from_glib_none(ptr as *const u8) |
| 2075 | } |
| 2076 | } |
| 2077 | |
| 2078 | #[doc (hidden)] |
| 2079 | impl FromGlibPtrNone<*mut i8> for GString { |
| 2080 | #[inline ] |
| 2081 | unsafe fn from_glib_none(ptr: *mut i8) -> Self { |
| 2082 | from_glib_none(ptr as *const u8) |
| 2083 | } |
| 2084 | } |
| 2085 | |
| 2086 | #[doc (hidden)] |
| 2087 | impl FromGlibPtrBorrow<*const u8> for GString { |
| 2088 | #[inline ] |
| 2089 | unsafe fn from_glib_borrow(ptr: *const u8) -> Borrowed<Self> { |
| 2090 | debug_assert!(!ptr.is_null()); |
| 2091 | |
| 2092 | // Check for valid UTF-8 here |
| 2093 | let cstr: &CStr = CStr::from_ptr(ptr as *const _); |
| 2094 | debug_assert!(cstr.to_str().is_ok()); |
| 2095 | Borrowed::new(Self(Inner::Foreign { |
| 2096 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
| 2097 | len: cstr.to_bytes().len(), |
| 2098 | })) |
| 2099 | } |
| 2100 | } |
| 2101 | |
| 2102 | #[doc (hidden)] |
| 2103 | impl FromGlibPtrBorrow<*const i8> for GString { |
| 2104 | #[inline ] |
| 2105 | unsafe fn from_glib_borrow(ptr: *const i8) -> Borrowed<Self> { |
| 2106 | from_glib_borrow(ptr as *const u8) |
| 2107 | } |
| 2108 | } |
| 2109 | |
| 2110 | #[doc (hidden)] |
| 2111 | impl FromGlibPtrBorrow<*mut u8> for GString { |
| 2112 | #[inline ] |
| 2113 | unsafe fn from_glib_borrow(ptr: *mut u8) -> Borrowed<Self> { |
| 2114 | from_glib_borrow(ptr as *const u8) |
| 2115 | } |
| 2116 | } |
| 2117 | |
| 2118 | #[doc (hidden)] |
| 2119 | impl FromGlibPtrBorrow<*mut i8> for GString { |
| 2120 | #[inline ] |
| 2121 | unsafe fn from_glib_borrow(ptr: *mut i8) -> Borrowed<Self> { |
| 2122 | from_glib_borrow(ptr as *const u8) |
| 2123 | } |
| 2124 | } |
| 2125 | |
| 2126 | #[allow (clippy::unnecessary_cast)] |
| 2127 | #[doc (hidden)] |
| 2128 | impl<'a> ToGlibPtr<'a, *const u8> for GString { |
| 2129 | type Storage = PhantomData<&'a Self>; |
| 2130 | |
| 2131 | #[inline ] |
| 2132 | fn to_glib_none(&'a self) -> Stash<'a, *const u8, Self> { |
| 2133 | Stash(self.as_ptr() as *const u8, PhantomData) |
| 2134 | } |
| 2135 | |
| 2136 | #[inline ] |
| 2137 | fn to_glib_full(&self) -> *const u8 { |
| 2138 | unsafe { self.clone().into_glib_ptr() as *const u8 } |
| 2139 | } |
| 2140 | } |
| 2141 | |
| 2142 | #[allow (clippy::unnecessary_cast)] |
| 2143 | #[doc (hidden)] |
| 2144 | impl<'a> ToGlibPtr<'a, *const i8> for GString { |
| 2145 | type Storage = PhantomData<&'a Self>; |
| 2146 | |
| 2147 | #[inline ] |
| 2148 | fn to_glib_none(&'a self) -> Stash<'a, *const i8, Self> { |
| 2149 | Stash(self.as_ptr() as *const i8, PhantomData) |
| 2150 | } |
| 2151 | |
| 2152 | #[inline ] |
| 2153 | fn to_glib_full(&self) -> *const i8 { |
| 2154 | unsafe { self.clone().into_glib_ptr() as *const i8 } |
| 2155 | } |
| 2156 | } |
| 2157 | |
| 2158 | #[allow (clippy::unnecessary_cast)] |
| 2159 | #[doc (hidden)] |
| 2160 | impl<'a> ToGlibPtr<'a, *mut u8> for GString { |
| 2161 | type Storage = PhantomData<&'a Self>; |
| 2162 | |
| 2163 | #[inline ] |
| 2164 | fn to_glib_none(&'a self) -> Stash<'a, *mut u8, Self> { |
| 2165 | Stash(self.as_ptr() as *mut u8, PhantomData) |
| 2166 | } |
| 2167 | |
| 2168 | #[inline ] |
| 2169 | fn to_glib_full(&self) -> *mut u8 { |
| 2170 | unsafe { self.clone().into_glib_ptr() as *mut u8 } |
| 2171 | } |
| 2172 | } |
| 2173 | |
| 2174 | #[allow (clippy::unnecessary_cast)] |
| 2175 | #[doc (hidden)] |
| 2176 | impl<'a> ToGlibPtr<'a, *mut i8> for GString { |
| 2177 | type Storage = PhantomData<&'a Self>; |
| 2178 | |
| 2179 | #[inline ] |
| 2180 | fn to_glib_none(&'a self) -> Stash<'a, *mut i8, Self> { |
| 2181 | Stash(self.as_ptr() as *mut i8, PhantomData) |
| 2182 | } |
| 2183 | |
| 2184 | #[inline ] |
| 2185 | fn to_glib_full(&self) -> *mut i8 { |
| 2186 | unsafe { self.clone().into_glib_ptr() as *mut i8 } |
| 2187 | } |
| 2188 | } |
| 2189 | |
| 2190 | #[doc (hidden)] |
| 2191 | impl FromGlibContainer<*const c_char, *const i8> for GString { |
| 2192 | unsafe fn from_glib_none_num(ptr: *const i8, num: usize) -> Self { |
| 2193 | if num == 0 || ptr.is_null() { |
| 2194 | return Self::default(); |
| 2195 | } |
| 2196 | let slice = slice::from_raw_parts(ptr as *const u8, num); |
| 2197 | if cfg!(debug_assertions) { |
| 2198 | // Also check if it's valid UTF-8 |
| 2199 | std::str::from_utf8(slice).unwrap().into() |
| 2200 | } else { |
| 2201 | std::str::from_utf8_unchecked(slice).into() |
| 2202 | } |
| 2203 | } |
| 2204 | |
| 2205 | unsafe fn from_glib_container_num(ptr: *const i8, num: usize) -> Self { |
| 2206 | if num == 0 || ptr.is_null() { |
| 2207 | return Self::default(); |
| 2208 | } |
| 2209 | |
| 2210 | if cfg!(debug_assertions) { |
| 2211 | // Check if it's valid UTF-8 |
| 2212 | let slice = slice::from_raw_parts(ptr as *const u8, num); |
| 2213 | std::str::from_utf8(slice).unwrap(); |
| 2214 | } |
| 2215 | |
| 2216 | GString(Inner::Foreign { |
| 2217 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
| 2218 | len: num, |
| 2219 | }) |
| 2220 | } |
| 2221 | |
| 2222 | unsafe fn from_glib_full_num(ptr: *const i8, num: usize) -> Self { |
| 2223 | if num == 0 || ptr.is_null() { |
| 2224 | return Self::default(); |
| 2225 | } |
| 2226 | |
| 2227 | if cfg!(debug_assertions) { |
| 2228 | // Check if it's valid UTF-8 |
| 2229 | let slice = slice::from_raw_parts(ptr as *const u8, num); |
| 2230 | std::str::from_utf8(slice).unwrap(); |
| 2231 | } |
| 2232 | |
| 2233 | GString(Inner::Foreign { |
| 2234 | ptr: ptr::NonNull::new_unchecked(ptr as *mut _), |
| 2235 | len: num, |
| 2236 | }) |
| 2237 | } |
| 2238 | } |
| 2239 | |
| 2240 | #[doc (hidden)] |
| 2241 | impl FromGlibContainer<*const c_char, *mut i8> for GString { |
| 2242 | unsafe fn from_glib_none_num(ptr: *mut i8, num: usize) -> Self { |
| 2243 | FromGlibContainer::from_glib_none_num(ptr as *const i8, num) |
| 2244 | } |
| 2245 | |
| 2246 | unsafe fn from_glib_container_num(ptr: *mut i8, num: usize) -> Self { |
| 2247 | FromGlibContainer::from_glib_container_num(ptr as *const i8, num) |
| 2248 | } |
| 2249 | |
| 2250 | unsafe fn from_glib_full_num(ptr: *mut i8, num: usize) -> Self { |
| 2251 | FromGlibContainer::from_glib_full_num(ptr as *const i8, num) |
| 2252 | } |
| 2253 | } |
| 2254 | |
| 2255 | #[doc (hidden)] |
| 2256 | impl FromGlibContainer<*const c_char, *const u8> for GString { |
| 2257 | unsafe fn from_glib_none_num(ptr: *const u8, num: usize) -> Self { |
| 2258 | FromGlibContainer::from_glib_none_num(ptr as *const i8, num) |
| 2259 | } |
| 2260 | |
| 2261 | unsafe fn from_glib_container_num(ptr: *const u8, num: usize) -> Self { |
| 2262 | FromGlibContainer::from_glib_container_num(ptr as *const i8, num) |
| 2263 | } |
| 2264 | |
| 2265 | unsafe fn from_glib_full_num(ptr: *const u8, num: usize) -> Self { |
| 2266 | FromGlibContainer::from_glib_full_num(ptr as *const i8, num) |
| 2267 | } |
| 2268 | } |
| 2269 | |
| 2270 | #[doc (hidden)] |
| 2271 | impl FromGlibContainer<*const c_char, *mut u8> for GString { |
| 2272 | unsafe fn from_glib_none_num(ptr: *mut u8, num: usize) -> Self { |
| 2273 | FromGlibContainer::from_glib_none_num(ptr as *const i8, num) |
| 2274 | } |
| 2275 | |
| 2276 | unsafe fn from_glib_container_num(ptr: *mut u8, num: usize) -> Self { |
| 2277 | FromGlibContainer::from_glib_container_num(ptr as *const i8, num) |
| 2278 | } |
| 2279 | |
| 2280 | unsafe fn from_glib_full_num(ptr: *mut u8, num: usize) -> Self { |
| 2281 | FromGlibContainer::from_glib_full_num(ptr as *const i8, num) |
| 2282 | } |
| 2283 | } |
| 2284 | |
| 2285 | impl GlibPtrDefault for GString { |
| 2286 | type GlibType = *const c_char; |
| 2287 | } |
| 2288 | |
| 2289 | impl StaticType for GString { |
| 2290 | #[inline ] |
| 2291 | fn static_type() -> Type { |
| 2292 | String::static_type() |
| 2293 | } |
| 2294 | } |
| 2295 | |
| 2296 | impl crate::value::ValueType for GString { |
| 2297 | type Type = String; |
| 2298 | } |
| 2299 | |
| 2300 | impl crate::value::ValueTypeOptional for GString {} |
| 2301 | |
| 2302 | unsafe impl<'a> crate::value::FromValue<'a> for GString { |
| 2303 | type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>; |
| 2304 | |
| 2305 | #[inline ] |
| 2306 | unsafe fn from_value(value: &'a Value) -> Self { |
| 2307 | Self::from(<&str>::from_value(value)) |
| 2308 | } |
| 2309 | } |
| 2310 | |
| 2311 | impl crate::value::ToValue for GString { |
| 2312 | #[inline ] |
| 2313 | fn to_value(&self) -> Value { |
| 2314 | <&str>::to_value(&self.as_str()) |
| 2315 | } |
| 2316 | |
| 2317 | #[inline ] |
| 2318 | fn value_type(&self) -> Type { |
| 2319 | String::static_type() |
| 2320 | } |
| 2321 | } |
| 2322 | |
| 2323 | impl crate::value::ToValueOptional for GString { |
| 2324 | #[inline ] |
| 2325 | fn to_value_optional(s: Option<&Self>) -> Value { |
| 2326 | <str>::to_value_optional(s.as_ref().map(|s: &&GString| s.as_str())) |
| 2327 | } |
| 2328 | } |
| 2329 | |
| 2330 | impl From<GString> for Value { |
| 2331 | #[inline ] |
| 2332 | fn from(s: GString) -> Self { |
| 2333 | unsafe { |
| 2334 | let mut value: Value = Value::for_value_type::<GString>(); |
| 2335 | gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, v_string:s.into_glib_ptr()); |
| 2336 | value |
| 2337 | } |
| 2338 | } |
| 2339 | } |
| 2340 | |
| 2341 | impl StaticType for Vec<GString> { |
| 2342 | #[inline ] |
| 2343 | fn static_type() -> Type { |
| 2344 | <Vec<String>>::static_type() |
| 2345 | } |
| 2346 | } |
| 2347 | |
| 2348 | impl crate::value::ValueType for Vec<GString> { |
| 2349 | type Type = Vec<GString>; |
| 2350 | } |
| 2351 | |
| 2352 | unsafe impl<'a> crate::value::FromValue<'a> for Vec<GString> { |
| 2353 | type Checker = crate::value::GenericValueTypeChecker<Self>; |
| 2354 | |
| 2355 | #[inline ] |
| 2356 | unsafe fn from_value(value: &'a Value) -> Self { |
| 2357 | let ptr: *const *const {unknown} = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char; |
| 2358 | FromGlibPtrContainer::from_glib_none(ptr) |
| 2359 | } |
| 2360 | } |
| 2361 | |
| 2362 | impl ToValue for Vec<GString> { |
| 2363 | #[inline ] |
| 2364 | fn to_value(&self) -> Value { |
| 2365 | unsafe { |
| 2366 | let mut value: Value = Value::for_value_type::<Self>(); |
| 2367 | let ptr: *mut *mut c_char = self.to_glib_full(); |
| 2368 | gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, v_boxed:ptr as *const c_void); |
| 2369 | value |
| 2370 | } |
| 2371 | } |
| 2372 | |
| 2373 | #[inline ] |
| 2374 | fn value_type(&self) -> Type { |
| 2375 | <Vec<GString>>::static_type() |
| 2376 | } |
| 2377 | } |
| 2378 | |
| 2379 | impl From<Vec<GString>> for Value { |
| 2380 | #[inline ] |
| 2381 | fn from(mut v: Vec<GString>) -> Self { |
| 2382 | unsafe { |
| 2383 | let mut value: Value = Value::for_value_type::<Vec<GString>>(); |
| 2384 | let container: (*mut *mut i8, (Vec>, …)) = |
| 2385 | ToGlibContainerFromSlice::<*mut *mut c_char>::to_glib_container_from_slice(&v); |
| 2386 | gobject_ffi::g_value_take_boxed( |
| 2387 | value.to_glib_none_mut().0, |
| 2388 | v_boxed:container.0 as *const c_void, |
| 2389 | ); |
| 2390 | v.set_len(new_len:0); |
| 2391 | value |
| 2392 | } |
| 2393 | } |
| 2394 | } |
| 2395 | |
| 2396 | impl_from_glib_container_as_vec_string!(GString, *const c_char); |
| 2397 | impl_from_glib_container_as_vec_string!(GString, *mut c_char); |
| 2398 | |
| 2399 | // rustdoc-stripper-ignore-next |
| 2400 | /// A trait to accept both <code>&[str]</code> or <code>&[GStr]</code> as an argument. |
| 2401 | pub trait IntoGStr { |
| 2402 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T; |
| 2403 | } |
| 2404 | |
| 2405 | impl IntoGStr for &GStr { |
| 2406 | #[inline ] |
| 2407 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
| 2408 | f(self) |
| 2409 | } |
| 2410 | } |
| 2411 | |
| 2412 | impl IntoGStr for GString { |
| 2413 | #[inline ] |
| 2414 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
| 2415 | f(self.as_gstr()) |
| 2416 | } |
| 2417 | } |
| 2418 | |
| 2419 | impl IntoGStr for &GString { |
| 2420 | #[inline ] |
| 2421 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
| 2422 | f(self.as_gstr()) |
| 2423 | } |
| 2424 | } |
| 2425 | |
| 2426 | // Limit borrowed from rust std CStr optimization: |
| 2427 | // https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/small_c_string.rs#L10 |
| 2428 | const MAX_STACK_ALLOCATION: usize = 384; |
| 2429 | |
| 2430 | impl IntoGStr for &str { |
| 2431 | #[inline ] |
| 2432 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
| 2433 | if self.len() < MAX_STACK_ALLOCATION { |
| 2434 | let mut s: MaybeUninit<[u8; 384]> = mem::MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); |
| 2435 | let ptr: *mut u8 = s.as_mut_ptr() as *mut u8; |
| 2436 | let gs: &GStr = unsafe { |
| 2437 | ptr::copy_nonoverlapping(self.as_ptr(), dst:ptr, self.len()); |
| 2438 | ptr.add(self.len()).write(val:0); |
| 2439 | GStr::from_utf8_with_nul_unchecked(bytes:slice::from_raw_parts(data:ptr, self.len() + 1)) |
| 2440 | }; |
| 2441 | f(gs) |
| 2442 | } else { |
| 2443 | f(GString::from(self).as_gstr()) |
| 2444 | } |
| 2445 | } |
| 2446 | } |
| 2447 | |
| 2448 | impl IntoGStr for String { |
| 2449 | #[inline ] |
| 2450 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(mut self, f: F) -> T { |
| 2451 | let len: usize = self.len(); |
| 2452 | if len < self.capacity() { |
| 2453 | self.reserve_exact(additional:1); |
| 2454 | self.push(ch:' \0' ); |
| 2455 | let gs: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(self.as_bytes()) }; |
| 2456 | f(gs) |
| 2457 | } else if len < MAX_STACK_ALLOCATION { |
| 2458 | self.as_str().run_with_gstr(f) |
| 2459 | } else { |
| 2460 | f(GString::from(self).as_gstr()) |
| 2461 | } |
| 2462 | } |
| 2463 | } |
| 2464 | |
| 2465 | impl IntoGStr for &String { |
| 2466 | #[inline ] |
| 2467 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T { |
| 2468 | self.as_str().run_with_gstr(f) |
| 2469 | } |
| 2470 | } |
| 2471 | |
| 2472 | pub const NONE_STR: Option<&'static str> = None; |
| 2473 | |
| 2474 | // rustdoc-stripper-ignore-next |
| 2475 | /// A trait to accept both <code>[Option]<&[str]></code> or <code>[Option]<&[GStr]></code> as |
| 2476 | /// an argument. |
| 2477 | pub trait IntoOptionalGStr { |
| 2478 | fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T; |
| 2479 | } |
| 2480 | |
| 2481 | impl<S: IntoGStr> IntoOptionalGStr for Option<S> { |
| 2482 | #[inline ] |
| 2483 | fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T { |
| 2484 | match self { |
| 2485 | Some(t: S) => t.run_with_gstr(|s: &GStr| f(Some(s))), |
| 2486 | None => f(None), |
| 2487 | } |
| 2488 | } |
| 2489 | } |
| 2490 | |
| 2491 | #[cfg (test)] |
| 2492 | #[allow (clippy::disallowed_names)] |
| 2493 | mod tests { |
| 2494 | use std::ffi::CString; |
| 2495 | |
| 2496 | use super::*; |
| 2497 | |
| 2498 | #[test ] |
| 2499 | fn test_gstring() { |
| 2500 | let data = CString::new("foo" ).unwrap(); |
| 2501 | let ptr = data.as_ptr(); |
| 2502 | |
| 2503 | unsafe { |
| 2504 | let ptr_copy = ffi::g_strdup(ptr); |
| 2505 | let gstring = GString::from_glib_full(ptr_copy); |
| 2506 | assert_eq!(gstring.as_str(), "foo" ); |
| 2507 | let foo: Box<str> = gstring.into(); |
| 2508 | assert_eq!(foo.as_ref(), "foo" ); |
| 2509 | } |
| 2510 | } |
| 2511 | |
| 2512 | #[test ] |
| 2513 | fn test_owned_glib_string() { |
| 2514 | let data = CString::new("foo" ).unwrap(); |
| 2515 | let ptr = data.as_ptr(); |
| 2516 | unsafe { |
| 2517 | let ptr_copy = ffi::g_strdup(ptr); |
| 2518 | let gstr = GString::from_glib_full(ptr_copy); |
| 2519 | assert_eq!(gstr, "foo" ); |
| 2520 | } |
| 2521 | } |
| 2522 | |
| 2523 | #[test ] |
| 2524 | fn test_gstring_from_str() { |
| 2525 | let gstring: GString = "foo" .into(); |
| 2526 | assert_eq!(gstring.as_str(), "foo" ); |
| 2527 | let foo: Box<str> = gstring.into(); |
| 2528 | assert_eq!(foo.as_ref(), "foo" ); |
| 2529 | } |
| 2530 | |
| 2531 | #[test ] |
| 2532 | fn test_string_from_gstring() { |
| 2533 | let gstring = GString::from("foo" ); |
| 2534 | assert_eq!(gstring.as_str(), "foo" ); |
| 2535 | let s = String::from(gstring); |
| 2536 | assert_eq!(s, "foo" ); |
| 2537 | } |
| 2538 | |
| 2539 | #[test ] |
| 2540 | fn test_gstring_from_cstring() { |
| 2541 | let cstr = CString::new("foo" ).unwrap(); |
| 2542 | let gstring = GString::try_from(cstr).unwrap(); |
| 2543 | assert_eq!(gstring.as_str(), "foo" ); |
| 2544 | let foo: Box<str> = gstring.into(); |
| 2545 | assert_eq!(foo.as_ref(), "foo" ); |
| 2546 | } |
| 2547 | |
| 2548 | #[test ] |
| 2549 | fn test_string_from_gstring_from_cstring() { |
| 2550 | let cstr = CString::new("foo" ).unwrap(); |
| 2551 | let gstring = GString::try_from(cstr).unwrap(); |
| 2552 | assert_eq!(gstring.as_str(), "foo" ); |
| 2553 | let s = String::from(gstring); |
| 2554 | assert_eq!(s, "foo" ); |
| 2555 | } |
| 2556 | |
| 2557 | #[test ] |
| 2558 | fn test_vec_u8_to_gstring() { |
| 2559 | let v: &[u8] = b"foo" ; |
| 2560 | let s: GString = GString::from_utf8(Vec::from(v)).unwrap(); |
| 2561 | assert_eq!(s.as_str(), "foo" ); |
| 2562 | } |
| 2563 | |
| 2564 | #[test ] |
| 2565 | fn test_as_ref_path() { |
| 2566 | fn foo<P: AsRef<Path>>(_path: P) {} |
| 2567 | let gstring: GString = "/my/path/" .into(); |
| 2568 | let gstr: &GStr = gstring.as_gstr(); |
| 2569 | foo(gstr); |
| 2570 | foo(gstring); |
| 2571 | } |
| 2572 | |
| 2573 | #[test ] |
| 2574 | fn test_from_glib_container() { |
| 2575 | unsafe { |
| 2576 | let test_a: GString = FromGlibContainer::from_glib_container_num( |
| 2577 | ffi::g_strdup("hello_world \0" .as_ptr() as *const _), |
| 2578 | 5, |
| 2579 | ); |
| 2580 | assert_eq!("hello" , test_a.as_str()); |
| 2581 | |
| 2582 | let test_b: GString = FromGlibContainer::from_glib_none_num("hello_world" .as_ptr(), 5); |
| 2583 | assert_eq!("hello" , test_b.as_str()); |
| 2584 | |
| 2585 | let test_c: GString = |
| 2586 | FromGlibContainer::from_glib_none_num(std::ptr::null::<std::os::raw::c_char>(), 0); |
| 2587 | assert_eq!("" , test_c.as_str()); |
| 2588 | |
| 2589 | let test_d: GString = FromGlibContainer::from_glib_none_num("" .as_ptr(), 0); |
| 2590 | assert_eq!("" , test_d.as_str()); |
| 2591 | |
| 2592 | let test_e: GString = |
| 2593 | FromGlibContainer::from_glib_container_num(ffi::g_strdup(std::ptr::null()), 0); |
| 2594 | assert_eq!("" , test_e.as_str()); |
| 2595 | } |
| 2596 | } |
| 2597 | |
| 2598 | #[test ] |
| 2599 | fn test_hashmap() { |
| 2600 | use std::collections::HashMap; |
| 2601 | |
| 2602 | let gstring = GString::from("foo" ); |
| 2603 | assert_eq!(gstring.as_str(), "foo" ); |
| 2604 | let mut h: HashMap<GString, i32> = HashMap::new(); |
| 2605 | h.insert(gstring, 42); |
| 2606 | let gstring: GString = "foo" .into(); |
| 2607 | assert!(h.contains_key(&gstring)); |
| 2608 | } |
| 2609 | |
| 2610 | #[test ] |
| 2611 | fn test_gstring_from_ptr_lossy() { |
| 2612 | let data = CString::new("foo" ).unwrap(); |
| 2613 | let ptr = data.as_ptr(); |
| 2614 | |
| 2615 | unsafe { |
| 2616 | let gstring = GString::from_ptr_lossy(ptr); |
| 2617 | assert_eq!(gstring.as_str(), "foo" ); |
| 2618 | assert_eq!(ptr, gstring.as_ptr()); |
| 2619 | } |
| 2620 | |
| 2621 | let data = b"foo \xF0\x90\x80bar \0" ; |
| 2622 | let ptr = data.as_ptr(); |
| 2623 | |
| 2624 | unsafe { |
| 2625 | let gstring = GString::from_ptr_lossy(ptr as *const _); |
| 2626 | assert_eq!(gstring.as_str(), "foo���bar" ); |
| 2627 | assert_ne!(ptr, gstring.as_ptr() as *const _); |
| 2628 | } |
| 2629 | } |
| 2630 | |
| 2631 | #[test ] |
| 2632 | fn gformat() { |
| 2633 | let s = gformat!("bla bla {} bla" , 123); |
| 2634 | assert_eq!(s, "bla bla 123 bla" ); |
| 2635 | } |
| 2636 | |
| 2637 | #[test ] |
| 2638 | fn layout() { |
| 2639 | // ensure the inline variant is not wider than the other variants |
| 2640 | enum NoInline { |
| 2641 | _Native(Box<str>), |
| 2642 | _Foreign(ptr::NonNull<c_char>, usize), |
| 2643 | } |
| 2644 | assert_eq!(mem::size_of::<GString>(), mem::size_of::<NoInline>()); |
| 2645 | assert_eq!(mem::size_of::<GString>(), mem::size_of::<String>()); |
| 2646 | } |
| 2647 | } |
| 2648 | |