Added progress bars to collect and install operations
This commit is contained in:
		
							
								
								
									
										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();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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> {
 | 
					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);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn copy_dir(src_dir: &std::path::Path, dst_dir: &std::path::Path, spinner: &indicatif::ProgressBar) -> Result<(), String> {
 | 
					    for file in files.into_iter().filter_map(trim_fn) {
 | 
				
			||||||
    let content = src_dir.read_dir().map_err(|e| e.to_string())?;
 | 
					        std::fs::copy(src_dir.join(&file), dst_dir.join(file)).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)?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user