Compare commits
No commits in common. "9956727cc9e034ba380c6891524cd429bf55bd02" and "49d83c29006cc2f0e2e508c8dcfb6df1253813e2" have entirely different histories.
9956727cc9
...
49d83c2900
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -250,12 +250,6 @@ version = "0.3.55"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
|
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "glob"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.19"
|
version = "0.3.19"
|
||||||
@ -529,7 +523,6 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"glob",
|
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
|||||||
@ -15,4 +15,3 @@ colored = "2.0.0"
|
|||||||
xml-rs = "0.8.0"
|
xml-rs = "0.8.0"
|
||||||
regex = "1.8.3"
|
regex = "1.8.3"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
glob = "0.3.1"
|
|
||||||
|
|||||||
@ -1,74 +1,42 @@
|
|||||||
use clap::Values;
|
use clap::Values;
|
||||||
use crate::utils::{self, nextsyncignore};
|
use crate::utils;
|
||||||
use crate::store;
|
use crate::store;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use glob::glob;
|
|
||||||
|
|
||||||
pub struct AddArgs<'a> {
|
pub fn add(files: Values<'_>) {
|
||||||
pub files: Values<'a>,
|
let root = match utils::path::nextsync_root() {
|
||||||
pub force: bool,
|
Some(path) => path,
|
||||||
}
|
None => {
|
||||||
|
eprintln!("fatal: not a nextsync repository (or any of the parent directories): .nextsync");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub fn add(args: AddArgs) {
|
let mut index_path = root.clone();
|
||||||
let mut index_file = store::index::open();
|
index_path.push(".nextsync");
|
||||||
let mut added_files: Vec<String> = vec![];
|
let mut index_file = store::index::open(index_path);
|
||||||
|
// todo avoid duplicate
|
||||||
|
// ./folder ./folder/file
|
||||||
|
|
||||||
let file_vec: Vec<&str> = args.files.collect();
|
let file_vec: Vec<&str> = files.collect();
|
||||||
for file in file_vec {
|
for file in file_vec {
|
||||||
let path = Path::new(file);
|
let path = Path::new(file);
|
||||||
|
println!("{}", file);
|
||||||
match path.exists() {
|
match path.exists() {
|
||||||
true => {
|
true => {
|
||||||
if path.is_dir() {
|
match writeln!(index_file, "{}", path.display()) {
|
||||||
added_files.push(String::from(path.to_str().unwrap()));
|
Ok(()) => (),
|
||||||
add_folder_content(path.to_path_buf(), &mut added_files);
|
Err(err) => eprintln!("{}", err),
|
||||||
} else {
|
|
||||||
added_files.push(String::from(path.to_str().unwrap()));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false => {
|
false => {
|
||||||
// todo deleted file/folder verif if exists
|
match writeln!(index_file, "{}", path.display()) {
|
||||||
added_files.push(String::from(path.to_str().unwrap()));
|
Ok(()) => (),
|
||||||
|
Err(err) => eprintln!("{}", err),
|
||||||
|
}
|
||||||
|
// todo can be regex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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<String>) {
|
|
||||||
let mut folders: Vec<PathBuf> = 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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,9 @@ enum RemoveSide {
|
|||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum State {
|
pub enum State {
|
||||||
Default,
|
Default,
|
||||||
New,
|
New,
|
||||||
@ -37,7 +39,8 @@ pub fn status() {
|
|||||||
print_status(staged_objs, objs);
|
print_status(staged_objs, objs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Obj {
|
pub struct Obj {
|
||||||
pub otype: String,
|
pub otype: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -321,16 +324,10 @@ mod tests {
|
|||||||
let hash4 = hasher.result_str();
|
let hash4 = hasher.result_str();
|
||||||
hasher.reset();
|
hasher.reset();
|
||||||
|
|
||||||
let mut hashes = HashMap::new();
|
let mut hashes = HashSet::new();
|
||||||
let default_obj = Obj {
|
hashes.insert(hash1.clone());
|
||||||
otype: String::from("tree"),
|
hashes.insert(hash2.clone());
|
||||||
name: String::from("test"),
|
hashes.insert(hash4.clone());
|
||||||
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<String> = vec![];
|
let mut objects: Vec<String> = vec![];
|
||||||
objects.push(String::from("file1"));
|
objects.push(String::from("file1"));
|
||||||
@ -339,7 +336,7 @@ mod tests {
|
|||||||
remove_duplicate(&mut hashes, &mut objects, RemoveSide::Both);
|
remove_duplicate(&mut hashes, &mut objects, RemoveSide::Both);
|
||||||
dbg!(hashes.clone());
|
dbg!(hashes.clone());
|
||||||
dbg!(objects.clone());
|
dbg!(objects.clone());
|
||||||
assert_eq!(hashes.contains_key(&hash4), true);
|
assert_eq!(hashes.contains(&hash4), true);
|
||||||
assert_eq!(hashes.len(), 1);
|
assert_eq!(hashes.len(), 1);
|
||||||
assert_eq!(objects, vec!["file3"]);
|
assert_eq!(objects, vec!["file3"]);
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/main.rs
12
src/main.rs
@ -1,5 +1,4 @@
|
|||||||
use clap::{App, Arg, SubCommand};
|
use clap::{App, Arg, SubCommand};
|
||||||
use crate::commands::add::AddArgs;
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod services;
|
mod services;
|
||||||
@ -56,12 +55,6 @@ fn main() {
|
|||||||
.value_name("FILE")
|
.value_name("FILE")
|
||||||
.help("Files to add"),
|
.help("Files to add"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("force")
|
|
||||||
.short("f")
|
|
||||||
.long("force")
|
|
||||||
.help("Allow adding otherwise ignored files."),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("config")
|
SubCommand::with_name("config")
|
||||||
@ -92,10 +85,7 @@ fn main() {
|
|||||||
commands::status::status();
|
commands::status::status();
|
||||||
} else if let Some(matches) = matches.subcommand_matches("add") {
|
} else if let Some(matches) = matches.subcommand_matches("add") {
|
||||||
if let Some(files) = matches.values_of("files") {
|
if let Some(files) = matches.values_of("files") {
|
||||||
commands::add::add(AddArgs {
|
commands::add::add(files);
|
||||||
files: files,
|
|
||||||
force: matches.is_present("force"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else if let Some(_) = matches.subcommand_matches("reset") {
|
} else if let Some(_) = matches.subcommand_matches("reset") {
|
||||||
commands::reset::reset();
|
commands::reset::reset();
|
||||||
|
|||||||
@ -11,12 +11,7 @@ pub fn _read_only(mut path: PathBuf) -> File {
|
|||||||
.open(path).expect("Cannot open index file")
|
.open(path).expect("Cannot open index file")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open() -> File {
|
pub fn open(mut path: PathBuf) -> File {
|
||||||
let mut path = match path::nextsync() {
|
|
||||||
Some(p) => p,
|
|
||||||
None => todo!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
path.push("index");
|
path.push("index");
|
||||||
OpenOptions::new()
|
OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
@ -32,11 +27,12 @@ pub fn read_line(mut path: PathBuf) -> io::Result<io::Lines<io::BufReader<File>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn rm_line(line: &str) -> io::Result<()> {
|
pub fn rm_line(line: &str) -> io::Result<()> {
|
||||||
let mut root = match path::nextsync() {
|
let mut root = match path::nextsync_root() {
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
None => todo!(),
|
None => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
root.push(".nextsync");
|
||||||
root.push("index");
|
root.push("index");
|
||||||
read::rm_line(root, line)?;
|
read::rm_line(root, line)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
pub mod path;
|
pub mod path;
|
||||||
pub mod read;
|
pub mod read;
|
||||||
pub mod nextsyncignore;
|
|
||||||
|
|||||||
@ -1,144 +0,0 @@
|
|||||||
use crate::utils::{read, path};
|
|
||||||
use regex::Regex;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{Cursor, Lines, BufReader, empty, BufRead};
|
|
||||||
|
|
||||||
fn read_lines() -> Result<Vec<String>, ()> {
|
|
||||||
if let Some(path) = path::nextsyncignore() {
|
|
||||||
let file = match File::open(path) {
|
|
||||||
Ok(buffer) => buffer,
|
|
||||||
Err(_) => return Err(()),
|
|
||||||
};
|
|
||||||
let reader = BufReader::new(file);
|
|
||||||
let mut lines = vec![];
|
|
||||||
for line in reader.lines() {
|
|
||||||
if let Ok(l) = line {
|
|
||||||
lines.push(normalize_rule(l.clone()));
|
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(lines);
|
|
||||||
}
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ignore_files(files: &mut Vec<String>) -> (bool, Vec<String>) {
|
|
||||||
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(), &mut ignored_f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(ignored_f.len() > 0, ignored_f)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn normalize_rule(l: String) -> String {
|
|
||||||
let mut line = l;
|
|
||||||
|
|
||||||
// define / as root
|
|
||||||
let re = Regex::new("^(!)?/").unwrap();
|
|
||||||
line = re.replace_all(&line, "$1^").to_string();
|
|
||||||
|
|
||||||
// escape .
|
|
||||||
let re = Regex::new(r"\.").unwrap();
|
|
||||||
line = re.replace_all(&line, r"\.").to_string();
|
|
||||||
|
|
||||||
// add . before *
|
|
||||||
let re = Regex::new(r"\*").unwrap();
|
|
||||||
line = re.replace_all(&line, r".*").to_string();
|
|
||||||
|
|
||||||
// add optional .* at the end of /
|
|
||||||
let re = Regex::new(r"/$").unwrap();
|
|
||||||
line = re.replace_all(&line, r"(/.*)?").to_string();
|
|
||||||
line
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ignore_file(path: &String, lines: Vec<String>, ignored_f: &mut Vec<String>) -> bool {
|
|
||||||
let mut ignored = false;
|
|
||||||
for mut line in lines {
|
|
||||||
if line.starts_with("!") {
|
|
||||||
if !ignored {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let strip_line = line.strip_prefix("!").unwrap();
|
|
||||||
let re = Regex::new(&strip_line).unwrap();
|
|
||||||
if re.is_match(path) {
|
|
||||||
ignored = false;
|
|
||||||
}
|
|
||||||
} else if !ignored {
|
|
||||||
let re = Regex::new(&line).unwrap();
|
|
||||||
if re.is_match(path) {
|
|
||||||
ignored = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ignored {
|
|
||||||
ignored_f.push(path.to_string());
|
|
||||||
}
|
|
||||||
ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ignore_files() {
|
|
||||||
let lines_data = b"*.log\nexclude\n/logs/\n/build/target/\n**/*.swp\nsecret/\n";
|
|
||||||
let cursor = Cursor::new(lines_data);
|
|
||||||
let reader = BufReader::new(cursor);
|
|
||||||
let mut lines = vec![];
|
|
||||||
for line in reader.lines() {
|
|
||||||
if let Ok(l) = line {
|
|
||||||
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("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("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("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("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("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]
|
|
||||||
fn test_ignore_files_negation() {
|
|
||||||
let lines_data = b"*\n!*.log\n!*log.*\n";
|
|
||||||
let cursor = Cursor::new(lines_data);
|
|
||||||
let reader = BufReader::new(cursor);
|
|
||||||
let mut lines = vec![];
|
|
||||||
for line in reader.lines() {
|
|
||||||
if let Ok(l) = line {
|
|
||||||
lines.push(normalize_rule(l.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ -58,15 +58,3 @@ pub fn objects() -> Option<PathBuf> {
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nextsyncignore() -> Option<PathBuf> {
|
|
||||||
if let Some(mut path) = nextsync_root() {
|
|
||||||
path.push(".nextsyncignore");
|
|
||||||
if path.exists() {
|
|
||||||
return Some(path);
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user