mod fs;
use std::path::{Component, Path, PathBuf};
use anyhow::Result;
use tokio::fs::OpenOptions;
use self::fs::directory::Directory;
use self::fs::file::File;
#[derive(Debug)]
pub enum Entry {
File(Box<File>),
Directory(Directory),
}
pub struct FileExplorer {
root: PathBuf,
}
impl FileExplorer {
pub fn new(root: PathBuf) -> Self {
Self { root }
}
pub async fn peek(&self, path: PathBuf) -> Result<Entry> {
let relative_path = self.build_relative_path(path);
self.open(relative_path).await
}
fn build_relative_path(&self, path: PathBuf) -> PathBuf {
let mut root = self.root.clone();
root.extend(&self.normalize_path(&path));
root
}
fn normalize_path(&self, path: &Path) -> PathBuf {
path.components()
.fold(PathBuf::new(), |mut result, p| match p {
Component::ParentDir => {
result.pop();
result
}
Component::Normal(os_string) => {
result.push(os_string);
result
}
_ => result,
})
}
#[cfg(not(target_os = "windows"))]
async fn open(&self, path: PathBuf) -> Result<Entry> {
let mut open_options = OpenOptions::new();
let entry_path: PathBuf = path.clone();
let file = open_options.read(true).open(path).await?;
let metadata = file.metadata().await?;
if metadata.is_dir() {
return Ok(Entry::Directory(Directory { path: entry_path }));
}
Ok(Entry::File(Box::new(File::new(entry_path, file, metadata))))
}
#[cfg(target_os = "windows")]
async fn open(&self, path: PathBuf) -> Result<Entry> {
const FILE_FLAG_BACKUP_SEMANTICS: u32 = 0x02000000;
let mut open_options = OpenOptions::new();
let entry_path: PathBuf = path.clone();
let file = open_options
.read(true)
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
.open(path)
.await?;
let metadata = file.metadata().await?;
if metadata.is_dir() {
return Ok(Entry::Directory(Directory { path: entry_path }));
}
Ok(Entry::File(Box::new(File::new(entry_path, file, metadata))))
}
}