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]]
|
[[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"
|
||||||
|
@ -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"
|
||||||
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
106
src/utils.rs
106
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> {
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_dir(src_dir: &std::path::Path, dst_dir: &std::path::Path, spinner: &indicatif::ProgressBar) -> Result<(), String> {
|
for dir in dirs.into_iter().filter_map(trim_fn) {
|
||||||
let content = src_dir.read_dir().map_err(|e| e.to_string())?;
|
std::fs::create_dir_all(dst_dir.join(dir)).map_err(|e| e.to_string())?;
|
||||||
for entry in content {
|
pb.inc(1);
|
||||||
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 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(())
|
Ok(())
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user