use clap::ArgMatches;
use commands::BasicOptions;
use error::Error;
use libpijul;
use libpijul::fs_representation::RepoRoot;
use rand;
use std::fs::{canonicalize, metadata};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy)]
pub enum Operation {
Add,
Remove,
}
pub fn run(args: &ArgMatches, op: Operation) -> Result<(), Error> {
debug!("fs_operation {:?}", op);
let opts = BasicOptions::from_args(args)?;
debug!("repo {:?}", &opts.repo_root);
let mut extra_space = 409600;
let recursive = args.is_present("recursive");
loop {
let touched_files = match args.values_of("files") {
Some(l) => l.map(|p| Path::new(p).to_owned()).collect(),
None => vec![],
};
match really_run(
&opts.repo_root,
&opts.cwd,
touched_files,
recursive,
op,
extra_space,
) {
Err(ref e) if e.lacks_space() => extra_space *= 2,
e => return e,
}
}
}
fn really_run(
repo_dir: &RepoRoot<PathBuf>,
wd: &Path,
mut files: Vec<PathBuf>,
recursive: bool,
op: Operation,
extra_space: u64,
) -> Result<(), Error> {
debug!("files {:?}", files);
let mut rng = rand::thread_rng();
let repo = repo_dir.open_repo(Some(extra_space))?;
let mut txn = repo.mut_txn_begin(&mut rng)?;
match op {
Operation::Add => {
for file_ in files.drain(..) {
let p = canonicalize(wd.join(&file_))?;
if recursive {
debug!("adding from {:?}", p);
let mut files = Vec::new();
for file in repo_dir.untracked_files(&txn, &p) {
debug!("untracked {:?}", file);
let m = metadata(repo_dir.absolutize(&file))?;
files.push((file, m.is_dir()));
}
for (file, is_dir) in files {
match txn.add_file(&file, is_dir) {
Ok(()) => {}
Err(libpijul::Error::AlreadyAdded) => {
eprintln!("{:?} is already in the repository", file_)
}
Err(e) => return Err(e.into()),
}
}
continue;
} else {
let m = metadata(&p)?;
if let Ok(file) = repo_dir.relativize(&p) {
match txn.add_file(&file, m.is_dir()) {
Ok(()) => {}
Err(libpijul::Error::AlreadyAdded) => {
eprintln!("{:?} is already in the repository", file_)
}
Err(e) => return Err(e.into()),
}
continue;
}
}
return Err(Error::InvalidPath { path: file_ });
}
}
Operation::Remove => {
for file in &files[..] {
debug!("file: {:?} {:?}", file, wd.join(file));
let p = wd.join(file).canonicalize()?;
debug!("p: {:?}", p);
if let Ok(file) = repo_dir.relativize(&p) {
debug!("remove_file {:?}", file);
txn.remove_file(&file)?
} else {
return Err(Error::InvalidPath {
path: file.to_path_buf(),
});
}
}
}
}
txn.commit()?;
Ok(())
}