use std::{borrow::Cow, fmt::Display, path::Path}; use crate::CURRENT_DIRECTORY; /// Converts invalid UTF-8 bytes to the Unicode replacement codepoint (�) in its Display implementation. pub struct EscapedPathDisplay<'a> { path: &'a Path, } impl<'a> EscapedPathDisplay<'a> { pub fn new(path: &'a Path) -> Self { Self { path } } } #[cfg(unix)] impl Display for EscapedPathDisplay<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use std::os::unix::prelude::OsStrExt; let bstr = bstr::BStr::new(self.path.as_os_str().as_bytes()); write!(f, "{}", bstr) } } #[cfg(windows)] impl Display for EscapedPathDisplay<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use std::{char, fmt::Write, os::windows::prelude::OsStrExt}; let utf16 = self.path.as_os_str().encode_wide(); let chars = char::decode_utf16(utf16).map(|decoded| decoded.unwrap_or(char::REPLACEMENT_CHARACTER)); for char in chars { f.write_char(char)?; } Ok(()) } } /// Converts an OsStr to utf8 with custom formatting. /// /// This is different from [`Path::display`]. /// /// See for a comparison. pub fn to_utf(os_str: &Path) -> Cow { let format = || { let text = format!("{:?}", os_str); Cow::Owned(text.trim_matches('"').to_string()) }; os_str.to_str().map_or_else(format, Cow::Borrowed) } /// Removes the current dir from the beginning of a path as it's redundant information, /// useful for presentation sake. pub fn strip_cur_dir(source_path: &Path) -> &Path { let current_dir = &*CURRENT_DIRECTORY; source_path.strip_prefix(current_dir).unwrap_or(source_path) } /// Converts a slice of AsRef to comma separated String /// /// Panics if the slice is empty. pub fn pretty_format_list_of_paths(os_strs: &[impl AsRef]) -> String { let mut iter = os_strs.iter().map(AsRef::as_ref); let first_element = iter.next().unwrap(); let mut string = to_utf(first_element).into_owned(); for os_str in iter { string += ", "; string += &to_utf(os_str); } string } /// Display the directory name, but use "current directory" when necessary. pub fn nice_directory_display(path: &Path) -> Cow { if path == Path::new(".") { Cow::Borrowed("current directory") } else { to_utf(path) } }