Add graceful error handling to df script

This commit is contained in:
2021-04-11 15:04:58 +02:00
parent 4becee4a89
commit f09c914881
6 changed files with 188 additions and 58 deletions

View File

@ -1,17 +1,18 @@
use crate::error::{Error, ErrorLocation, Errors};
use crate::Config;
use async_recursion::async_recursion;
use futures::future::join_all;
use std::io::{self, ErrorKind};
use std::io::ErrorKind;
use std::path::PathBuf;
use tokio::fs::{create_dir, read_dir, remove_file, symlink};
use tokio::try_join;
use tokio::join;
pub async fn link_tree(cfg: &Config) -> io::Result<()> {
pub async fn link_tree(cfg: &Config) -> Result<(), Errors> {
dir(cfg, PathBuf::new()).await
}
#[async_recursion]
async fn dir(cfg: &Config, relative: PathBuf) -> io::Result<()> {
async fn dir(cfg: &Config, relative: PathBuf) -> Result<(), Errors> {
let build_path = cfg.build_dir.join(&relative);
let link_path = cfg.link_dir.join(&relative);
@ -20,16 +21,16 @@ async fn dir(cfg: &Config, relative: PathBuf) -> io::Result<()> {
match create_dir(&link_path).await {
Ok(_) => {}
Err(e) if e.kind() == ErrorKind::AlreadyExists => {}
Err(e) => return Err(e),
Err(e) => return Err(e.with_location(&link_path).into()),
}
let mut walker = read_dir(&build_path).await?;
let mut walker = read_dir(&build_path).await.with_location(&build_path)?;
let mut dir_tasks = vec![];
let mut file_tasks = vec![];
while let Some(entry) = walker.next_entry().await? {
let meta = entry.metadata().await?;
while let Some(entry) = walker.next_entry().await.with_location(&build_path)? {
let meta = entry.metadata().await.with_location(&entry.path())?;
let new_relative = relative.join(entry.file_name());
if meta.is_dir() {
@ -39,26 +40,28 @@ async fn dir(cfg: &Config, relative: PathBuf) -> io::Result<()> {
}
}
let dirs = async {
join_all(dir_tasks)
.await
.into_iter()
.collect::<Result<Vec<_>, _>>()
};
let dirs = async { join_all(dir_tasks).await.into_iter().collect::<Vec<_>>() };
let files = async { join_all(file_tasks).await.into_iter().collect::<Vec<_>>() };
let (dirs, files) = join!(dirs, files);
let files = async {
join_all(file_tasks)
.await
.into_iter()
.collect::<Result<Vec<_>, _>>()
};
let mut errors: Errors = files
.into_iter()
.filter_map(|r| r.err())
.collect::<Vec<_>>()
.into();
try_join!(dirs, files)?;
for error in dirs.into_iter().filter_map(|r| r.err()) {
errors.join(error);
}
Ok(())
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
async fn file(cfg: &Config, relative: PathBuf) -> io::Result<()> {
async fn file(cfg: &Config, relative: PathBuf) -> Result<(), Error> {
let build_path = cfg.build_dir.join(&relative);
let link_path = cfg.link_dir.join(&relative);
@ -67,7 +70,7 @@ async fn file(cfg: &Config, relative: PathBuf) -> io::Result<()> {
info!("removed existing file {:?}", link_path);
}
Err(e) if e.kind() == ErrorKind::NotFound => {}
Err(e) => return Err(e),
Err(e) => return Err(e.with_location(&link_path)),
};
info!("linking {:?} to {:?}", link_path, build_path);
@ -85,7 +88,9 @@ async fn file(cfg: &Config, relative: PathBuf) -> io::Result<()> {
relative_symlink
};
symlink(symlink_content, link_path).await?;
symlink(symlink_content, &link_path)
.await
.with_location(&link_path)?;
Ok(())
}