use std::io::Write; use std::path::{Path, PathBuf}; use clap::Values; use glob::glob; use crate::store::{self, object::Object}; use crate::utils::{self, path}; use crate::store::object::object::{Obj, ObjMethods}; use crate::utils::nextsyncignore::{self, ignore_file}; use crate::utils::path::{normalize_relative, repo_root, path_buf_to_string}; pub struct AddArgs<'a> { pub files: Option>, pub force: bool, pub all: bool, } // todo match deleted files pub fn add(args: AddArgs) { let mut pattern: String; let file_vec: Vec<&str> = match args.all { true => { pattern = path_buf_to_string(repo_root()); pattern.push_str("/*"); vec![&pattern] }, false => args.files.unwrap().collect(), }; let mut added_files: Vec = vec![]; let mut ignored_f = vec![]; let rules = nextsyncignore::get_rules(); for file in file_vec { dbg!(&file); let f = match normalize_relative(file) { Ok(f) => f, Err(err) => { eprintln!("err: {} {}", file, err); continue; } }; let path = repo_root().join(Path::new(&f)); match path.exists() { true => { add_entry(path, args.force, &mut added_files, rules.clone(), &mut ignored_f); }, false => { if Object::new(path.to_str().unwrap()).exists() { // object is deleted so not present but can still be added for deletion added_files.push(String::from(f)); } else { // try globbing if nothing has been found for entry in try_globbing(path) { add_entry(entry, args.force, &mut added_files, rules.clone(), &mut ignored_f); } } } } } print_ignored_files(ignored_f); write_added_files(added_files); } fn add_entry(entry: PathBuf, force: bool, added_files: &mut Vec, rules: Vec, ignored_f: &mut Vec) { // ignore nextsync config files if path::is_nextsync_config(entry.clone()) { return; } // check if the file must be ignored if !force && ignore_file(&path_buf_to_string(entry.clone()), rules, ignored_f) { return; } // add the parent if there is one and it is not already created add_parent(entry.clone(), added_files); added_files.push(path_buf_to_string(entry.strip_prefix(repo_root()).unwrap().to_path_buf())); if entry.is_dir() { add_folder_content(entry.to_path_buf(), added_files); } } fn add_parent(entry: PathBuf, added_files: &mut Vec) { let test_parent = entry.strip_prefix(repo_root()).unwrap().parent(); if test_parent.is_none() || test_parent.unwrap() == PathBuf::new() { return; } let parent = entry.parent().unwrap(); if !Obj::from_path(parent).exists_on_remote() { add_parent(parent.to_path_buf(), added_files); added_files.push(path_buf_to_string(parent.strip_prefix(repo_root()).unwrap().to_path_buf())); } } fn print_ignored_files(ignored_files: Vec) { if ignored_files.len() > 0 { // todo multiple nextsyncignore println!("The following paths are ignored by your .nextsyncignore file:"); for file in ignored_files { println!("{}", file); } } } fn write_added_files(added_files: Vec) { let mut index_file = store::index::open(); for file in added_files { if store::index::alread_added(file.clone()) { continue; } match writeln!(index_file, "{}", file) { Ok(()) => (), Err(err) => eprintln!("{}", err), } } drop(index_file); } fn try_globbing(path: PathBuf) -> Vec { let mut paths: Vec = vec![]; if let Ok(entries) = glob(path.to_str().unwrap()) { for entry in entries { match entry { Ok(ppath) => paths.push(ppath), Err(e) => { eprintln!("err: {} incorrect pattern ({})", path.display(), e); } } } } else { eprintln!("err: {} is not something you can add.", path.to_str().unwrap()); } return paths; } fn add_folder_content(path: PathBuf, added_files: &mut Vec) { // todo check for changes 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::is_nextsync_config(path_entry.clone()) { if path_entry.is_dir() { folders.push(path_entry.clone()); } added_files.push(path_buf_to_string(path_entry.strip_prefix(repo_root()).unwrap().to_path_buf())); } } } } }