Compare commits

..

9 Commits

Author SHA1 Message Date
grimhilt
4842a20024 organize import order 2023-06-19 18:31:03 +02:00
grimhilt
8b3ba64e48 move push factory in several files 2023-06-19 18:25:51 +02:00
grimhilt
f1d552a31c move async in services 2023-06-19 18:04:50 +02:00
grimhilt
4cde39dffd add lastmodified on folder 2023-06-19 17:34:18 +02:00
grimhilt
5d25429546 add lastmodified in store for files 2023-06-19 17:23:52 +02:00
grimhilt
e0d4c5efac add lastmodified in req props 2023-06-17 16:41:00 +02:00
grimhilt
1fd7948122 merge list folders and req props 2023-06-17 16:26:31 +02:00
grimhilt
eabf707844 minor cleaning 2023-06-17 15:54:09 +02:00
grimhilt
7cfd572ad0 cleaning clone 2023-06-17 15:36:18 +02:00
30 changed files with 570 additions and 505 deletions

72
Cargo.lock generated
View File

@ -11,6 +11,21 @@ dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
@ -73,6 +88,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "clap"
version = "2.34.0"
@ -376,6 +406,29 @@ dependencies = [
"tokio-native-tls",
]
[[package]]
name = "iana-time-zone"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "idna"
version = "0.3.0"
@ -526,6 +579,7 @@ dependencies = [
name = "nextsync"
version = "0.1.0"
dependencies = [
"chrono",
"clap",
"colored",
"dotenv",
@ -538,6 +592,15 @@ dependencies = [
"xml-rs",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
@ -1259,6 +1322,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets 0.48.0",
]
[[package]]
name = "windows-sys"
version = "0.42.0"

View File

@ -16,3 +16,4 @@ xml-rs = "0.8.0"
regex = "1.8.3"
lazy_static = "1.4.0"
glob = "0.3.1"
chrono = "0.4.26"

View File

@ -0,0 +1,21 @@
# Conventions
## Path Variables
Considering cloning:
* ``https://nextcloud.example.com/remote.php/dav/files/grimhilt/dir/dir_to_clone``
We have (in ``ApiProps`` for example):
* ``host``: ``https://nextcloud.example.com``
* ``username``: ``grimhilt``
* ``root``: ``/dir/dir_to_clone``
Concerning paths we have:
* ``remote_p``: ``/remote.php/dav/files/grimhilt/dir/dir_to_clone/D1/D1_F1.md``
* ``ref_p``: ``/home/grimhilt/dir_cloned``
* ``relative_p``: ``D1/D1_F1.md``
* ``abs_p``: ``/home/grimhilt/dir_cloned/D1_D1_F1.md``
Use prefix:
* ``p`` for Path or PathBuffer
* ``ps`` for String

View File

@ -1,9 +1,9 @@
use std::io::Write;
use std::path::{Path, PathBuf};
use clap::Values;
use crate::store;
use crate::utils::{self};
use crate::utils::nextsyncignore::{self, ignore_file};
use crate::store;
use std::path::{Path, PathBuf};
use std::io::Write;
pub struct AddArgs<'a> {
pub files: Values<'a>,

View File

@ -2,19 +2,19 @@ use std::fs::DirBuilder;
use std::path::{Path, PathBuf};
use clap::Values;
use regex::Regex;
use crate::services::api::ApiError;
use crate::services::list_folders::ListFolders;
use crate::services::download_files::DownloadFiles;
use crate::store::object;
use crate::commands;
use crate::utils::api::{get_local_path, get_local_path_t};
use crate::utils::api::ApiProps;
use crate::global::global::{DIR_PATH, set_dir_path};
use crate::services::api::ApiError;
use crate::services::req_props::{ReqProps, ObjProps};
use crate::services::download_files::DownloadFiles;
use crate::store::object::{self, add_blob, add_tree};
use crate::commands::init;
pub fn clone(remote: Values<'_>) {
let d = DIR_PATH.lock().unwrap().clone();
let url = remote.clone().next().unwrap();
let (domain, tmp_user, dist_path_str) = get_url_props(url);
let (host, tmp_user, dist_path_str) = get_url_props(url);
let username = match tmp_user {
Some(u) => u,
None => {
@ -22,6 +22,11 @@ pub fn clone(remote: Values<'_>) {
todo!();
}
};
let api_props = ApiProps {
host: host.clone(),
username: username.to_string(),
root: dist_path_str.to_string(),
};
let ref_path = match d.clone() {
Some(dir) => Path::new(&dir).to_owned(),
@ -34,27 +39,25 @@ pub fn clone(remote: Values<'_>) {
},
};
let mut folders = vec![String::from(dist_path_str)];
let mut url_request;
let mut files: Vec<String> = vec![];
let mut folders: Vec<ObjProps> = vec![ObjProps::new()];
let mut files: Vec<ObjProps> = vec![];
let mut first_iter = true;
while folders.len() > 0 {
let folder = folders.pop().unwrap();
url_request = String::from(domain.clone());
if first_iter {
url_request.push_str("/remote.php/dav/files/");
url_request.push_str(username);
}
url_request.push_str(folder.as_str());
let relative_s = match folder.relative_s {
Some(relative_s) => relative_s,
None => String::from(""),
};
// request folder content
let mut objs = vec![];
tokio::runtime::Runtime::new().unwrap().block_on(async {
let res = ListFolders::new(url_request.as_str())
let res = ReqProps::new()
.set_request(relative_s.as_str(), &api_props)
.gethref()
.send_with_res()
.await;
objs = match res {
.getlastmodified()
.send_req_multiple();
let mut objs = match res {
Ok(o) => o,
Err(ApiError::IncorrectRequest(err)) => {
eprintln!("fatal: {}", err.status());
@ -69,8 +72,7 @@ pub fn clone(remote: Values<'_>) {
std::process::exit(1);
},
Err(ApiError::Unexpected(_)) => todo!()
}
});
};
// create folder
if first_iter {
@ -79,19 +81,20 @@ pub fn clone(remote: Values<'_>) {
// destination path 'path' already exists and is not an empty directory.
//std::process::exit(1);
} else {
commands::init::init();
init::init();
}
} else {
// create folder
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()) {
eprintln!("error: cannot create directory {}: {}", local_folder.display(), err);
let p = ref_path.clone().join(Path::new(&relative_s));
if let Err(err) = DirBuilder::new().recursive(true).create(p.clone()) {
eprintln!("err: cannot create directory {} ({})", p.display(), err);
}
// add tree
let path_folder = local_folder.strip_prefix(ref_path.clone()).unwrap();
if object::add_tree(&path_folder).is_err() {
eprintln!("error: cannot store object {}", path_folder.display());
let path_folder = p.strip_prefix(ref_path.clone()).unwrap();
let lastmodified = folder.lastmodified.unwrap().timestamp_millis();
if let Err(err) = add_tree(&path_folder, &lastmodified.to_string()) {
eprintln!("err: saving ref of {} ({})", path_folder.display(), err);
}
}
@ -100,36 +103,34 @@ pub fn clone(remote: Values<'_>) {
iter.next(); // jump first element which is the folder cloned
for object in iter {
if object.href.clone().unwrap().chars().last().unwrap() == '/' {
folders.push(object.href.clone().unwrap().to_string());
folders.push(object.clone());
} else {
files.push(object.href.clone().unwrap().to_string());
files.push(object.clone());
}
}
first_iter = false;
}
download_files(ref_path.clone(), files);
download_files(ref_path.clone(), files, &api_props);
}
fn download_files(local_p: PathBuf, files: Vec<String>) {
for remote_file in files {
tokio::runtime::Runtime::new().unwrap().block_on(async {
fn download_files(ref_p: PathBuf, files: Vec<ObjProps>, api_props: &ApiProps) {
for obj in files {
let relative_s = &obj.clone().relative_s.unwrap();
let res = DownloadFiles::new()
.set_url_with_remote(remote_file.as_str())
.save(local_p.clone()).await;
.set_url(&relative_s, api_props)
.save(ref_p.clone());
match res {
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());
let relative_p = Path::new(&relative_s);
let lastmodified = obj.clone().lastmodified.unwrap().timestamp_millis();
if let Err(err) = add_blob(relative_p, &lastmodified.to_string()) {
eprintln!("err: saving ref of {} ({})", relative_s.clone(), err);
}
},
Err(ApiError::Unexpected(_)) => {
eprintln!("error writing {}", remote_file);
eprintln!("err: writing {}", relative_s);
},
Err(ApiError::IncorrectRequest(err)) => {
eprintln!("fatal: {}", err.status());
@ -141,7 +142,6 @@ fn download_files(local_p: PathBuf, files: Vec<String>) {
std::process::exit(1);
}
}
});
}
}

View File

@ -1,6 +1,6 @@
use crate::utils::{path, read};
use std::fs::OpenOptions;
use std::io::{self, Write};
use crate::utils::{path, read};
pub fn set(var: &str, val: &str) -> io::Result<()> {
let mut root = match path::nextsync() {
@ -40,7 +40,6 @@ pub fn get(var: &str) -> Option<String> {
if let Ok(lines) = read::read_lines(root) {
for line in lines {
if let Ok(l) = line {
dbg!(l.clone());
if l.starts_with(var.clone()) {
let (_, val) = l.split_once(" ").unwrap();
return Some(val.to_owned());

View File

@ -1,6 +1,6 @@
use std::env;
use std::fs::{DirBuilder, File};
use std::path::PathBuf;
use std::env;
use crate::global::global::DIR_PATH;
pub fn init() {

View File

@ -1,11 +1,16 @@
use crate::commands::{status, config};
use crate::services::req_props::ReqProps;
use crate::services::api::ApiError;
use crate::services::upload_file::UploadFile;
use crate::services::delete_path::DeletePath;
use crate::commands::status::{State, Obj};
use crate::store::object::{add_blob, rm_blob};
use crate::services::req_props::{ReqProps, ObjProps};
use crate::store::index;
use crate::store::object::{add_blob, rm_blob};
use crate::commands::{status, config};
use crate::commands::status::{State, Obj};
use crate::commands::push::push_factory::{PushFactory, PushState};
pub mod push_factory;
pub mod new;
pub mod deleted;
pub fn push() {
dbg!(status::get_all_staged());
@ -36,205 +41,3 @@ pub fn push() {
// if dir upload dir
}
#[derive(Debug)]
enum PushState {
Done,
Valid,
Conflict,
Error,
}
trait PushChange {
fn can_push(&self) -> PushState;
fn push(&self);
}
struct New {
obj: Obj,
}
impl PushChange for New {
fn can_push(&self) -> PushState {
// check if exist on server
let file_infos = tokio::runtime::Runtime::new().unwrap().block_on(async {
let res = ReqProps::new()
.set_url(&self.obj.path.to_str().unwrap())
.getlastmodified()
.send_with_err()
.await;
match res {
Ok(data) => Ok(data),
Err(ApiError::IncorrectRequest(err)) => {
if err.status() == 404 {
Ok(vec![])
} else {
Err(())
}
},
Err(_) => Err(()),
}
});
if let Ok(infos) = file_infos {
if infos.len() == 0 {
// file doesn't exist on remote
PushState::Valid
} else {
// todo check date
PushState::Conflict
}
} else {
PushState::Error
}
}
fn push(&self) {
let obj = &self.obj;
tokio::runtime::Runtime::new().unwrap().block_on(async {
let res = UploadFile::new()
.set_url(obj.path.to_str().unwrap())
.set_file(obj.path.clone())
.send_with_err()
.await;
match res {
Err(ApiError::IncorrectRequest(err)) => {
eprintln!("fatal: error pushing file {}: {}", obj.name, err.status());
std::process::exit(1);
},
Err(ApiError::RequestError(_)) => {
eprintln!("fatal: request error pushing file {}", obj.name);
std::process::exit(1);
}
_ => (),
}
});
// update tree
add_blob(&obj.path.clone(), "todo_date");
// remove index
index::rm_line(obj.path.to_str().unwrap());
}
}
struct Deleted {
obj: Obj,
}
impl PushChange for Deleted {
fn can_push(&self) -> PushState {
// check if exist on server
let file_infos = tokio::runtime::Runtime::new().unwrap().block_on(async {
let res = ReqProps::new()
.set_url(&self.obj.path.to_str().unwrap())
.getlastmodified()
.send_with_err()
.await;
match res {
Ok(data) => Ok(data),
Err(ApiError::IncorrectRequest(err)) => {
if err.status() == 404 {
Ok(vec![])
} else {
Err(())
}
},
Err(_) => Err(()),
}
});
if let Ok(infos) = file_infos {
if infos.len() == 0 {
// file doesn't exist on remote
PushState::Done
} else {
// todo check date
//PushState::Conflict
PushState::Valid
}
} else {
PushState::Error
}
}
fn push(&self) {
let obj = &self.obj;
tokio::runtime::Runtime::new().unwrap().block_on(async {
let res = DeletePath::new()
.set_url(obj.path.to_str().unwrap())
.send_with_err()
.await;
match res {
Err(ApiError::IncorrectRequest(err)) => {
eprintln!("fatal: error deleting file {}: {}", obj.name, err.status());
std::process::exit(1);
},
Err(ApiError::RequestError(_)) => {
eprintln!("fatal: request error deleting file {}", obj.name);
std::process::exit(1);
}
_ => (),
}
});
// update tree
rm_blob(&obj.path.clone());
// remove index
index::rm_line(obj.path.to_str().unwrap());
}
}
struct PushFactory;
impl PushFactory {
fn new(&self, obj: Obj) -> Box<dyn PushChange> {
match obj.state {
State::New => Box::new(New { obj: obj.clone() }),
State::Renamed => todo!(),
State::Modified => todo!(),
State::Deleted => Box::new(Deleted { obj: obj.clone() }),
State::Default => todo!(),
}
}
}
fn can_push_file(obj: Obj) -> PushState {
dbg!(obj.clone());
// check if exist on server
let file_infos = tokio::runtime::Runtime::new().unwrap().block_on(async {
let res = ReqProps::new()
.set_url(obj.path.to_str().unwrap())
.getlastmodified()
.send_with_err()
.await;
match res {
Ok(data) => Ok(data),
Err(ApiError::IncorrectRequest(err)) => {
if err.status() == 404 {
Ok(vec![])
} else {
Err(())
}
},
Err(_) => Err(()),
}
});
if let Ok(infos) = file_infos {
if infos.len() == 0 {
// file doesn't exist on remote
PushState::Valid
} else {
// check date
PushState::Conflict
}
} else {
PushState::Error
}
}

View File

@ -0,0 +1,70 @@
use crate::services::api::ApiError;
use crate::services::req_props::ReqProps;
use crate::services::delete_path::DeletePath;
use crate::store::index;
use crate::store::object::rm_blob;
use crate::commands::status::Obj;
use crate::commands::push::push_factory::{PushState, PushChange};
pub struct Deleted {
pub obj: Obj,
}
impl PushChange for Deleted {
fn can_push(&self) -> PushState {
// check if exist on server
let res = ReqProps::new()
.set_url(&self.obj.path.to_str().unwrap())
.getlastmodified()
.send_with_err();
let file_infos = match res {
Ok(obj) => Ok(Some(obj)),
Err(ApiError::IncorrectRequest(err)) => {
if err.status() == 404 {
Ok(None)
} else {
Err(())
}
},
Err(_) => Err(()),
};
if let Ok(infos) = file_infos {
if let Some(inf) = infos {
// file doesn't exist on remote
PushState::Done
} else {
// todo check date
//PushState::Conflict
PushState::Valid
}
} else {
PushState::Error
}
}
fn push(&self) {
let obj = &self.obj;
let res = DeletePath::new()
.set_url(obj.path.to_str().unwrap())
.send_with_err();
match res {
Err(ApiError::IncorrectRequest(err)) => {
eprintln!("fatal: error deleting file {}: {}", obj.name, err.status());
std::process::exit(1);
},
Err(ApiError::RequestError(_)) => {
eprintln!("fatal: request error deleting file {}", obj.name);
std::process::exit(1);
}
_ => (),
}
// update tree
rm_blob(&obj.path.clone());
// remove index
index::rm_line(obj.path.to_str().unwrap());
}
}

71
src/commands/push/new.rs Normal file
View File

@ -0,0 +1,71 @@
use crate::services::api::ApiError;
use crate::services::req_props::ReqProps;
use crate::services::upload_file::UploadFile;
use crate::store::index;
use crate::store::object::add_blob;
use crate::commands::status::Obj;
use crate::commands::push::push_factory::{PushState, PushChange, PushFactory};
pub struct New {
pub obj: Obj,
}
impl PushChange for New {
fn can_push(&self) -> PushState {
// check if exist on server
let res = ReqProps::new()
.set_url(&self.obj.path.to_str().unwrap())
.getlastmodified()
.send_req_single();
let file_infos = match res {
Ok(obj) => Ok(Some(obj)),
Err(ApiError::IncorrectRequest(err)) => {
if err.status() == 404 {
Ok(None)
} else {
Err(())
}
},
Err(_) => Err(()),
};
if let Ok(infos) = file_infos {
if let Some(info) = infos {
// file doesn't exist on remote
PushState::Valid
} else {
// todo check date
PushState::Conflict
}
} else {
PushState::Error
}
}
fn push(&self) {
let obj = &self.obj;
let res = UploadFile::new()
.set_url(obj.path.to_str().unwrap())
.set_file(obj.path.clone())
.send_with_err();
match res {
Err(ApiError::IncorrectRequest(err)) => {
eprintln!("fatal: error pushing file {}: {}", obj.name, err.status());
std::process::exit(1);
},
Err(ApiError::RequestError(_)) => {
eprintln!("fatal: request error pushing file {}", obj.name);
std::process::exit(1);
}
_ => (),
}
// update tree
add_blob(&obj.path.clone(), "todo_date");
// remove index
index::rm_line(obj.path.to_str().unwrap());
}
}

View File

@ -0,0 +1,32 @@
use crate::commands::status::{State, Obj};
use crate::commands::push::new::New;
use crate::commands::push::deleted::Deleted;
#[derive(Debug)]
pub enum PushState {
Done,
Valid,
Conflict,
Error,
}
pub trait PushChange {
fn can_push(&self) -> PushState;
fn push(&self);
}
pub struct PushFactory;
impl PushFactory {
pub fn new(&self, obj: Obj) -> Box<dyn PushChange> {
match obj.state {
State::New => Box::new(New { obj: obj.clone() }),
State::Renamed => todo!(),
State::Modified => todo!(),
State::Deleted => Box::new(Deleted { obj: obj.clone() }),
State::Default => todo!(),
}
}
}

View File

@ -1,10 +1,10 @@
use std::fs::File;
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use std::collections::{HashSet, HashMap};
use colored::Colorize;
use std::path::PathBuf;
use std::io::{self, Lines, BufReader};
use std::collections::{HashSet, HashMap};
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use colored::Colorize;
use crate::utils;
use crate::store::{self, object};
@ -58,10 +58,9 @@ pub fn get_all_staged() -> Vec<Obj> {
staged_objs
}
fn get_renamed(new_obj: &mut Vec<Obj>, del_obj: &mut Vec<Obj>) -> Vec<Obj> {
fn get_renamed(_new_obj: &mut Vec<Obj>, _del_obj: &mut Vec<Obj>) -> Vec<Obj> {
// get hash of all new obj, compare to hash of all del
let renamed_objs = vec![];
renamed_objs
}

View File

@ -1,5 +1,6 @@
use clap::{App, Arg, SubCommand};
use crate::commands::add::AddArgs;
mod commands;
mod utils;
mod services;

View File

@ -1,5 +1,4 @@
pub mod api;
pub mod list_folders;
pub mod download_files;
pub mod req_props;
pub mod upload_file;

View File

@ -1,8 +1,9 @@
use std::env;
use dotenv::dotenv;
use reqwest::Client;
use reqwest::RequestBuilder;
use reqwest::{Response, Error, IntoUrl, Method};
use std::env;
use dotenv::dotenv;
use crate::utils::api::ApiProps;
#[derive(Debug)]
pub enum ApiError {
@ -25,7 +26,7 @@ impl ApiBuilder {
}
}
pub fn set_request<U: IntoUrl>(mut self, method: Method, url: U) -> ApiBuilder {
pub fn set_request<U: IntoUrl>(&mut self, method: Method, url: U) -> &mut ApiBuilder {
self.request = Some(self.client.request(method, url));
self
}
@ -48,13 +49,14 @@ impl ApiBuilder {
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);
pub fn set_req(&mut self, meth: Method, p: &str, api_props: &ApiProps) -> &mut ApiBuilder {
let mut url = String::from(&api_props.host);
url.push_str("/remote.php/dav/files/");
url.push_str("/");
url.push_str(path);
dbg!(url.clone());
url.push_str(&api_props.username);
url.push_str(&api_props.root);
url.push_str("/");
url.push_str(p);
self.request = Some(self.client.request(meth, url));
self
}

View File

@ -1,5 +1,5 @@
use crate::services::api::{ApiBuilder, ApiError};
use reqwest::{Method, IntoUrl, Response, Error};
use crate::services::api::{ApiBuilder, ApiError};
pub struct CreateFolder {
api_builder: ApiBuilder,

View File

@ -1,5 +1,5 @@
use crate::services::api::{ApiBuilder, ApiError};
use reqwest::{Method, Response, Error};
use crate::services::api::{ApiBuilder, ApiError};
pub struct DeletePath {
api_builder: ApiBuilder,
@ -21,10 +21,15 @@ impl DeletePath {
self.api_builder.send().await
}
pub async fn send_with_err(&mut self) -> Result<String, ApiError> {
let res = self.send().await.map_err(ApiError::RequestError)?;
pub fn send_with_err(&mut self) -> Result<String, ApiError> {
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
self.send().await
}).map_err(ApiError::RequestError)?;
if res.status().is_success() {
let body = res.text().await.map_err(ApiError::EmptyError)?;
let body = tokio::runtime::Runtime::new().unwrap().block_on(async {
res.text().await
}).map_err(ApiError::EmptyError)?;
Ok(body)
} else {
Err(ApiError::IncorrectRequest(res))

View File

@ -1,26 +1,26 @@
use crate::services::api::{ApiBuilder, ApiError};
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};
use reqwest::{Method, Response, Error};
use crate::utils::api::ApiProps;
use crate::services::api::{ApiBuilder, ApiError};
pub struct DownloadFiles {
api_builder: ApiBuilder,
path: String,
relative_ps: String,
}
impl DownloadFiles {
pub fn new() -> Self {
DownloadFiles {
api_builder: ApiBuilder::new(),
path: String::from(""),
relative_ps: 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);
pub fn set_url(&mut self, relative_ps: &str, api_props: &ApiProps) -> &mut DownloadFiles {
self.relative_ps = relative_ps.to_string();
self.api_builder.set_req(Method::from_bytes(b"PROPFIND").unwrap(), relative_ps, api_props);
self
}
@ -38,8 +38,9 @@ impl DownloadFiles {
}
}
pub async fn save(&mut self, local_path: PathBuf) -> Result<(), ApiError> {
let p = local_path.join(PathBuf::from(self.path.clone()));
pub fn save(&mut self, ref_p: PathBuf) -> Result<(), ApiError> {
tokio::runtime::Runtime::new().unwrap().block_on(async {
let p = ref_p.join(PathBuf::from(self.relative_ps.clone()));
let res = self.send().await.map_err(ApiError::RequestError)?;
if res.status().is_success() {
let body = res.bytes().await.map_err(ApiError::EmptyError)?;
@ -50,6 +51,7 @@ impl DownloadFiles {
} else {
Err(ApiError::IncorrectRequest(res))
}
})
}
fn write_file(path: PathBuf, content: &Vec<u8>) -> io::Result<()> {

View File

@ -1,114 +0,0 @@
use crate::services::api::{ApiBuilder, ApiError};
use xml::reader::{EventReader, XmlEvent};
use std::io::Cursor;
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 {
api_builder: ApiBuilder,
xml_balises: Vec<String>,
}
impl ListFolders {
pub fn new<U: IntoUrl>(url: U) -> Self {
ListFolders {
api_builder: ApiBuilder::new()
.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> {
self.api_builder.send().await
}
pub async fn send_with_err(&mut self) -> Result<String, ApiError> {
let res = self.send().await.map_err(ApiError::RequestError)?;
if res.status().is_success() {
let body = res.text().await.map_err(ApiError::EmptyError)?;
Ok(body)
} else {
Err(ApiError::IncorrectRequest(res))
}
}
pub async fn send_with_res(&mut self) -> Result<Vec<FolderContent>, ApiError> {
match self.send_with_err().await {
Ok(body) => Ok(self.parse(body)),
Err(err) => Err(err),
}
}
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
}
}

View File

@ -1,20 +1,52 @@
use crate::services::api::{ApiBuilder, ApiError};
use xml::reader::{EventReader, XmlEvent};
use std::io::Cursor;
use chrono::{Utc, DateTime};
use reqwest::{Method, Response, Error};
use xml::reader::{EventReader, XmlEvent};
use crate::utils::time::parse_timestamp;
use crate::utils::api::{get_relative_s, ApiProps};
use crate::services::api::{ApiBuilder, ApiError};
#[derive(Debug)]
pub struct ObjProps {
pub href: Option<String>,
pub relative_s: Option<String>,
pub lastmodified: Option<DateTime<Utc>>,
}
impl Clone for ObjProps {
fn clone(&self) -> Self {
ObjProps {
href: self.href.clone(),
relative_s: self.relative_s.clone(),
lastmodified: self.lastmodified.clone(),
}
}
}
impl ObjProps {
pub fn new() -> Self {
ObjProps {
href: None,
relative_s: None,
lastmodified: None,
}
}
}
pub struct ReqProps {
api_builder: ApiBuilder,
xml_list: Vec<String>,
xml_balises: Vec<String>,
xml_payload: String,
api_props: Option<ApiProps>
}
impl ReqProps {
pub fn new() -> Self {
ReqProps {
api_builder: ApiBuilder::new(),
xml_list: vec![],
xml_balises: vec![],
xml_payload: String::new(),
api_props: None,
}
}
@ -23,38 +55,49 @@ impl ReqProps {
self
}
pub fn set_request(&mut self, p: &str, api_props: &ApiProps) -> &mut ReqProps {
self.api_props = Some(api_props.clone());
self.api_builder.set_req(Method::from_bytes(b"PROPFIND").unwrap(), p, api_props);
self
}
pub fn gethref(&mut self) -> &mut ReqProps {
self.xml_balises.push(String::from("href"));
self
}
pub fn getlastmodified(&mut self) -> &mut ReqProps {
self.xml_list.push(String::from("getlastmodified"));
self.xml_balises.push(String::from("getlastmodified"));
self.xml_payload.push_str(r#"<d:getlastmodified/>"#);
self
}
pub fn getcontentlenght(&mut self) -> &mut ReqProps {
self.xml_list.push(String::from("getcontentlength"));
self.xml_balises.push(String::from("getcontentlength"));
self.xml_payload.push_str(r#"<d:getcontentlength/>"#);
self
}
pub fn _getcontenttype(&mut self) -> &mut ReqProps {
self.xml_list.push(String::from("getcontenttype"));
self.xml_balises.push(String::from("getcontenttype"));
self.xml_payload.push_str(r#"<d:getcontenttype/>"#);
self
}
pub fn _getpermissions(&mut self) -> &mut ReqProps {
self.xml_list.push(String::from("permissions"));
self.xml_balises.push(String::from("permissions"));
self.xml_payload.push_str(r#"<oc:permissions/>"#);
self
}
pub fn _getressourcetype(&mut self) -> &mut ReqProps {
self.xml_list.push(String::from("resourcetype"));
self.xml_balises.push(String::from("resourcetype"));
self.xml_payload.push_str(r#"<d:resourcetype/>"#);
self
}
pub fn _getetag(&mut self) -> &mut ReqProps {
self.xml_list.push(String::from("getetag"));
self.xml_balises.push(String::from("getetag"));
self.xml_payload.push_str(r#"<d:getetag/>"#);
self
}
@ -72,37 +115,83 @@ impl ReqProps {
self.api_builder.send().await
}
pub async fn send_with_err(&mut self) -> Result<Vec<String>, ApiError> {
let res = self.send().await.map_err(ApiError::RequestError)?;
pub fn send_with_err(&mut self) -> Result<String, ApiError> {
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
self.send().await
}).map_err(ApiError::RequestError)?;
if res.status().is_success() {
let body = res.text().await.map_err(ApiError::EmptyError)?;
Ok(self.parse(body))
let body = tokio::runtime::Runtime::new().unwrap().block_on(async {
res.text().await
}).map_err(ApiError::EmptyError)?;
Ok(body)
} else {
Err(ApiError::IncorrectRequest(res))
}
}
pub fn parse(&self, xml: String) -> Vec<String> {
pub fn send_req_multiple(&mut self) -> Result<Vec<ObjProps>, ApiError> {
match self.send_with_err() {
Ok(body) => Ok(self.parse(body, true)),
Err(err) => Err(err),
}
}
pub fn send_req_single(&mut self) -> Result<ObjProps, ApiError> {
match self.send_with_err() {
Ok(body) => {
let objs = self.parse(body, false);
let obj = objs[0].clone();
Ok(obj)
},
Err(err) => Err(err),
}
}
fn parse(&self, xml: String, multiple: bool) -> Vec<ObjProps> {
let cursor = Cursor::new(xml);
let parser = EventReader::new(cursor);
let mut should_get = false;
let mut values: Vec<String> = vec![];
let mut iter = self.xml_list.iter();
let mut values: Vec<ObjProps> = vec![];
let mut iter = self.xml_balises.iter();
let mut val = iter.next();
let mut content = ObjProps::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 if want multiple
if multiple {
values.push(content.clone());
iter = self.xml_balises.iter();
val = iter.next();
content = ObjProps::new();
if let Some(v) = val.clone() {
should_get = &name.local_name == v;
}
} else {
break;
}
}
}
Ok(XmlEvent::Characters(text)) => {
if !text.trim().is_empty() && should_get {
values.push(text);
match val.unwrap().as_str() {
"href" => {
content.href = Some(text.clone());
content.relative_s = Some(get_relative_s(text, &(self.api_props.clone().unwrap())));
},
"getlastmodified" => {
content.lastmodified = Some(parse_timestamp(&text).unwrap());
},
_ => (),
}
val = iter.next()
}
}

View File

@ -1,8 +1,8 @@
use std::fs::File;
use crate::services::api::{ApiBuilder, ApiError};
use std::path::PathBuf;
use std::io::{Read};
use std::path::PathBuf;
use reqwest::{Method, Response, Error};
use crate::services::api::{ApiBuilder, ApiError};
pub struct UploadFile {
api_builder: ApiBuilder,
@ -34,10 +34,15 @@ impl UploadFile {
self.api_builder.send().await
}
pub async fn send_with_err(&mut self) -> Result<String, ApiError> {
let res = self.send().await.map_err(ApiError::RequestError)?;
pub fn send_with_err(&mut self) -> Result<String, ApiError> {
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
self.send().await
}).map_err(ApiError::RequestError)?;
if res.status().is_success() {
let body = res.text().await.map_err(ApiError::EmptyError)?;
let body = tokio::runtime::Runtime::new().unwrap().block_on(async {
res.text().await
}).map_err(ApiError::EmptyError)?;
Ok(body)
} else {
Err(ApiError::IncorrectRequest(res))

View File

@ -1,7 +1,7 @@
use std::fs::{File, OpenOptions};
use std::path::PathBuf;
use crate::utils::{read, path};
use std::fs::{File, OpenOptions};
use std::io::{self, Write};
use crate::utils::{read, path};
pub fn _read_only(mut path: PathBuf) -> File {
path.push("HEAD");

View File

@ -1,8 +1,8 @@
use std::fs::OpenOptions;
use std::io;
use std::fs::File;
use std::fs::OpenOptions;
use std::path::PathBuf;
use crate::utils::{read, path};
use std::io;
pub fn _read_only(mut path: PathBuf) -> File {
path.push("index");

View File

@ -1,11 +1,11 @@
use std::fs::File;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use crate::utils::{read, path};
use crate::store::head;
use std::fs::{OpenOptions, self};
use crypto::sha1::Sha1;
use crypto::digest::Digest;
use std::fs::{OpenOptions, self};
use std::io::{self, Write};
use std::fs::File;
use crate::utils::{read, path};
use crate::store::head;
/// Returns (line, hash, name)
///
@ -40,7 +40,7 @@ pub fn parse_line(line: String) -> (String, String, String) {
(String::from(ftype), String::from(hash), String::from(name))
}
pub fn add_tree(path: &Path) -> io::Result<()> {
pub fn add_tree(path: &Path, date: &str) -> io::Result<()> {
let (line, hash, name) = parse_path(path.clone(), false);
// add tree reference to parent
@ -51,7 +51,10 @@ pub fn add_tree(path: &Path) -> io::Result<()> {
}
// create tree object
create_object(hash, &name)?;
let mut content = name;
content.push_str(" ");
content.push_str(date);
create_object(hash, &content)?;
Ok(())
}
@ -93,8 +96,6 @@ pub fn add_blob(path: &Path, date: &str) -> io::Result<()> {
let mut content = name.clone().to_owned();
content.push_str(" ");
content.push_str("tmp_hash");
content.push_str(" ");
content.push_str(date);
// create blob object
@ -133,7 +134,6 @@ pub fn read_tree(tree: String) -> Option<(String, io::Lines<io::BufReader<File>>
obj_p.push(dir);
obj_p.push(res);
match read::read_lines(obj_p) {
Ok(mut reader) => {
let name = match reader.next() {
@ -147,7 +147,6 @@ pub fn read_tree(tree: String) -> Option<(String, io::Lines<io::BufReader<File>>
None
},
}
}
fn rm_node(path: &Path, node: &str) -> io::Result<()> {

View File

@ -2,3 +2,4 @@ pub mod path;
pub mod read;
pub mod nextsyncignore;
pub mod api;
pub mod time;

View File

@ -1,23 +1,25 @@
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())
#[derive(Debug)]
pub struct ApiProps {
pub host: String, // nextcloud.example.com
pub username: String,
pub root: String, // /dir/cloned
}
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()
impl Clone for ApiProps {
fn clone(&self) -> Self {
ApiProps {
host: self.host.to_string(),
username: self.username.to_string(),
root: self.root.to_string(),
}
}
}
pub fn get_relative_s(p: String, api_props: &ApiProps) -> String {
let mut final_p = p.clone();
final_p = final_p.strip_prefix("/remote.php/dav/files/").unwrap().to_string();
final_p = final_p.strip_prefix(&api_props.username).unwrap().to_string();
final_p = final_p.strip_prefix(&api_props.root).unwrap().to_string();
final_p = final_p.strip_prefix("/").unwrap().to_string();
final_p
}

View File

@ -1,7 +1,7 @@
use crate::utils::path;
use regex::Regex;
use std::fs::File;
use std::io::{BufReader, BufRead};
use regex::Regex;
use crate::utils::path;
pub fn read_lines() -> Result<Vec<String>, ()> {
if let Some(path) = path::nextsyncignore() {

View File

@ -1,7 +1,7 @@
use std::env;
use std::fs::canonicalize;
use std::path::{PathBuf, Path};
use crate::global::global::DIR_PATH;
use std::fs::canonicalize;
pub fn current() -> Option<PathBuf> {
let d = DIR_PATH.lock().unwrap();

View File

@ -1,6 +1,6 @@
use std::path::{Path, PathBuf};
use std::io::{self, BufRead, BufReader, Write};
use std::fs::{self, File, OpenOptions};
use std::io::{self, BufRead, BufReader, Write};
pub fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {

6
src/utils/time.rs Normal file
View File

@ -0,0 +1,6 @@
use chrono::{DateTime, TimeZone, Utc, ParseError};
pub fn parse_timestamp(timestamp: &str) -> Result<DateTime<Utc>, ParseError> {
let format = "%a, %d %b %Y %H:%M:%S %Z";
Utc.datetime_from_str(timestamp, format)
}