Compare commits
6 Commits
9956727cc9
...
b16058b4d3
Author | SHA1 | Date | |
---|---|---|---|
|
b16058b4d3 | ||
|
ea2b0772af | ||
|
b911ad8606 | ||
|
0bf5fb76e0 | ||
|
7a34b3c79b | ||
|
cbbf185b1a |
@ -1,9 +1,9 @@
|
|||||||
use clap::Values;
|
use clap::Values;
|
||||||
use crate::utils::{self, nextsyncignore};
|
use crate::utils::{self};
|
||||||
|
use crate::utils::nextsyncignore::{self, ignore_file};
|
||||||
use crate::store;
|
use crate::store;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use glob::glob;
|
|
||||||
|
|
||||||
pub struct AddArgs<'a> {
|
pub struct AddArgs<'a> {
|
||||||
pub files: Values<'a>,
|
pub files: Values<'a>,
|
||||||
@ -13,13 +13,21 @@ pub struct AddArgs<'a> {
|
|||||||
pub fn add(args: AddArgs) {
|
pub fn add(args: AddArgs) {
|
||||||
let mut index_file = store::index::open();
|
let mut index_file = store::index::open();
|
||||||
let mut added_files: Vec<String> = vec![];
|
let mut added_files: Vec<String> = 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();
|
let file_vec: Vec<&str> = args.files.collect();
|
||||||
for file in file_vec {
|
for file in file_vec {
|
||||||
|
if !args.force && ignore_file(&file.to_string(), rules.clone(), &mut ignored_f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let path = Path::new(file);
|
let path = Path::new(file);
|
||||||
match path.exists() {
|
match path.exists() {
|
||||||
true => {
|
true => {
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
|
|
||||||
added_files.push(String::from(path.to_str().unwrap()));
|
added_files.push(String::from(path.to_str().unwrap()));
|
||||||
add_folder_content(path.to_path_buf(), &mut added_files);
|
add_folder_content(path.to_path_buf(), &mut added_files);
|
||||||
} else {
|
} else {
|
||||||
@ -33,19 +41,16 @@ pub fn add(args: AddArgs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check ignored file if not forced
|
if ignored_f.len() > 0 {
|
||||||
if !args.force {
|
|
||||||
let (ignored, ignored_files) = nextsyncignore::ignore_files(&mut added_files);
|
|
||||||
if ignored {
|
|
||||||
// todo multiple nextsyncignore
|
// todo multiple nextsyncignore
|
||||||
println!("The following paths are ignored by your .nextsyncignore file:");
|
println!("The following paths are ignored by your .nextsyncignore file:");
|
||||||
for file in ignored_files {
|
for file in ignored_f {
|
||||||
println!("{}", file);
|
println!("{}", file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// save all added_files in index
|
// save all added_files in index
|
||||||
|
// todo avoid duplication
|
||||||
for file in added_files {
|
for file in added_files {
|
||||||
match writeln!(index_file, "{}", file) {
|
match writeln!(index_file, "{}", file) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
use std::fs::OpenOptions;
|
|
||||||
use std::fs::DirBuilder;
|
use std::fs::DirBuilder;
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::io::{self, Cursor};
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use clap::Values;
|
use clap::Values;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use xml::reader::{EventReader, XmlEvent};
|
|
||||||
use crate::services::api::ApiError;
|
use crate::services::api::ApiError;
|
||||||
use crate::services::list_folders::ListFolders;
|
use crate::services::list_folders::ListFolders;
|
||||||
use crate::services::download_files::DownloadFiles;
|
use crate::services::download_files::DownloadFiles;
|
||||||
use crate::store::object;
|
use crate::store::object;
|
||||||
use crate::commands;
|
use crate::commands;
|
||||||
|
use crate::utils::api::{get_local_path, get_local_path_t};
|
||||||
use crate::global::global::{DIR_PATH, set_dir_path};
|
use crate::global::global::{DIR_PATH, set_dir_path};
|
||||||
|
|
||||||
pub fn clone(remote: Values<'_>) {
|
pub fn clone(remote: Values<'_>) {
|
||||||
@ -23,11 +20,10 @@ pub fn clone(remote: Values<'_>) {
|
|||||||
None => {
|
None => {
|
||||||
eprintln!("No username found");
|
eprintln!("No username found");
|
||||||
todo!();
|
todo!();
|
||||||
""
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let local_path = match d.clone() {
|
let ref_path = match d.clone() {
|
||||||
Some(dir) => Path::new(&dir).to_owned(),
|
Some(dir) => Path::new(&dir).to_owned(),
|
||||||
None => {
|
None => {
|
||||||
let iter = Path::new(dist_path_str).iter();
|
let iter = Path::new(dist_path_str).iter();
|
||||||
@ -52,16 +48,33 @@ pub fn clone(remote: Values<'_>) {
|
|||||||
url_request.push_str(folder.as_str());
|
url_request.push_str(folder.as_str());
|
||||||
|
|
||||||
// request folder content
|
// request folder content
|
||||||
let mut body = Default::default();
|
let mut objs = vec![];
|
||||||
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
||||||
body = ListFolders::new(url_request.as_str())
|
let res = ListFolders::new(url_request.as_str())
|
||||||
|
.gethref()
|
||||||
.send_with_res()
|
.send_with_res()
|
||||||
.await;
|
.await;
|
||||||
|
objs = match res {
|
||||||
|
Ok(o) => o,
|
||||||
|
Err(ApiError::IncorrectRequest(err)) => {
|
||||||
|
eprintln!("fatal: {}", err.status());
|
||||||
|
std::process::exit(1);
|
||||||
|
},
|
||||||
|
Err(ApiError::EmptyError(_)) => {
|
||||||
|
eprintln!("Failed to get body");
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
Err(ApiError::RequestError(err)) => {
|
||||||
|
eprintln!("fatal: {}", err);
|
||||||
|
std::process::exit(1);
|
||||||
|
},
|
||||||
|
Err(ApiError::Unexpected(_)) => todo!()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// create folder
|
// create folder
|
||||||
if first_iter {
|
if first_iter {
|
||||||
if DirBuilder::new().create(local_path.clone()).is_err() {
|
if DirBuilder::new().create(ref_path.clone()).is_err() {
|
||||||
eprintln!("fatal: directory already exist");
|
eprintln!("fatal: directory already exist");
|
||||||
// destination path 'path' already exists and is not an empty directory.
|
// destination path 'path' already exists and is not an empty directory.
|
||||||
//std::process::exit(1);
|
//std::process::exit(1);
|
||||||
@ -70,70 +83,54 @@ pub fn clone(remote: Values<'_>) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// create folder
|
// create folder
|
||||||
let local_folder = get_local_path(folder, local_path.clone(), username, dist_path_str);
|
let local_folder = get_local_path(folder, ref_path.clone(), username, dist_path_str);
|
||||||
if let Err(err) = DirBuilder::new().recursive(true).create(local_folder.clone()) {
|
if let Err(err) = DirBuilder::new().recursive(true).create(local_folder.clone()) {
|
||||||
eprintln!("error: cannot create directory {}: {}", local_folder.display(), err);
|
eprintln!("error: cannot create directory {}: {}", local_folder.display(), err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add tree
|
// add tree
|
||||||
let path_folder = local_folder.strip_prefix(local_path.clone()).unwrap();
|
let path_folder = local_folder.strip_prefix(ref_path.clone()).unwrap();
|
||||||
if object::add_tree(&path_folder).is_err() {
|
if object::add_tree(&path_folder).is_err() {
|
||||||
eprintln!("error: cannot store object {}", path_folder.display());
|
eprintln!("error: cannot store object {}", path_folder.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find folders and files in response
|
// find folders and files in response
|
||||||
let objects = get_objects_xml(body);
|
let mut iter = objs.iter();
|
||||||
let mut iter = objects.iter();
|
iter.next(); // jump first element which is the folder cloned
|
||||||
iter.next(); // jump first element which the folder fetched
|
|
||||||
for object in iter {
|
for object in iter {
|
||||||
if object.chars().last().unwrap() == '/' {
|
if object.href.clone().unwrap().chars().last().unwrap() == '/' {
|
||||||
folders.push(object.to_string());
|
folders.push(object.href.clone().unwrap().to_string());
|
||||||
} else {
|
} else {
|
||||||
files.push(object.to_string());
|
files.push(object.href.clone().unwrap().to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
first_iter = false;
|
first_iter = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
download_files(&domain, local_path.clone(), username, dist_path_str, files);
|
download_files(ref_path.clone(), files);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_local_path(p: String, local_p: PathBuf, username: &str, dist_p: &str) -> PathBuf {
|
fn download_files(local_p: PathBuf, files: Vec<String>) {
|
||||||
let mut final_p = Path::new(p.as_str());
|
for remote_file in files {
|
||||||
final_p = final_p.strip_prefix("/remote.php/dav/files/").unwrap();
|
|
||||||
final_p = final_p.strip_prefix(username.clone()).unwrap();
|
|
||||||
let dist_p = Path::new(dist_p).strip_prefix("/");
|
|
||||||
final_p = final_p.strip_prefix(dist_p.unwrap()).unwrap();
|
|
||||||
local_p.clone().join(final_p.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_file(path: PathBuf, content: &Vec<u8>, local_p: PathBuf) -> io::Result<()> {
|
|
||||||
let mut f = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.open(path.clone())?;
|
|
||||||
|
|
||||||
f.write_all(&content)?;
|
|
||||||
|
|
||||||
let relative_p = Path::new(&path).strip_prefix(local_p).unwrap();
|
|
||||||
object::add_blob(relative_p, "tmpdate")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn download_files(domain: &str, local_p: PathBuf, username: &str, dist_p: &str, files: Vec<String>) {
|
|
||||||
for file in files {
|
|
||||||
let mut url_request = String::from(domain.clone());
|
|
||||||
url_request.push_str(file.as_str());
|
|
||||||
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
||||||
match DownloadFiles::new(url_request.as_str()).send_with_err().await {
|
let res = DownloadFiles::new()
|
||||||
Ok(b) => {
|
.set_url_with_remote(remote_file.as_str())
|
||||||
let p_to_save = get_local_path(file.clone(), local_p.clone(), username, dist_p);
|
.save(local_p.clone()).await;
|
||||||
|
|
||||||
if let Err(_) = write_file(p_to_save, &b, local_p.clone()) {
|
match res {
|
||||||
eprintln!("error writing {}", file);
|
Ok(()) => {
|
||||||
|
|
||||||
|
let s = &get_local_path_t(&remote_file.clone());
|
||||||
|
let ss = s.strip_prefix("/").unwrap();
|
||||||
|
let relative_p = Path::new(ss);
|
||||||
|
if let Err(_) = object::add_blob(relative_p, "tmpdate") {
|
||||||
|
eprintln!("error saving reference of {}", remote_file.clone());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Err(ApiError::Unexpected(_)) => {
|
||||||
|
eprintln!("error writing {}", remote_file);
|
||||||
|
},
|
||||||
Err(ApiError::IncorrectRequest(err)) => {
|
Err(ApiError::IncorrectRequest(err)) => {
|
||||||
eprintln!("fatal: {}", err.status());
|
eprintln!("fatal: {}", err.status());
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
@ -148,37 +145,6 @@ fn download_files(domain: &str, local_p: PathBuf, username: &str, dist_p: &str,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_objects_xml(xml: String) -> Vec<String> {
|
|
||||||
let cursor = Cursor::new(xml);
|
|
||||||
let parser = EventReader::new(cursor);
|
|
||||||
|
|
||||||
let mut should_get = false;
|
|
||||||
let mut objects: Vec<String> = vec![];
|
|
||||||
|
|
||||||
for event in parser {
|
|
||||||
match event {
|
|
||||||
Ok(XmlEvent::StartElement { name, .. }) => {
|
|
||||||
should_get = name.local_name == "href";
|
|
||||||
}
|
|
||||||
Ok(XmlEvent::Characters(text)) => {
|
|
||||||
if !text.trim().is_empty() && should_get {
|
|
||||||
objects.push(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(XmlEvent::EndElement { .. }) => {
|
|
||||||
should_get = false;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error: {}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
objects
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo allow http
|
|
||||||
fn get_url_props(url: &str) -> (String, Option<&str>, &str) {
|
fn get_url_props(url: &str) -> (String, Option<&str>, &str) {
|
||||||
let mut username = None;
|
let mut username = None;
|
||||||
let mut domain = "";
|
let mut domain = "";
|
||||||
@ -193,7 +159,7 @@ fn get_url_props(url: &str) -> (String, Option<&str>, &str) {
|
|||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
}
|
}
|
||||||
} else if url.find("?").is_some() {
|
} else if url.find("?").is_some() { // from browser url
|
||||||
let re = Regex::new(r"((https?://)?.+?)/.+dir=(.+?)&").unwrap();
|
let re = Regex::new(r"((https?://)?.+?)/.+dir=(.+?)&").unwrap();
|
||||||
match re.captures_iter(url).last() {
|
match re.captures_iter(url).last() {
|
||||||
Some(cap) => {
|
Some(cap) => {
|
||||||
@ -218,9 +184,13 @@ fn get_url_props(url: &str) -> (String, Option<&str>, &str) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let re = Regex::new(r"^http://").unwrap();
|
||||||
|
if !re.is_match(domain) {
|
||||||
let re = Regex::new(r"(^https?://)?").unwrap();
|
let re = Regex::new(r"(^https?://)?").unwrap();
|
||||||
let secure_domain = re.replace(domain, "https://").to_string();
|
let secure_domain = re.replace(domain, "https://").to_string();
|
||||||
(secure_domain, username, path)
|
return (secure_domain, username, path);
|
||||||
|
}
|
||||||
|
(domain.to_string(), username, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -231,21 +201,22 @@ mod tests {
|
|||||||
fn test_get_url_props() {
|
fn test_get_url_props() {
|
||||||
let p = "/foo/bar";
|
let p = "/foo/bar";
|
||||||
let u = Some("user");
|
let u = Some("user");
|
||||||
let d = String::from("https://nextcloud.com");
|
let d = String::from("http://nextcloud.com");
|
||||||
let ld = String::from("https://nextcloud.example.com");
|
let sd = String::from("https://nextcloud.com");
|
||||||
assert_eq!(get_url_props("user@nextcloud.com/remote.php/dav/files/user/foo/bar"), (d.clone(), u, p));
|
let sld = String::from("https://nextcloud.example.com");
|
||||||
assert_eq!(get_url_props("user@nextcloud.com/foo/bar"), (d.clone(), u, p));
|
let ld = String::from("http://nextcloud.example.com");
|
||||||
assert_eq!(get_url_props("user@nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (ld.clone(), u, p));
|
assert_eq!(get_url_props("user@nextcloud.com/remote.php/dav/files/user/foo/bar"), (sd.clone(), u, p));
|
||||||
assert_eq!(get_url_props("user@nextcloud.example.com/foo/bar"), (ld.clone(), u, p));
|
assert_eq!(get_url_props("user@nextcloud.com/foo/bar"), (sd.clone(), u, p));
|
||||||
assert_eq!(get_url_props("https://nextcloud.example.com/apps/files/?dir=/foo/bar&fileid=166666"), (ld.clone(), None, p));
|
assert_eq!(get_url_props("user@nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (sld.clone(), u, p));
|
||||||
assert_eq!(get_url_props("https://nextcloud.com/apps/files/?dir=/foo/bar&fileid=166666"), (d.clone(), None, p));
|
assert_eq!(get_url_props("user@nextcloud.example.com/foo/bar"), (sld.clone(), u, p));
|
||||||
|
assert_eq!(get_url_props("https://nextcloud.example.com/apps/files/?dir=/foo/bar&fileid=166666"), (sld.clone(), None, p));
|
||||||
|
assert_eq!(get_url_props("https://nextcloud.com/apps/files/?dir=/foo/bar&fileid=166666"), (sd.clone(), None, p));
|
||||||
assert_eq!(get_url_props("http://nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (ld.clone(), u, p));
|
assert_eq!(get_url_props("http://nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (ld.clone(), u, p));
|
||||||
assert_eq!(get_url_props("https://nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (ld.clone(), u, p));
|
assert_eq!(get_url_props("https://nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (sld.clone(), u, p));
|
||||||
assert_eq!(get_url_props("http://nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (ld.clone(), u, p));
|
assert_eq!(get_url_props("http://nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (ld.clone(), u, p));
|
||||||
assert_eq!(get_url_props("nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (ld.clone(), u, p));
|
assert_eq!(get_url_props("nextcloud.example.com/remote.php/dav/files/user/foo/bar"), (sld.clone(), u, p));
|
||||||
assert_eq!(get_url_props("https://nextcloud.example.com/foo/bar"), (ld.clone(), None, p));
|
assert_eq!(get_url_props("https://nextcloud.example.com/foo/bar"), (sld.clone(), None, p));
|
||||||
assert_eq!(get_url_props("http://nextcloud.example.com/foo/bar"), (ld.clone(), None, p));
|
assert_eq!(get_url_props("http://nextcloud.example.com/foo/bar"), (ld.clone(), None, p));
|
||||||
assert_eq!(get_url_props("nextcloud.example.com/foo/bar"), (ld.clone(), None, p));
|
assert_eq!(get_url_props("nextcloud.example.com/foo/bar"), (sld.clone(), None, p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,23 +76,14 @@ fn get_staged(objs: &mut Vec<Obj>) -> Vec<Obj> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut to_remove: Vec<usize> = vec![];
|
objs.retain(|obj| {
|
||||||
let mut index = 0;
|
|
||||||
for obj in &mut *objs {
|
|
||||||
dbg!(obj.clone().path.to_str().unwrap());
|
|
||||||
if indexes.contains(obj.clone().path.to_str().unwrap()) {
|
if indexes.contains(obj.clone().path.to_str().unwrap()) {
|
||||||
staged_objs.push(obj.clone());
|
staged_objs.push(obj.clone());
|
||||||
to_remove.push(index);
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
index += 1;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
let mut offset = 0;
|
|
||||||
for i in to_remove {
|
|
||||||
objs.remove(i + offset.clone());
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
staged_objs
|
staged_objs
|
||||||
}
|
}
|
||||||
@ -260,35 +251,29 @@ fn print_staged_object(obj: Obj) {
|
|||||||
|
|
||||||
fn remove_duplicate(hashes: &mut HashMap<String, Obj>, objects: &mut Vec<String>, remove_option: RemoveSide) -> Vec<String> {
|
fn remove_duplicate(hashes: &mut HashMap<String, Obj>, objects: &mut Vec<String>, remove_option: RemoveSide) -> Vec<String> {
|
||||||
let mut hasher = Sha1::new();
|
let mut hasher = Sha1::new();
|
||||||
let mut to_remove: Vec<usize> = vec![];
|
|
||||||
let mut i = 0;
|
|
||||||
let mut duplicate = vec![];
|
let mut duplicate = vec![];
|
||||||
|
|
||||||
for object in &mut *objects {
|
objects.retain(|obj| {
|
||||||
// hash the object
|
// hash the object
|
||||||
hasher.input_str(object);
|
hasher.input_str(obj);
|
||||||
let hash = hasher.result_str();
|
let hash = hasher.result_str();
|
||||||
hasher.reset();
|
hasher.reset();
|
||||||
|
|
||||||
// find it on the list of hashes
|
// find it on the list of hashes
|
||||||
if hashes.contains_key(&hash) {
|
if hashes.contains_key(&hash) {
|
||||||
duplicate.push(object.clone());
|
duplicate.push(obj.clone());
|
||||||
|
|
||||||
|
// remove from hashes
|
||||||
if remove_option == RemoveSide::Left || remove_option == RemoveSide::Both {
|
if remove_option == RemoveSide::Left || remove_option == RemoveSide::Both {
|
||||||
hashes.remove(&hash);
|
hashes.remove(&hash);
|
||||||
}
|
}
|
||||||
if remove_option == RemoveSide::Right || remove_option == RemoveSide::Both {
|
|
||||||
to_remove.push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove all objects existing in the list of hashes
|
// remove from objects
|
||||||
i = 0;
|
remove_option != RemoveSide::Right && remove_option != RemoveSide::Both
|
||||||
for index in to_remove {
|
} else {
|
||||||
objects.remove(index-i);
|
true
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
duplicate
|
duplicate
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ fn main() {
|
|||||||
if let Some(remote) = matches.values_of("remote") {
|
if let Some(remote) = matches.values_of("remote") {
|
||||||
commands::clone::clone(remote);
|
commands::clone::clone(remote);
|
||||||
}
|
}
|
||||||
} else if let Some(matches) = matches.subcommand_matches("push") {
|
} else if let Some(_matches) = matches.subcommand_matches("push") {
|
||||||
commands::push::push();
|
commands::push::push();
|
||||||
} else if let Some(matches) = matches.subcommand_matches("config") {
|
} else if let Some(matches) = matches.subcommand_matches("config") {
|
||||||
if let Some(mut var) = matches.values_of("variable") {
|
if let Some(mut var) = matches.values_of("variable") {
|
||||||
|
@ -9,6 +9,7 @@ pub enum ApiError {
|
|||||||
IncorrectRequest(reqwest::Response),
|
IncorrectRequest(reqwest::Response),
|
||||||
EmptyError(reqwest::Error),
|
EmptyError(reqwest::Error),
|
||||||
RequestError(reqwest::Error),
|
RequestError(reqwest::Error),
|
||||||
|
Unexpected(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ApiBuilder {
|
pub struct ApiBuilder {
|
||||||
@ -47,6 +48,17 @@ impl ApiBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_request_remote(&mut self, meth: Method, path: &str) -> &mut ApiBuilder {
|
||||||
|
dotenv().ok();
|
||||||
|
let host = env::var("HOST").unwrap();
|
||||||
|
let mut url = String::from(host);
|
||||||
|
url.push_str("/");
|
||||||
|
url.push_str(path);
|
||||||
|
dbg!(url.clone());
|
||||||
|
self.request = Some(self.client.request(meth, url));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn set_auth(&mut self) -> &mut ApiBuilder {
|
fn set_auth(&mut self) -> &mut ApiBuilder {
|
||||||
// todo if not exist
|
// todo if not exist
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
@ -1,23 +1,34 @@
|
|||||||
use crate::services::api::{ApiBuilder, ApiError};
|
use crate::services::api::{ApiBuilder, ApiError};
|
||||||
use reqwest::{Method, IntoUrl, Response, Error};
|
use std::path::PathBuf;
|
||||||
|
use reqwest::{Method, Response, Error};
|
||||||
|
use crate::utils::api::get_local_path_t;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
pub struct DownloadFiles {
|
pub struct DownloadFiles {
|
||||||
api_builder: ApiBuilder,
|
api_builder: ApiBuilder,
|
||||||
|
path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DownloadFiles {
|
impl DownloadFiles {
|
||||||
pub fn new<U: IntoUrl>(url: U) -> Self {
|
pub fn new() -> Self {
|
||||||
DownloadFiles {
|
DownloadFiles {
|
||||||
api_builder: ApiBuilder::new()
|
api_builder: ApiBuilder::new(),
|
||||||
.set_request(Method::GET, url),
|
path: String::from(""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_url_with_remote(&mut self, url: &str) -> &mut DownloadFiles {
|
||||||
|
self.path = get_local_path_t(url.clone()).strip_prefix("/").unwrap().to_string();
|
||||||
|
self.api_builder.build_request_remote(Method::GET, url);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send(&mut self) -> Result<Response, Error> {
|
pub async fn send(&mut self) -> Result<Response, Error> {
|
||||||
self.api_builder.send().await
|
self.api_builder.send().await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_with_err(mut self) -> Result<Vec<u8>, ApiError> {
|
pub async fn _send_with_err(mut self) -> Result<Vec<u8>, ApiError> {
|
||||||
let res = self.send().await.map_err(ApiError::RequestError)?;
|
let res = self.send().await.map_err(ApiError::RequestError)?;
|
||||||
if res.status().is_success() {
|
if res.status().is_success() {
|
||||||
let body = res.bytes().await.map_err(ApiError::EmptyError)?;
|
let body = res.bytes().await.map_err(ApiError::EmptyError)?;
|
||||||
@ -26,4 +37,28 @@ impl DownloadFiles {
|
|||||||
Err(ApiError::IncorrectRequest(res))
|
Err(ApiError::IncorrectRequest(res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn save(&mut self, local_path: PathBuf) -> Result<(), ApiError> {
|
||||||
|
let p = local_path.join(PathBuf::from(self.path.clone()));
|
||||||
|
let res = self.send().await.map_err(ApiError::RequestError)?;
|
||||||
|
if res.status().is_success() {
|
||||||
|
let body = res.bytes().await.map_err(ApiError::EmptyError)?;
|
||||||
|
match DownloadFiles::write_file(p, &body.to_vec()) {
|
||||||
|
Err(_) => Err(ApiError::Unexpected(String::from(""))),
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ApiError::IncorrectRequest(res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_file(path: PathBuf, content: &Vec<u8>) -> io::Result<()> {
|
||||||
|
let mut f = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(path.clone())?;
|
||||||
|
|
||||||
|
f.write_all(&content)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,31 @@
|
|||||||
use crate::services::api::{ApiBuilder, ApiError};
|
use crate::services::api::{ApiBuilder, ApiError};
|
||||||
|
use xml::reader::{EventReader, XmlEvent};
|
||||||
|
use std::io::Cursor;
|
||||||
use reqwest::{Method, IntoUrl, Response, Error};
|
use reqwest::{Method, IntoUrl, Response, Error};
|
||||||
|
|
||||||
|
pub struct FolderContent {
|
||||||
|
pub href: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for FolderContent {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
FolderContent {
|
||||||
|
href: self.href.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderContent {
|
||||||
|
fn new() -> Self {
|
||||||
|
FolderContent {
|
||||||
|
href: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ListFolders {
|
pub struct ListFolders {
|
||||||
api_builder: ApiBuilder,
|
api_builder: ApiBuilder,
|
||||||
|
xml_balises: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListFolders {
|
impl ListFolders {
|
||||||
@ -10,14 +33,20 @@ impl ListFolders {
|
|||||||
ListFolders {
|
ListFolders {
|
||||||
api_builder: ApiBuilder::new()
|
api_builder: ApiBuilder::new()
|
||||||
.set_request(Method::from_bytes(b"PROPFIND").unwrap(), url),
|
.set_request(Method::from_bytes(b"PROPFIND").unwrap(), url),
|
||||||
|
xml_balises: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gethref(&mut self) -> &mut ListFolders {
|
||||||
|
self.xml_balises.push(String::from("href"));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send(&mut self) -> Result<Response, Error> {
|
pub async fn send(&mut self) -> Result<Response, Error> {
|
||||||
self.api_builder.send().await
|
self.api_builder.send().await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_with_err(mut self) -> Result<String, ApiError> {
|
pub async fn send_with_err(&mut self) -> Result<String, ApiError> {
|
||||||
let res = self.send().await.map_err(ApiError::RequestError)?;
|
let res = self.send().await.map_err(ApiError::RequestError)?;
|
||||||
if res.status().is_success() {
|
if res.status().is_success() {
|
||||||
let body = res.text().await.map_err(ApiError::EmptyError)?;
|
let body = res.text().await.map_err(ApiError::EmptyError)?;
|
||||||
@ -27,21 +56,59 @@ impl ListFolders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_with_res(self) -> String {
|
pub async fn send_with_res(&mut self) -> Result<Vec<FolderContent>, ApiError> {
|
||||||
match self.send_with_err().await {
|
match self.send_with_err().await {
|
||||||
Ok(body) => body,
|
Ok(body) => Ok(self.parse(body)),
|
||||||
Err(ApiError::IncorrectRequest(err)) => {
|
Err(err) => Err(err),
|
||||||
eprintln!("fatal: {}", err.status());
|
|
||||||
std::process::exit(1);
|
|
||||||
},
|
|
||||||
Err(ApiError::EmptyError(_)) => {
|
|
||||||
eprintln!("Failed to get body");
|
|
||||||
String::from("")
|
|
||||||
}
|
}
|
||||||
Err(ApiError::RequestError(err)) => {
|
}
|
||||||
eprintln!("fatal: {}", err);
|
|
||||||
std::process::exit(1);
|
pub fn parse(&self, xml: String) -> Vec<FolderContent> {
|
||||||
|
let cursor = Cursor::new(xml);
|
||||||
|
let parser = EventReader::new(cursor);
|
||||||
|
|
||||||
|
let mut should_get = false;
|
||||||
|
let mut values: Vec<FolderContent> = vec![];
|
||||||
|
|
||||||
|
let mut iter = self.xml_balises.iter();
|
||||||
|
let mut val = iter.next();
|
||||||
|
let mut content = FolderContent::new();
|
||||||
|
|
||||||
|
for event in parser {
|
||||||
|
match event {
|
||||||
|
Ok(XmlEvent::StartElement { name, .. }) => {
|
||||||
|
if let Some(v) = val.clone() {
|
||||||
|
should_get = &name.local_name == v;
|
||||||
|
} else {
|
||||||
|
// end of balises to get then start over for next object
|
||||||
|
values.push(content.clone());
|
||||||
|
iter = self.xml_balises.iter();
|
||||||
|
val = iter.next();
|
||||||
|
content = FolderContent::new();
|
||||||
|
if let Some(v) = val.clone() {
|
||||||
|
should_get = &name.local_name == v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(XmlEvent::Characters(text)) => {
|
||||||
|
if !text.trim().is_empty() && should_get {
|
||||||
|
match val.unwrap().as_str() {
|
||||||
|
"href" => content.href = Some(text),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
val = iter.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(XmlEvent::EndElement { .. }) => {
|
||||||
|
should_get = false;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,25 +35,25 @@ impl ReqProps {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getcontenttype(&mut self) -> &mut ReqProps {
|
pub fn _getcontenttype(&mut self) -> &mut ReqProps {
|
||||||
self.xml_list.push(String::from("getcontenttype"));
|
self.xml_list.push(String::from("getcontenttype"));
|
||||||
self.xml_payload.push_str(r#"<d:getcontenttype/>"#);
|
self.xml_payload.push_str(r#"<d:getcontenttype/>"#);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getpermissions(&mut self) -> &mut ReqProps {
|
pub fn _getpermissions(&mut self) -> &mut ReqProps {
|
||||||
self.xml_list.push(String::from("permissions"));
|
self.xml_list.push(String::from("permissions"));
|
||||||
self.xml_payload.push_str(r#"<oc:permissions/>"#);
|
self.xml_payload.push_str(r#"<oc:permissions/>"#);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getressourcetype(&mut self) -> &mut ReqProps {
|
pub fn _getressourcetype(&mut self) -> &mut ReqProps {
|
||||||
self.xml_list.push(String::from("resourcetype"));
|
self.xml_list.push(String::from("resourcetype"));
|
||||||
self.xml_payload.push_str(r#"<d:resourcetype/>"#);
|
self.xml_payload.push_str(r#"<d:resourcetype/>"#);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getetag(&mut self) -> &mut ReqProps {
|
pub fn _getetag(&mut self) -> &mut ReqProps {
|
||||||
self.xml_list.push(String::from("getetag"));
|
self.xml_list.push(String::from("getetag"));
|
||||||
self.xml_payload.push_str(r#"<d:getetag/>"#);
|
self.xml_payload.push_str(r#"<d:getetag/>"#);
|
||||||
self
|
self
|
||||||
|
@ -57,7 +57,7 @@ pub fn add_tree(path: &Path) -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn rm_blob(path: &Path) -> io::Result<()> {
|
pub fn rm_blob(path: &Path) -> io::Result<()> {
|
||||||
let (line, hash, name) = parse_path(path.clone(), true);
|
let (line, hash, _) = parse_path(path.clone(), true);
|
||||||
|
|
||||||
// remove blob reference to parent
|
// remove blob reference to parent
|
||||||
if path.iter().count() == 1 {
|
if path.iter().count() == 1 {
|
||||||
@ -84,7 +84,6 @@ pub fn rm_blob(path: &Path) -> io::Result<()> {
|
|||||||
|
|
||||||
pub fn add_blob(path: &Path, date: &str) -> io::Result<()> {
|
pub fn add_blob(path: &Path, date: &str) -> io::Result<()> {
|
||||||
let (line, hash, name) = parse_path(path.clone(), true);
|
let (line, hash, name) = parse_path(path.clone(), true);
|
||||||
|
|
||||||
// add blob reference to parent
|
// add blob reference to parent
|
||||||
if path.iter().count() == 1 {
|
if path.iter().count() == 1 {
|
||||||
head::add_line(line)?;
|
head::add_line(line)?;
|
||||||
@ -112,7 +111,7 @@ fn hash_obj(obj: &str) -> (String, String) {
|
|||||||
(String::from(dir), String::from(res))
|
(String::from(dir), String::from(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn object_path(obj: &str) -> PathBuf {
|
fn _object_path(obj: &str) -> PathBuf {
|
||||||
let mut root = match path::objects() {
|
let mut root = match path::objects() {
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
None => todo!(),
|
None => todo!(),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pub mod path;
|
pub mod path;
|
||||||
pub mod read;
|
pub mod read;
|
||||||
pub mod nextsyncignore;
|
pub mod nextsyncignore;
|
||||||
|
pub mod api;
|
||||||
|
23
src/utils/api.rs
Normal file
23
src/utils/api.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use std::path::{PathBuf, Path};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
pub fn get_local_path(p: String, local_p: PathBuf, username: &str, dist_p: &str) -> PathBuf {
|
||||||
|
let mut final_p = Path::new(p.as_str());
|
||||||
|
final_p = final_p.strip_prefix("/remote.php/dav/files/").unwrap();
|
||||||
|
final_p = final_p.strip_prefix(username.clone()).unwrap();
|
||||||
|
let dist_p = Path::new(dist_p).strip_prefix("/");
|
||||||
|
final_p = final_p.strip_prefix(dist_p.unwrap()).unwrap();
|
||||||
|
local_p.clone().join(final_p.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_local_path_t(p: &str) -> String {
|
||||||
|
dbg!(p.clone());
|
||||||
|
let username = env::var("USERNAME").unwrap();
|
||||||
|
let root = env::var("ROOT").unwrap();
|
||||||
|
let mut final_p = p;
|
||||||
|
final_p = final_p.strip_prefix("/remote.php/dav/files/").unwrap();
|
||||||
|
final_p = final_p.strip_prefix(&username).unwrap();
|
||||||
|
final_p = final_p.strip_prefix("/").unwrap();
|
||||||
|
final_p = final_p.strip_prefix(&root).unwrap();
|
||||||
|
final_p.to_string()
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
use crate::utils::{read, path};
|
use crate::utils::path;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Cursor, Lines, BufReader, empty, BufRead};
|
use std::io::{BufReader, BufRead};
|
||||||
|
|
||||||
fn read_lines() -> Result<Vec<String>, ()> {
|
pub fn read_lines() -> Result<Vec<String>, ()> {
|
||||||
if let Some(path) = path::nextsyncignore() {
|
if let Some(path) = path::nextsyncignore() {
|
||||||
let file = match File::open(path) {
|
let file = match File::open(path) {
|
||||||
Ok(buffer) => buffer,
|
Ok(buffer) => buffer,
|
||||||
@ -23,13 +23,11 @@ fn read_lines() -> Result<Vec<String>, ()> {
|
|||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ignore_files(files: &mut Vec<String>) -> (bool, Vec<String>) {
|
pub fn _ignore_files(files: &mut Vec<String>) -> (bool, Vec<String>) {
|
||||||
let mut ignored_f = vec![];
|
let mut ignored_f = vec![];
|
||||||
if let Some(path) = path::nextsyncignore() {
|
|
||||||
if let Ok(lines) = read_lines() {
|
if let Ok(lines) = read_lines() {
|
||||||
files.retain(|file| !ignore_file(file, lines.clone(), &mut ignored_f));
|
files.retain(|file| !ignore_file(file, lines.clone(), &mut ignored_f));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
(ignored_f.len() > 0, ignored_f)
|
(ignored_f.len() > 0, ignored_f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +54,7 @@ fn normalize_rule(l: String) -> String {
|
|||||||
|
|
||||||
pub fn ignore_file(path: &String, lines: Vec<String>, ignored_f: &mut Vec<String>) -> bool {
|
pub fn ignore_file(path: &String, lines: Vec<String>, ignored_f: &mut Vec<String>) -> bool {
|
||||||
let mut ignored = false;
|
let mut ignored = false;
|
||||||
for mut line in lines {
|
for line in lines {
|
||||||
if line.starts_with("!") {
|
if line.starts_with("!") {
|
||||||
if !ignored {
|
if !ignored {
|
||||||
continue;
|
continue;
|
||||||
|
Loading…
Reference in New Issue
Block a user