From 9537113a500d4ff3fe4d790560e4207fa6b9215f Mon Sep 17 00:00:00 2001 From: Mutzi Date: Wed, 25 Jan 2023 16:06:29 +0100 Subject: [PATCH] Added progress bars to collect and install operations --- Cargo.lock | 9 +--- Cargo.toml | 3 +- src/operations.rs | 49 +++++++++------------- src/utils.rs | 104 ++++++++++++++++++++++++++++++++-------------- 4 files changed, 94 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f5581e..9b41dc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,11 +81,10 @@ dependencies = [ [[package]] name = "dotfiles_installer" -version = "0.1.0" +version = "1.0.0" dependencies = [ "attohttpc", "dialoguer", - "fs_extra", "indicatif", "serde", "toml", @@ -121,12 +120,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" - [[package]] name = "hashbrown" version = "0.12.3" diff --git a/Cargo.toml b/Cargo.toml index 7b9c4e6..7559aee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dotfiles_installer" -version = "0.1.0" +version = "1.0.0" edition = "2021" # 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" indicatif = "0.17.3" toml = "0.6.0" -fs_extra = "1.2.0" [dependencies.serde] version = "1.0.152" diff --git a/src/operations.rs b/src/operations.rs index 78795a7..2630086 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -10,14 +10,13 @@ impl ToString for 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 { let src = std::path::Path::new("repo").join(&entry.0); if src.is_file() { if let Err(err) = std::fs::copy(src, entry.1) { println!("Failed to copy {}:\n{}", entry.0, err); } - spinner.tick(); } else { let dst = std::path::Path::new(entry.1.as_str()); @@ -26,21 +25,15 @@ fn install_mod(m: NamedModule) { continue 'content_iter; } - let to_remove = match crate::utils::get_dir_content_filtered(dst, &m.1.ignore) { - Ok(v) => v, - Err(err) => { println!("Failed to get destination content:\n{}", err); continue 'content_iter; } - }; + let pb = indicatif::ProgressBar::new(0); + pb.set_style(indicatif::ProgressStyle::with_template("{spinner} [{wide_bar}] {pos:>6}/{len:6}").unwrap().progress_chars("#>-")); - for file in to_remove.files { - let _ = std::fs::remove_file(file); - spinner.tick(); + if let Err(err) = crate::utils::remove_dir(dst, &m.1.ignore, &pb) { + println!("Failed to delete directory:\n{}", err); + continue 'content_iter; } - for dir in to_remove.directories.into_iter().rev() { - let _ = std::fs::remove_dir(dir); - spinner.tick(); - } - if let Err(err) = crate::utils::copy_dir(&src, dst, &spinner) { + if let Err(err) = crate::utils::copy_dir(&src, dst, &[], &pb) { println!("Failed to copy directory:\n{}", err); } } @@ -48,7 +41,7 @@ fn install_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 { let dst = std::path::Path::new("repo").join(entry.0.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) { println!("Failed to copy {}:\n{}", entry.1, err); } - spinner.tick(); } 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) { println!("Failed to create directory {}:\n{}", entry.0, err); continue 'content_iter; } - let to_copy = match crate::utils::get_dir_content_filtered(src, &m.1.ignore) { - Ok(v) => crate::utils::trim_dir_content(v, src), - Err(err) => { println!("Failed to get source content:\n{}", err); 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(); + if let Err(err) = crate::utils::copy_dir(src, &dst, &m.1.ignore, &pb) { + println!("Failed to copy source content:\n{}", err); + continue 'content_iter; } } } diff --git a/src/utils.rs b/src/utils.rs index aab82ac..4623b11 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -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 { - let p = p.canonicalize().map_err(|e| e.to_string())?; - let base_path_len = p.to_str().unwrap().len() + 1; +fn get_dir_content_recursive(p: &Path, filter: &F, pb: &indicatif::ProgressBar, dirs: &mut Vec, files: &mut Vec) -> bool + where F: Fn(&Path) -> bool { + 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, + pub files: Vec +} + +pub fn get_dir_content_filtered(p: &Path, ignored: &[String], pb: &indicatif::ProgressBar) -> Result { + 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; } let rel_path = p.split_at(base_path_len).1; !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())?; - content.files.retain(retain_fn); - content.directories.retain(retain_fn); - Ok(content) + let mut dirs = Vec::::new(); + let mut files = Vec::::new(); + + 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 { - let base_path_len = base.canonicalize().unwrap().to_str().unwrap().len() + 1; - - let iter_fn = |p: &mut String| { - if p.len() < base_path_len { return; } - let _ = p.drain(..base_path_len).collect::>(); +pub fn copy_dir(src_dir: &Path, dst_dir: &Path, ignored: &[String], pb: &indicatif::ProgressBar) -> Result<(), String> { + let src_dir_len = src_dir.iter().count(); + let trim_fn = |p: PathBuf| { + let new_path = p.into_iter().skip(src_dir_len).collect::(); + (new_path.iter().count() != 0).then_some(new_path).or_else(|| { pb.set_length(pb.length().unwrap() - 1); None }) }; - content.files.iter_mut().for_each(iter_fn); - content.directories.iter_mut().for_each(iter_fn); - content + let DirContent { dirs, files } = get_dir_content_filtered(src_dir, ignored, pb)?; + + 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> { - let content = src_dir.read_dir().map_err(|e| e.to_string())?; - for entry in content { - let entry = entry.map_err(|e| e.to_string())?; - let new_dst = dst_dir.join(entry.file_name()); - if entry.path().is_file() { - 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)?; - } +pub fn remove_dir(dir: &Path, ignored: &[String], pb: &indicatif::ProgressBar) -> Result<(), String> { + let to_remove = get_dir_content_filtered(dir, ignored, pb)?; + + for file in to_remove.files { + std::fs::remove_file(file).map_err(|e| e.to_string())?; + pb.inc(1); } + + for dir in to_remove.dirs { + std::fs::remove_dir(dir).map_err(|e| e.to_string())?; + pb.inc(1); + } + Ok(()) } \ No newline at end of file