Files
dkl/src/fs.rs
T

105 lines
3.1 KiB
Rust
Raw Normal View History

2025-07-21 01:41:03 +02:00
use std::fs::Metadata;
2026-04-12 19:55:56 +02:00
use std::path::{Path, PathBuf};
use tokio::fs;
2025-07-21 01:41:03 +02:00
use tokio::sync::mpsc;
2026-04-12 19:55:56 +02:00
pub type Result<T> = std::result::Result<T, Error>;
2026-04-13 21:11:07 +02:00
pub use tokio::fs::ReadDir;
2026-04-12 19:55:56 +02:00
#[derive(Debug, thiserror::Error)]
pub enum Error {
2026-04-13 21:11:07 +02:00
#[error("{0}: read dir: {1}")]
2026-04-12 19:55:56 +02:00
ReadDir(PathBuf, std::io::Error),
2026-04-13 21:11:07 +02:00
#[error("{0}: exists: {1}")]
Exists(PathBuf, std::io::Error),
2026-04-12 19:55:56 +02:00
#[error("{0}: read: {1}")]
Read(PathBuf, std::io::Error),
#[error("{0}: stat: {1}")]
Stat(PathBuf, std::io::Error),
2026-04-13 21:11:07 +02:00
#[error("{0}: create dir: {1}")]
CreateDir(PathBuf, std::io::Error),
2026-04-12 19:55:56 +02:00
#[error("{0}: write: {1}")]
Write(PathBuf, std::io::Error),
2026-04-13 21:11:07 +02:00
#[error("{0}: remove file: {1}")]
RemoveFile(PathBuf, std::io::Error),
#[error("{0}: symlink: {1}")]
Symlink(PathBuf, std::io::Error),
2026-04-12 19:55:56 +02:00
#[error("{0}: {1}")]
Other(PathBuf, String),
}
macro_rules! wrap_path {
($fn:ident $( ( $( $pname:ident : $ptype:ty ),* ) )? -> $result:ty, $err:ident) => {
pub async fn $fn(path: impl AsRef<Path>$($(, $pname: $ptype)*)?) -> Result<$result> {
let path = path.as_ref();
fs::$fn(path $($(, $pname)*)?).await.map_err(|e| Error::$err(path.into(), e))
}
};
}
2026-04-13 21:11:07 +02:00
wrap_path!(read_dir -> ReadDir, ReadDir);
wrap_path!(try_exists -> bool, Exists);
2026-04-12 19:55:56 +02:00
wrap_path!(read -> Vec<u8>, Read);
wrap_path!(read_to_string -> String, Read);
2026-04-13 21:11:07 +02:00
wrap_path!(create_dir -> (), CreateDir);
wrap_path!(create_dir_all -> (), CreateDir);
wrap_path!(remove_file -> (), RemoveFile);
wrap_path!(symlink(link_src: impl AsRef<Path>) -> (), Symlink);
wrap_path!(write(content: impl AsRef<[u8]>) -> (), Write);
2026-04-12 19:55:56 +02:00
2025-07-21 01:41:03 +02:00
pub fn spawn_walk_dir(
dir: impl Into<PathBuf> + Send + 'static,
) -> mpsc::Receiver<Result<(PathBuf, Metadata)>> {
let (tx, rx) = mpsc::channel(1);
tokio::spawn(walk_dir(dir, tx));
rx
}
pub async fn walk_dir(dir: impl Into<PathBuf>, tx: mpsc::Sender<Result<(PathBuf, Metadata)>>) {
let dir: PathBuf = dir.into();
let mut todo = std::collections::LinkedList::new();
if let Ok(rd) = read_dir(&dir).await {
todo.push_front(rd);
}
while let Some(rd) = todo.front_mut() {
let entry = match rd.next_entry().await {
Ok(v) => v,
Err(e) => {
2026-04-12 19:55:56 +02:00
if tx.send(Err(Error::ReadDir(dir.clone(), e))).await.is_err() {
2025-07-21 01:41:03 +02:00
return;
}
todo.pop_front(); // skip dir on error
continue;
}
};
let Some(entry) = entry else {
todo.pop_front();
continue;
};
let Ok(md) = entry.metadata().await else {
continue;
};
let is_dir = md.is_dir();
let Ok(path) = entry.path().strip_prefix(&dir).map(|p| p.to_path_buf()) else {
continue; // sub-entry not in dir, weird but semantically, we ignore
};
if tx.send(Ok((path, md))).await.is_err() {
return;
}
// recurse in sub directories
if is_dir {
if let Ok(rd) = read_dir(entry.path()).await {
todo.push_front(rd);
}
}
}
}