1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
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<Path>) -> ReadResult<Self> {
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> {
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<Vec<u8>> {
Ok(io_result_to_read_result(std::fs::read(&self.path), &self.path)?)
}
pub fn read_as_utf8(&self) -> ReadResult<String> {
return Ok(String::from_utf8_lossy(&self.read_as_bytes()?).to_string());
}
}
impl AsRef<Path> for Entry {
fn as_ref(&self) -> &Path {
&self.original_path
}
}
|