use std::path::{Path, PathBuf}; use crate::*; #[derive(PartialEq)] pub enum EntryType { File, Directory, } pub struct Entry { pub entry_type: EntryType, pub is_symlink: bool, /// The final segment of the file path, including any file extensions. pub name: String, pub extension: String, /// The canonical file path, with intermediate symbolic links resolved. pub path: PathBuf, /// The file path as originally presented to the [Entry] constructor. pub original_path: PathBuf, } impl Entry { pub fn from_path(path: impl AsRef) -> ReadResult { let path = path.as_ref(); macro_rules! raise { ($err:expr) => { io_result_to_read_result($err, path)? }; } let metadata = raise!(path.metadata()); let canonical_path = raise!(std::fs::canonicalize(path)); let canonical_metadata = raise!(canonical_path.metadata()); let entry_type = if canonical_metadata.is_file() { EntryType::File } else if canonical_metadata.is_dir() { EntryType::Directory } else { unreachable!("Canonical metadata must describe either a file or a directory"); }; let name = match path.file_name() { Some(os_str) => os_str.to_string_lossy().to_string(), None => path.to_string_lossy().to_string(), }; let extension = match path.extension() { Some(extension) => extension.to_string_lossy().into(), None => String::default(), }; Ok(Entry { entry_type, name, extension, path: canonical_path, original_path: path.to_path_buf(), is_symlink: metadata.is_symlink(), }) } pub fn parent(&self) -> Option { Entry::from_path(self.original_path.parent()?).ok() } /// Split the filename on the last period, ignoring any period at the /// start of the filename. If no extension is found, the extension is empty. pub fn split_name(&self) -> (String, String) { match self.name.rsplit_once(".") { Some(("", _)) | None => (self.name.to_string(), String::new()), Some((prefix, extension)) => (prefix.to_string(), extension.to_string()), } } pub fn is_file(&self) -> bool { self.entry_type == EntryType::File } pub fn is_directory(&self) -> bool { self.entry_type == EntryType::Directory } pub fn read_as_bytes(&self) -> ReadResult> { Ok(io_result_to_read_result(std::fs::read(&self.path), &self.path)?) } pub fn read_as_utf8(&self) -> ReadResult { return Ok(String::from_utf8_lossy(&self.read_as_bytes()?).to_string()); } } impl AsRef for Entry { fn as_ref(&self) -> &Path { &self.original_path } }