/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::{borrow::Borrow, io, path::PathBuf}; /// A representation of a path as a wide character sequence that can /// contain unpaired surrogates. /// /// A [`WidePathBuf`] can be created from an [`nsstring::nsAString`], /// and copied into an `[nsstring::nsString]`, without re-encoding. /// Unlike `ns{A}String`, [`WidePathBuf`] implements the built-in /// comparison and hashing traits, so it can be used as a key in /// a `HashMap` or a `BTreeMap`. #[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] pub struct WidePathBuf(Vec); impl WidePathBuf { /// Creates a [`WidePathBuf`] from a wide character sequence. pub fn new(wide: impl AsRef<[u16]>) -> Self { Self(wide.as_ref().into()) } /// Returns the wide character sequence in this [`WidePathBuf`]. pub fn as_wide(&self) -> &[u16] { &self.0 } /// Returns the canonical, absolute form of a path, normalizing /// `/./` and `/../` components, symbolic links, and /// {drive, directory}-relative paths. /// /// ### Notes /// /// This function calls [`std::fs::canonicalize`] on Windows, and /// reimplements [`std::fs::canonicalize`] on Unix platforms /// to work around an [allocator crash][1]. /// /// [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1531887 pub fn canonicalize(&self) -> io::Result { #[cfg(windows)] { use std::{ffi::OsString, os::windows::prelude::*}; std::fs::canonicalize(OsString::from_wide(&*self.0)) } #[cfg(unix)] { use std::{ ffi::{CStr, CString, OsString}, os::unix::prelude::*, }; let path = CString::new(String::from_utf16(&*self.0).map_err(io::Error::other)?)?; let mut bytes = [0 as libc::c_char; libc::PATH_MAX as usize]; let ptr = unsafe { libc::realpath(path.as_ptr(), bytes[..].as_mut_ptr()) }; if ptr.is_null() { return Err(io::Error::last_os_error()); } Ok(OsString::from_vec(unsafe { CStr::from_ptr(ptr) }.to_bytes().into()).into()) } #[cfg(all(not(unix), not(windows)))] compile_error!("`WidePathBuf::canonicalize` requires Windows or Unix") } } // Convenience implementation for comparing a [`WidePathBuf`] to a Rust // `String` or `str`. impl PartialEq for WidePathBuf where T: Borrow, { fn eq(&self, other: &T) -> bool { other .borrow() .encode_utf16() .eq(self.as_wide().iter().copied()) } }