From 9956727cc9e034ba380c6891524cd429bf55bd02 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Fri, 16 Jun 2023 18:37:09 +0200 Subject: [PATCH] add ignore file to add command with force option --- src/commands/add.rs | 82 ++++++++++++++++++++++++++----------- src/commands/status.rs | 23 ++++++----- src/main.rs | 12 +++++- src/store/index.rs | 10 +++-- src/utils.rs | 1 + src/utils/nextsyncignore.rs | 63 ++++++++++++++-------------- 6 files changed, 122 insertions(+), 69 deletions(-) diff --git a/src/commands/add.rs b/src/commands/add.rs index f5f5d89..d5de5a9 100644 --- a/src/commands/add.rs +++ b/src/commands/add.rs @@ -1,42 +1,74 @@ use clap::Values; -use crate::utils; +use crate::utils::{self, nextsyncignore}; use crate::store; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::io::Write; +use glob::glob; -pub fn add(files: Values<'_>) { - let root = match utils::path::nextsync_root() { - Some(path) => path, - None => { - eprintln!("fatal: not a nextsync repository (or any of the parent directories): .nextsync"); - std::process::exit(1); - } - }; +pub struct AddArgs<'a> { + pub files: Values<'a>, + pub force: bool, +} - let mut index_path = root.clone(); - index_path.push(".nextsync"); - let mut index_file = store::index::open(index_path); - // todo avoid duplicate - // ./folder ./folder/file +pub fn add(args: AddArgs) { + let mut index_file = store::index::open(); + let mut added_files: Vec = vec![]; - let file_vec: Vec<&str> = files.collect(); + let file_vec: Vec<&str> = args.files.collect(); for file in file_vec { let path = Path::new(file); - println!("{}", file); match path.exists() { true => { - match writeln!(index_file, "{}", path.display()) { - Ok(()) => (), - Err(err) => eprintln!("{}", err), + if path.is_dir() { + added_files.push(String::from(path.to_str().unwrap())); + add_folder_content(path.to_path_buf(), &mut added_files); + } else { + added_files.push(String::from(path.to_str().unwrap())); } }, false => { - match writeln!(index_file, "{}", path.display()) { - Ok(()) => (), - Err(err) => eprintln!("{}", err), - } - // todo can be regex + // todo deleted file/folder verif if exists + added_files.push(String::from(path.to_str().unwrap())); } } } + + // check ignored file if not forced + if !args.force { + let (ignored, ignored_files) = nextsyncignore::ignore_files(&mut added_files); + if ignored { + // todo multiple nextsyncignore + println!("The following paths are ignored by your .nextsyncignore file:"); + for file in ignored_files { + println!("{}", file); + } + } + } + + // save all added_files in index + for file in added_files { + match writeln!(index_file, "{}", file) { + Ok(()) => (), + Err(err) => eprintln!("{}", err), + } + } + drop(index_file); +} + +fn add_folder_content(path: PathBuf, added_files: &mut Vec) { + let mut folders: Vec = vec![]; + folders.push(path); + + while let Some(folder) = folders.pop() { + if let Ok(entries) = utils::read::read_folder(folder.clone()) { + for entry in entries { + let path_entry = PathBuf::from(entry); + if path_entry.is_dir() { + folders.push(path_entry.clone()); + } + added_files.push(String::from(path_entry.to_str().unwrap())); + } + } + } + } diff --git a/src/commands/status.rs b/src/commands/status.rs index 65a2b42..8d2c26f 100644 --- a/src/commands/status.rs +++ b/src/commands/status.rs @@ -15,9 +15,7 @@ enum RemoveSide { Right, } -#[derive(PartialEq)] -#[derive(Debug)] -#[derive(Clone)] +#[derive(PartialEq, Debug, Clone)] pub enum State { Default, New, @@ -39,8 +37,7 @@ pub fn status() { print_status(staged_objs, objs); } -#[derive(Debug)] -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Obj { pub otype: String, pub name: String, @@ -324,10 +321,16 @@ mod tests { let hash4 = hasher.result_str(); hasher.reset(); - let mut hashes = HashSet::new(); - hashes.insert(hash1.clone()); - hashes.insert(hash2.clone()); - hashes.insert(hash4.clone()); + let mut hashes = HashMap::new(); + let default_obj = Obj { + otype: String::from("tree"), + name: String::from("test"), + path: PathBuf::from(""), + state: State::Default, + }; + hashes.insert(hash1.clone(), default_obj.clone()); + hashes.insert(hash2.clone(), default_obj.clone()); + hashes.insert(hash4.clone(), default_obj.clone()); let mut objects: Vec = vec![]; objects.push(String::from("file1")); @@ -336,7 +339,7 @@ mod tests { remove_duplicate(&mut hashes, &mut objects, RemoveSide::Both); dbg!(hashes.clone()); dbg!(objects.clone()); - assert_eq!(hashes.contains(&hash4), true); + assert_eq!(hashes.contains_key(&hash4), true); assert_eq!(hashes.len(), 1); assert_eq!(objects, vec!["file3"]); } diff --git a/src/main.rs b/src/main.rs index 420885f..8f971b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use clap::{App, Arg, SubCommand}; +use crate::commands::add::AddArgs; mod commands; mod utils; mod services; @@ -55,6 +56,12 @@ fn main() { .value_name("FILE") .help("Files to add"), ) + .arg( + Arg::with_name("force") + .short("f") + .long("force") + .help("Allow adding otherwise ignored files."), + ) ) .subcommand( SubCommand::with_name("config") @@ -85,7 +92,10 @@ fn main() { commands::status::status(); } else if let Some(matches) = matches.subcommand_matches("add") { if let Some(files) = matches.values_of("files") { - commands::add::add(files); + commands::add::add(AddArgs { + files: files, + force: matches.is_present("force"), + }); } } else if let Some(_) = matches.subcommand_matches("reset") { commands::reset::reset(); diff --git a/src/store/index.rs b/src/store/index.rs index bb55cce..4f740c8 100644 --- a/src/store/index.rs +++ b/src/store/index.rs @@ -11,7 +11,12 @@ pub fn _read_only(mut path: PathBuf) -> File { .open(path).expect("Cannot open index file") } -pub fn open(mut path: PathBuf) -> File { +pub fn open() -> File { + let mut path = match path::nextsync() { + Some(p) => p, + None => todo!(), + }; + path.push("index"); OpenOptions::new() .read(true) @@ -27,12 +32,11 @@ pub fn read_line(mut path: PathBuf) -> io::Result> } pub fn rm_line(line: &str) -> io::Result<()> { - let mut root = match path::nextsync_root() { + let mut root = match path::nextsync() { Some(path) => path, None => todo!(), }; - root.push(".nextsync"); root.push("index"); read::rm_line(root, line)?; Ok(()) diff --git a/src/utils.rs b/src/utils.rs index f47ce16..68136a3 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,2 +1,3 @@ pub mod path; pub mod read; +pub mod nextsyncignore; diff --git a/src/utils/nextsyncignore.rs b/src/utils/nextsyncignore.rs index 98d9a97..642de50 100644 --- a/src/utils/nextsyncignore.rs +++ b/src/utils/nextsyncignore.rs @@ -23,15 +23,14 @@ fn read_lines() -> Result, ()> { Ok(vec![]) } -fn ignore_files(files: &mut Vec) -> bool { - let ignored = false; - let origin_len = files.len(); +pub fn ignore_files(files: &mut Vec) -> (bool, Vec) { + let mut ignored_f = vec![]; if let Some(path) = path::nextsyncignore() { if let Ok(lines) = read_lines() { - files.retain(|file| ignore_file(file, lines.clone())); + files.retain(|file| !ignore_file(file, lines.clone(), &mut ignored_f)); } } - files.len() != origin_len + (ignored_f.len() > 0, ignored_f) } fn normalize_rule(l: String) -> String { @@ -55,7 +54,7 @@ fn normalize_rule(l: String) -> String { line } -pub fn ignore_file(path: &String, lines: Vec) -> bool { +pub fn ignore_file(path: &String, lines: Vec, ignored_f: &mut Vec) -> bool { let mut ignored = false; for mut line in lines { if line.starts_with("!") { @@ -74,6 +73,9 @@ pub fn ignore_file(path: &String, lines: Vec) -> bool { } } } + if ignored { + ignored_f.push(path.to_string()); + } ignored } @@ -92,29 +94,29 @@ mod tests { lines.push(normalize_rule(l.clone())); } } + let mut ignf = vec![]; + assert_eq!(ignore_file(&String::from("error.log"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("./error.log"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("dir/error.log"), lines.clone(), &mut ignf), true); - assert_eq!(ignore_file(&String::from("error.log"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("./error.log"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("dir/error.log"), lines.clone()), true); + assert_eq!(ignore_file(&String::from("exclude"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("dir/exclude"), lines.clone(), &mut ignf), true); - assert_eq!(ignore_file(&String::from("exclude"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("dir/exclude"), lines.clone()), true); + assert_eq!(ignore_file(&String::from("logs/dir/file2"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("dir/logs/dir/file2"), lines.clone(), &mut ignf), false); - assert_eq!(ignore_file(&String::from("logs/dir/file2"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("dir/logs/dir/file2"), lines.clone()), false); + assert_eq!(ignore_file(&String::from("build/target/file1"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("build/target/dir/file1"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("build"), lines.clone(), &mut ignf), false); + assert_eq!(ignore_file(&String::from("build/target"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("dir/build/target"), lines.clone(), &mut ignf), false); - assert_eq!(ignore_file(&String::from("build/target/file1"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("build/target/dir/file1"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("build"), lines.clone()), false); - assert_eq!(ignore_file(&String::from("build/target"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("dir/build/target"), lines.clone()), false); + assert_eq!(ignore_file(&String::from("dir/file.swp"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from(".swp"), lines.clone(), &mut ignf), false); - assert_eq!(ignore_file(&String::from("dir/file.swp"), lines.clone()), true); - assert_eq!(ignore_file(&String::from(".swp"), lines.clone()), false); - - assert_eq!(ignore_file(&String::from("secret"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("dir/secret"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("dir/secret/file"), lines.clone()), true); + assert_eq!(ignore_file(&String::from("secret"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("dir/secret"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("dir/secret/file"), lines.clone(), &mut ignf), true); } #[test] @@ -129,12 +131,13 @@ mod tests { } } - assert_eq!(ignore_file(&String::from("file"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("dir/file"), lines.clone()), true); - assert_eq!(ignore_file(&String::from("file.log"), lines.clone()), false); - assert_eq!(ignore_file(&String::from("log.file"), lines.clone()), false); - assert_eq!(ignore_file(&String::from("dir/file.log"), lines.clone()), false); - assert_eq!(ignore_file(&String::from("dir/log.file"), lines.clone()), false); + let mut ignf = vec![]; + assert_eq!(ignore_file(&String::from("file"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("dir/file"), lines.clone(), &mut ignf), true); + assert_eq!(ignore_file(&String::from("file.log"), lines.clone(), &mut ignf), false); + assert_eq!(ignore_file(&String::from("log.file"), lines.clone(), &mut ignf), false); + assert_eq!(ignore_file(&String::from("dir/file.log"), lines.clone(), &mut ignf), false); + assert_eq!(ignore_file(&String::from("dir/log.file"), lines.clone(), &mut ignf), false); } }