diff --git a/src/commands.rs b/src/commands.rs index 43763f1..d7bb0c0 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1 +1,2 @@ +pub mod add; pub mod init; diff --git a/src/commands/add.rs b/src/commands/add.rs new file mode 100644 index 0000000..859cd98 --- /dev/null +++ b/src/commands/add.rs @@ -0,0 +1,59 @@ +use crate::config::config::Config; +use crate::store::ignorer::Ignorer; +// use crate::store::object::object::{Obj, ObjMethods}; +// use crate::store::{self, object::Object}; +// use crate::utils::nextsyncignore::{self, ignore_file}; +// use crate::utils::path::{normalize_relative, path_buf_to_string, repo_root}; +// use crate::utils::{self, path}; +// use glob::glob; +use std::path::{Path, PathBuf}; + +pub struct AddArgs { + /// List of files/dirs to add + pub files: Vec, + /// Allow ignored files + pub force: bool, + /// Select all files + pub all: bool, +} + +pub fn exec(args: AddArgs, config: Config) { + // Init ignorer + let mut ignorer = Ignorer::new(config.get_root_unsafe()); + ignorer.active_nsignore(!args.force); + + if args.all { + return add_dir(config.get_root_unsafe(), &mut ignorer); + } + + /* + for all files + if globbing + take all match glob nextsyncignore + else + if dir + add dir + add all childs with nextsyncignore + else + add files + */ +} + +/// Add all files in a directory +/// +/// # Parameters +/// * `dir`: the directory to add from +/// * `force`: true if we should apply the nextsyncignore's rules +fn add_dir(dir: &PathBuf, ignorer: &mut Ignorer) { + /* + for elements present in dir { + if !force && should_ignore() { + continue; + } + if is dir + add_dir(new_dir, force) + else + + } + */ +} diff --git a/src/commands/init.rs b/src/commands/init.rs index 6c9c00a..700541d 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -1,5 +1,5 @@ use crate::config::config::Config; -use std::fs::{create_dir_all, DirBuilder, File}; +use std::fs::{DirBuilder, File}; use std::path::PathBuf; pub struct InitArgs {} @@ -32,7 +32,7 @@ pub fn exec(_: InitArgs, config: Config) { } let mut binding = DirBuilder::new(); - let mut builder = binding.recursive(true); + let builder = binding.recursive(true); for dir in &["objects", "refs"] { path.push(dir); diff --git a/src/config/config.rs b/src/config/config.rs index b409c2b..2fdc05d 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -1,20 +1,58 @@ use std::env; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::sync::OnceLock; /// /// # Parameters /// * `execution_path`: path of the command (directory arg) or current path +/// * `root`: path of the repo pub struct Config { pub execution_path: PathBuf, + root: OnceLock>, } impl Config { - pub fn new(exec_path: Option<&String>) -> Self { + pub fn new() -> Self { Config { - execution_path: match exec_path { - Some(path) => PathBuf::from(path), - None => PathBuf::from(env::current_dir().unwrap()), + execution_path: PathBuf::from(env::current_dir().unwrap()), + root: OnceLock::new(), + } + } + + pub fn from(exec_path: Option<&String>) -> Self { + match exec_path { + Some(path) => Config { + execution_path: PathBuf::from(path), + root: OnceLock::new(), }, + None => Config::new(), + } + } + + pub fn get_root(&self) -> &Option { + self.root.get_or_init(|| { + let mut path = self.execution_path.clone(); + + let root = loop { + path.push(".nextsync"); + if path.exists() { + path.pop(); + break Some(path); + } + path.pop(); + path.pop(); + if path == Path::new("/") { + break None; + } + }; + root + }) + } + + pub fn get_root_unsafe(&self) -> &PathBuf { + match self.get_root() { + Some(path) => path, + None => panic!("fatal: not a nextsync repository (or any parent up to mount point /)"), } } } diff --git a/src/main.rs b/src/main.rs index 4ebf403..e4c02f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use clap::Command; mod commands; mod config; +mod store; mod subcommands; fn main() { @@ -9,13 +10,14 @@ fn main() { .version("1.0") .author("grimhilt") .about("A git-line command line tool to interact with nextcloud") - .subcommands([subcommands::init::create()]); + .subcommands([subcommands::init::create(), subcommands::add::create()]); // .setting(clap::AppSettings::SubcommandRequiredElseHelp); let matches = app.get_matches(); match matches.subcommand() { Some(("init", args)) => subcommands::init::handler(args), + Some(("add", args)) => subcommands::add::handler(args), Some((_, _)) => {} None => {} }; diff --git a/src/store.rs b/src/store.rs new file mode 100644 index 0000000..733d50c --- /dev/null +++ b/src/store.rs @@ -0,0 +1,2 @@ +pub mod ignorer; +pub mod nsignore; diff --git a/src/store/ignorer.rs b/src/store/ignorer.rs new file mode 100644 index 0000000..90c08e4 --- /dev/null +++ b/src/store/ignorer.rs @@ -0,0 +1,53 @@ +use crate::store::nsignore::{get_nsignore_file, get_nsignore_rules, is_file_ignored}; +use std::path::PathBuf; +use std::sync::OnceLock; + +pub struct Ignorer { + /// Path of this level + path: PathBuf, + /// Should it use the nextsyncignore file + use_nsignore: bool, + /// Nsignore's rules + rules: OnceLock>, + childs: Option>>, +} + +impl Ignorer { + pub fn new(path: &PathBuf) -> Self { + Ignorer { + path: path.to_path_buf(), + use_nsignore: true, + rules: OnceLock::new(), + childs: None, + } + } + + pub fn active_nsignore(&mut self, active: bool) { + self.use_nsignore = active; + } + + fn get_rules(&mut self) -> &Vec { + self.rules + .get_or_init(|| match get_nsignore_file(&self.path) { + Some(nsignore_path) => get_nsignore_rules(&nsignore_path), + None => Vec::new(), + }) + } + + /// Returns whether a file/dir is ignored or not + /// + /// * `path`: + fn is_ignored(&mut self, path: PathBuf) -> bool { + is_file_ignored(path.to_str().unwrap(), self.get_rules()) + } + + /// Returns whether a file/dir should be ignored by the program or not + /// + /// This takes advantage of the `use_nsignore` variable set by + /// `active_nsignore`. + /// + /// * `path`: + pub fn should_ignore(&mut self, path: PathBuf) -> bool { + self.use_nsignore && self.is_ignored(path) + } +} diff --git a/src/subcommands.rs b/src/subcommands.rs index 43763f1..d7bb0c0 100644 --- a/src/subcommands.rs +++ b/src/subcommands.rs @@ -1 +1,2 @@ +pub mod add; pub mod init; diff --git a/src/subcommands/add.rs b/src/subcommands/add.rs new file mode 100644 index 0000000..d3cce3a --- /dev/null +++ b/src/subcommands/add.rs @@ -0,0 +1,46 @@ +use clap::{Arg, ArgAction, ArgMatches, Command}; + +use crate::commands; +use crate::commands::add::AddArgs; +use crate::config::config::Config; + +pub fn create() -> Command { + Command::new("add") + .arg( + Arg::new("files") + .required_unless_present("all") + .conflicts_with("all") + .num_args(1..) + .value_name("FILE") + .help("Files to add"), + ) + .arg( + Arg::new("force") + .short('f') + .long("force") + .action(ArgAction::SetTrue) + .help("Allow adding otherwise ignored files."), + ) + .arg( + Arg::new("all") + .short('A') + .long("all") + .action(ArgAction::SetTrue) + .help("This adds, modifies, and removes index entries to match the working tree"), + ) + .about("Add changes to the index") +} + +pub fn handler(args: &ArgMatches) { + commands::add::exec( + AddArgs { + files: match args.get_many::("files") { + None => vec![], + Some(vals) => vals.map(|s| s.to_string()).collect(), + }, + force: *args.get_one::("force").unwrap(), + all: *args.get_one::("all").unwrap(), + }, + Config::new(), + ); +} diff --git a/src/subcommands/init.rs b/src/subcommands/init.rs index f3f4ac5..be139f7 100644 --- a/src/subcommands/init.rs +++ b/src/subcommands/init.rs @@ -16,5 +16,5 @@ pub fn create() -> Command { pub fn handler(args: &ArgMatches) { let exec_dir = args.get_one::("directory"); - commands::init::exec(commands::init::InitArgs {}, Config::new(exec_dir)); + commands::init::exec(commands::init::InitArgs {}, Config::from(exec_dir)); }