use std::{
fs::Metadata,
io,
path::{Path, PathBuf},
};
use tokio::fs::{self};
pub fn get_basename_ext_name(path: &str) -> (String, String) {
let path = path.strip_prefix("./").unwrap_or(path);
let (basename, ext) = path.split_at(path.rfind('.').unwrap_or(path.len()));
(basename.to_string(), ext.to_string())
}
pub static JS_EXTENSIONS: &[&str] = &[".js", ".mjs", ".cjs"];
pub fn get_js_path(path: &str) -> Option<PathBuf> {
let (mut basename, ext) = get_basename_ext_name(path);
let filepath = Path::new(path);
let exists = filepath.exists();
if !ext.is_empty() && exists {
return Some(filepath.to_owned());
}
if filepath.is_dir() && exists {
basename = format!("{}/index", &basename);
}
for ext in JS_EXTENSIONS {
let path = &format!("{}{}", &basename, ext);
let path = Path::new(path);
if path.exists() {
return Some(path.to_owned());
}
}
None
}
pub struct DirectoryWalker<T>
where
T: Fn(&str) -> bool,
{
stack: Vec<(PathBuf, Option<Metadata>)>,
filter: T,
recursive: bool,
eat_root: bool,
}
impl<T> DirectoryWalker<T>
where
T: Fn(&str) -> bool,
{
pub fn new(root: PathBuf, filter: T) -> Self {
Self {
stack: vec![(root, None)],
filter,
recursive: false,
eat_root: true,
}
}
pub fn set_recursive(&mut self, recursive: bool) {
self.recursive = recursive;
}
pub async fn walk(&mut self) -> io::Result<Option<(PathBuf, Metadata)>> {
if self.eat_root {
self.eat_root = false;
let (dir, _) = self.stack.pop().unwrap();
self.append_stack(&dir).await?;
}
if let Some((dir, metadata)) = self.stack.pop() {
let metadata = metadata.unwrap();
if self.recursive && metadata.is_dir() {
self.append_stack(&dir).await?;
}
Ok(Some((dir, metadata)))
} else {
Ok(None)
}
}
pub fn walk_sync(&mut self) -> io::Result<Option<(PathBuf, Metadata)>> {
if self.eat_root {
self.eat_root = false;
let (dir, _) = self.stack.pop().unwrap();
self.append_stack_sync(&dir)?;
}
if let Some((dir, metadata)) = self.stack.pop() {
let metadata = metadata.unwrap();
if self.recursive && metadata.is_dir() {
self.append_stack_sync(&dir)?;
}
Ok(Some((dir, metadata)))
} else {
Ok(None)
}
}
async fn append_stack(&mut self, dir: &PathBuf) -> io::Result<()> {
let mut stream = fs::read_dir(dir).await?;
while let Some(entry) = stream.next_entry().await? {
let entry_path = entry.path();
let name = entry.file_name().to_string_lossy().to_string();
if !(self.filter)(&name) {
continue;
}
let metadata = fs::symlink_metadata(&entry_path).await?;
self.stack.push((entry_path, Some(metadata)));
}
Ok(())
}
fn append_stack_sync(&mut self, dir: &PathBuf) -> io::Result<()> {
let dir = std::fs::read_dir(dir)?;
for entry in dir.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if !(self.filter)(&name) {
continue;
}
let entry_path = entry.path();
let metadata = entry_path.symlink_metadata()?;
self.stack.push((entry_path, Some(metadata)))
}
Ok(())
}
}