Added progress bars to collect and install operations

This commit is contained in:
Mutzi 2023-01-25 16:06:29 +01:00
parent 1b496211a6
commit 9537113a50
4 changed files with 94 additions and 71 deletions

9
Cargo.lock generated
View File

@ -81,11 +81,10 @@ dependencies = [
[[package]] [[package]]
name = "dotfiles_installer" name = "dotfiles_installer"
version = "0.1.0" version = "1.0.0"
dependencies = [ dependencies = [
"attohttpc", "attohttpc",
"dialoguer", "dialoguer",
"fs_extra",
"indicatif", "indicatif",
"serde", "serde",
"toml", "toml",
@ -121,12 +120,6 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "fs_extra"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "dotfiles_installer" name = "dotfiles_installer"
version = "0.1.0" version = "1.0.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -9,7 +9,6 @@ edition = "2021"
dialoguer = "0.10.3" dialoguer = "0.10.3"
indicatif = "0.17.3" indicatif = "0.17.3"
toml = "0.6.0" toml = "0.6.0"
fs_extra = "1.2.0"
[dependencies.serde] [dependencies.serde]
version = "1.0.152" version = "1.0.152"

View File

@ -10,14 +10,13 @@ impl ToString for NamedModule {
} }
fn install_mod(m: NamedModule) { fn install_mod(m: NamedModule) {
let spinner = indicatif::ProgressBar::new_spinner().with_message(format!("Installing module {}...", m.0)); println!("Installing module {}...", m.0);
'content_iter: for entry in m.1.content { 'content_iter: for entry in m.1.content {
let src = std::path::Path::new("repo").join(&entry.0); let src = std::path::Path::new("repo").join(&entry.0);
if src.is_file() { if src.is_file() {
if let Err(err) = std::fs::copy(src, entry.1) { if let Err(err) = std::fs::copy(src, entry.1) {
println!("Failed to copy {}:\n{}", entry.0, err); println!("Failed to copy {}:\n{}", entry.0, err);
} }
spinner.tick();
} else { } else {
let dst = std::path::Path::new(entry.1.as_str()); let dst = std::path::Path::new(entry.1.as_str());
@ -26,21 +25,15 @@ fn install_mod(m: NamedModule) {
continue 'content_iter; continue 'content_iter;
} }
let to_remove = match crate::utils::get_dir_content_filtered(dst, &m.1.ignore) { let pb = indicatif::ProgressBar::new(0);
Ok(v) => v, pb.set_style(indicatif::ProgressStyle::with_template("{spinner} [{wide_bar}] {pos:>6}/{len:6}").unwrap().progress_chars("#>-"));
Err(err) => { println!("Failed to get destination content:\n{}", err); continue 'content_iter; }
};
for file in to_remove.files { if let Err(err) = crate::utils::remove_dir(dst, &m.1.ignore, &pb) {
let _ = std::fs::remove_file(file); println!("Failed to delete directory:\n{}", err);
spinner.tick(); continue 'content_iter;
} }
for dir in to_remove.directories.into_iter().rev() { if let Err(err) = crate::utils::copy_dir(&src, dst, &[], &pb) {
let _ = std::fs::remove_dir(dir);
spinner.tick();
}
if let Err(err) = crate::utils::copy_dir(&src, dst, &spinner) {
println!("Failed to copy directory:\n{}", err); println!("Failed to copy directory:\n{}", err);
} }
} }
@ -48,7 +41,7 @@ fn install_mod(m: NamedModule) {
} }
fn collect_mod(m: NamedModule) { fn collect_mod(m: NamedModule) {
let spinner = indicatif::ProgressBar::new_spinner().with_message(format!("Collecting module {}...", m.0)); println!("Collecting module {}...", m.0);
'content_iter: for entry in m.1.content { 'content_iter: for entry in m.1.content {
let dst = std::path::Path::new("repo").join(entry.0.as_str()); let dst = std::path::Path::new("repo").join(entry.0.as_str());
let src = std::path::Path::new(entry.1.as_str()); let src = std::path::Path::new(entry.1.as_str());
@ -56,27 +49,23 @@ fn collect_mod(m: NamedModule) {
if let Err(err) = std::fs::copy(src, dst) { if let Err(err) = std::fs::copy(src, dst) {
println!("Failed to copy {}:\n{}", entry.1, err); println!("Failed to copy {}:\n{}", entry.1, err);
} }
spinner.tick();
} else { } else {
let _ = std::fs::remove_dir_all(&dst); let pb = indicatif::ProgressBar::new(0);
pb.set_style(indicatif::ProgressStyle::with_template("{spinner} [{wide_bar}] {pos:>6}/{len:6}").unwrap().progress_chars("#>-"));
if let Err(err) = crate::utils::remove_dir(&dst, &[], &pb) {
println!("Failed to delete directory:\n{}", err);
continue 'content_iter;
}
if let Err(err) = std::fs::create_dir_all(&dst) { if let Err(err) = std::fs::create_dir_all(&dst) {
println!("Failed to create directory {}:\n{}", entry.0, err); println!("Failed to create directory {}:\n{}", entry.0, err);
continue 'content_iter; continue 'content_iter;
} }
let to_copy = match crate::utils::get_dir_content_filtered(src, &m.1.ignore) { if let Err(err) = crate::utils::copy_dir(src, &dst, &m.1.ignore, &pb) {
Ok(v) => crate::utils::trim_dir_content(v, src), println!("Failed to copy source content:\n{}", err);
Err(err) => { println!("Failed to get source content:\n{}", err); continue 'content_iter; } continue 'content_iter;
};
for dir in to_copy.directories {
std::fs::create_dir_all(dst.join(dir)).unwrap();
spinner.tick();
}
for file in to_copy.files {
std::fs::copy(src.join(&file), dst.join(&file)).unwrap();
spinner.tick();
} }
} }
} }

View File

@ -1,47 +1,89 @@
use fs_extra::dir::DirContent; use std::path::{Path, PathBuf};
pub fn get_dir_content_filtered(p: &std::path::Path, ignored: &[String]) -> Result<DirContent, String> { fn get_dir_content_recursive<F>(p: &Path, filter: &F, pb: &indicatif::ProgressBar, dirs: &mut Vec<PathBuf>, files: &mut Vec<PathBuf>) -> bool
let p = p.canonicalize().map_err(|e| e.to_string())?; where F: Fn(&Path) -> bool {
let base_path_len = p.to_str().unwrap().len() + 1; let mut add = true;
for child in p.read_dir().unwrap() {
let child = child.unwrap();
let p = child.path();
if filter(&p) {
if child.file_type().unwrap().is_dir() {
add = get_dir_content_recursive(&child.path(), filter, pb, dirs, files) && add;
} else {
files.push(p);
pb.inc_length(1);
}
} else { add = false; }
let retain_fn = |p: &String| { }
if add {
dirs.push(p.to_path_buf());
pb.inc_length(1);
}
return add;
}
pub struct DirContent {
pub dirs: Vec<PathBuf>,
pub files: Vec<PathBuf>
}
pub fn get_dir_content_filtered(p: &Path, ignored: &[String], pb: &indicatif::ProgressBar) -> Result<DirContent, String> {
let base = p.canonicalize().map_err(|e| e.to_string())?;
let base_path_len = base.to_str().unwrap().len() + 1;
let path_filter = |p: &Path| {
let p = p.canonicalize().unwrap();
let p = p.to_string_lossy();
if p.len() < base_path_len { return false; } if p.len() < base_path_len { return false; }
let rel_path = p.split_at(base_path_len).1; let rel_path = p.split_at(base_path_len).1;
!ignored.iter().any(|i| rel_path.starts_with(i)) !ignored.iter().any(|i| rel_path.starts_with(i))
}; };
let mut content = fs_extra::dir::get_dir_content(p).map_err(|e| e.to_string())?; let mut dirs = Vec::<PathBuf>::new();
content.files.retain(retain_fn); let mut files = Vec::<PathBuf>::new();
content.directories.retain(retain_fn);
Ok(content) get_dir_content_recursive(&base, &path_filter, pb, &mut dirs, &mut files);
Ok(DirContent {
dirs,
files
})
} }
pub fn trim_dir_content(mut content: DirContent, base: &std::path::Path) -> DirContent { pub fn copy_dir(src_dir: &Path, dst_dir: &Path, ignored: &[String], pb: &indicatif::ProgressBar) -> Result<(), String> {
let base_path_len = base.canonicalize().unwrap().to_str().unwrap().len() + 1; let src_dir_len = src_dir.iter().count();
let trim_fn = |p: PathBuf| {
let iter_fn = |p: &mut String| { let new_path = p.into_iter().skip(src_dir_len).collect::<PathBuf>();
if p.len() < base_path_len { return; } (new_path.iter().count() != 0).then_some(new_path).or_else(|| { pb.set_length(pb.length().unwrap() - 1); None })
let _ = p.drain(..base_path_len).collect::<Vec<_>>();
}; };
content.files.iter_mut().for_each(iter_fn); let DirContent { dirs, files } = get_dir_content_filtered(src_dir, ignored, pb)?;
content.directories.iter_mut().for_each(iter_fn);
content for dir in dirs.into_iter().filter_map(trim_fn) {
std::fs::create_dir_all(dst_dir.join(dir)).map_err(|e| e.to_string())?;
pb.inc(1);
}
for file in files.into_iter().filter_map(trim_fn) {
std::fs::copy(src_dir.join(&file), dst_dir.join(file)).map_err(|e| e.to_string())?;
pb.inc(1);
}
Ok(())
} }
pub fn copy_dir(src_dir: &std::path::Path, dst_dir: &std::path::Path, spinner: &indicatif::ProgressBar) -> Result<(), String> { pub fn remove_dir(dir: &Path, ignored: &[String], pb: &indicatif::ProgressBar) -> Result<(), String> {
let content = src_dir.read_dir().map_err(|e| e.to_string())?; let to_remove = get_dir_content_filtered(dir, ignored, pb)?;
for entry in content {
let entry = entry.map_err(|e| e.to_string())?; for file in to_remove.files {
let new_dst = dst_dir.join(entry.file_name()); std::fs::remove_file(file).map_err(|e| e.to_string())?;
if entry.path().is_file() { pb.inc(1);
std::fs::copy(entry.path(), new_dst).map_err(|e| e.to_string())?;
spinner.tick();
} else {
std::fs::create_dir_all(&new_dst).map_err(|e| e.to_string())?;
spinner.tick();
copy_dir(&entry.path(), &new_dst, spinner)?;
}
} }
for dir in to_remove.dirs {
std::fs::remove_dir(dir).map_err(|e| e.to_string())?;
pb.inc(1);
}
Ok(()) Ok(())
} }