diff --git a/src/commands/add.rs b/src/commands/add.rs index e043508..a180922 100644 --- a/src/commands/add.rs +++ b/src/commands/add.rs @@ -1,9 +1,10 @@ use std::io::Write; use std::path::{Path, PathBuf}; use clap::Values; -use crate::store; -use crate::utils::{self}; +use crate::store::{self, object::Object}; +use crate::utils; use crate::utils::nextsyncignore::{self, ignore_file}; +use crate::utils::path::{normalize_relative, repo_root}; pub struct AddArgs<'a> { pub files: Values<'a>, @@ -12,33 +13,48 @@ pub struct AddArgs<'a> { // todo match deleted files // todo match weird reg expression -// todo normalize path pub fn add(args: AddArgs) { let mut index_file = store::index::open(); let mut added_files: Vec = vec![]; + let rules = match nextsyncignore::read_lines() { Ok(r) => r, Err(_) => vec![], }; + let mut ignored_f = vec![]; let file_vec: Vec<&str> = args.files.collect(); for file in file_vec { - if !args.force && ignore_file(&file.to_string(), rules.clone(), &mut ignored_f) { + + let f = match normalize_relative(file) { + Ok(f) => f, + Err(err) => { + eprintln!("err: {} {}", file, err); + continue; + } + }; + + if !args.force && ignore_file(&f, rules.clone(), &mut ignored_f) { continue; } - let path = Path::new(file); + + let path = repo_root().join(Path::new(&f)); + match path.exists() { true => { if path.is_dir() { - added_files.push(String::from(path.to_str().unwrap())); + added_files.push(f); add_folder_content(path.to_path_buf(), &mut added_files); } else { added_files.push(String::from(path.to_str().unwrap())); } }, false => { - // todo deleted file/folder verif if exists - added_files.push(String::from(path.to_str().unwrap())); + if Object::new(path.to_str().unwrap()).exists() { + added_files.push(String::from(f)); + } else { + eprintln!("err: {} is not something you can add.", path.to_str().unwrap()); + } } } } diff --git a/src/utils/path.rs b/src/utils/path.rs index 31ffd25..f4bbb16 100644 --- a/src/utils/path.rs +++ b/src/utils/path.rs @@ -1,8 +1,61 @@ use std::env; use std::fs::canonicalize; -use std::path::{PathBuf, Path}; +use std::path::{PathBuf, Path, Component}; + use crate::global::global::DIR_PATH; +/// Improve the path to try remove and solve .. token. +/// Taken from https://stackoverflow.com/questions/68231306/stdfscanonicalize-for-files-that-dont-exist +/// +/// This assumes that `a/b/../c` is `a/c` which might be different from +/// what the OS would have chosen when b is a link. This is OK +/// for broot verb arguments but can't be generally used elsewhere +/// +/// This function ensures a given path ending with '/' still +/// ends with '/' after normalization. +pub fn normalize_path>(path: P) -> PathBuf { + let ends_with_slash = path.as_ref() + .to_str() + .map_or(false, |s| s.ends_with('/')); + let mut normalized = PathBuf::new(); + for component in path.as_ref().components() { + match &component { + Component::ParentDir => { + if !normalized.pop() { + normalized.push(component); + } + } + _ => { + normalized.push(component); + } + } + } + if ends_with_slash { + normalized.push(""); + } + normalized +} + +pub fn normalize_relative(file: &str) -> Result { + let current = match current() { + Some(p) => p, + None => { + return Err("cannot find current location".to_owned()); + } + }; + + let p = { + let tmp_p = current.join(PathBuf::from(file)); + normalize_path(tmp_p) + }; + + let relative_p = match p.strip_prefix(repo_root()) { + Ok(p) => p, + Err(_) => return Err("is not in a nextsync repo or doesn't exist".to_owned()), + }; + Ok(relative_p.to_str().unwrap().to_owned()) +} + pub fn current() -> Option { let d = DIR_PATH.lock().unwrap();