Added progress bars to collect and install operations
This commit is contained in:
parent
1b496211a6
commit
9537113a50
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
104
src/utils.rs
104
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<DirContent, String> {
|
||||
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<F>(p: &Path, filter: &F, pb: &indicatif::ProgressBar, dirs: &mut Vec<PathBuf>, files: &mut Vec<PathBuf>) -> 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<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; }
|
||||
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::<PathBuf>::new();
|
||||
let mut files = Vec::<PathBuf>::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::<Vec<_>>();
|
||||
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::<PathBuf>();
|
||||
(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)?;
|
||||
|
||||
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)?;
|
||||
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 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(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user