cleaner clone

This commit is contained in:
grimhilt 2023-06-05 18:12:02 +02:00
parent 1042a7ea22
commit 5b7b75846c
4 changed files with 143 additions and 76 deletions

View File

@ -1,64 +1,50 @@
use clap::Values;
use std::io::Bytes;
use std::fs::OpenOptions;
use std::env; use std::env;
use std::fs::OpenOptions;
use std::fs::DirBuilder;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::Cursor; use std::io::Cursor;
use xml::reader::{EventReader, XmlEvent};
use std::fs::{File, DirBuilder};
use std::path::Path; use std::path::Path;
use clap::Values;
use regex::Regex;
use xml::reader::{EventReader, XmlEvent};
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 regex::Regex;
pub fn clone(remote: Values<'_>) { pub fn clone(remote: Values<'_>) {
let url = remote.clone().next().unwrap(); let url = remote.clone().next().unwrap();
let mut path = Path::new(url); let (domain, tmp_user, path_str) = get_url_props(url);
let path = Path::new(path_str);
let domain_regex = Regex::new(r"(https?:\/\/.+?)\/").unwrap(); let username = match tmp_user {
let domain = match domain_regex.captures_iter(url).last() { Some(u) => u,
Some(capture) => capture.get(1).expect("Domain not found").as_str(),
None => { None => {
eprintln!("fatal: no domain found"); eprintln!("No username found");
std::process::exit(1); // todo
}, ""
}
}; };
let url_without_domain = domain_regex.replace(url, "/").to_string();
let mut it = path.iter();
it.next();
it.next();
it.next();
it.next();
it.next();
let username = it.next().unwrap();
let mut folders = vec![url_without_domain]; let mut folders = vec![String::from(path_str)];
let mut url_request; let mut url_request;
let mut files: Vec<String> = vec![]; let mut files: Vec<String> = vec![];
let mut first_iter = true; let mut first_iter = true;
while folders.len() > 0 { while folders.len() > 0 {
let folder = folders.pop().unwrap(); let folder = folders.pop().unwrap();
url_request = String::from(domain.clone()); 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()); url_request.push_str(folder.as_str());
let mut body = Default::default(); let mut body = Default::default();
tokio::runtime::Runtime::new().unwrap().block_on(async { tokio::runtime::Runtime::new().unwrap().block_on(async {
match call(url_request.as_str()).await { body = ListFolders::new(url_request.as_str())
Ok(b) => body = b.clone(), .send_with_res()
Err(MyError::IncorrectRequest(err)) => { .await;
eprintln!("fatal: {}", err.status());
std::process::exit(1);
},
Err(MyError::EmptyError(_)) => eprintln!("Failed to get body"),
Err(MyError::RequestError(err)) => {
eprintln!("fatal: {}", err);
std::process::exit(1);
}
}
}); });
if first_iter { if first_iter {
first_iter = false; first_iter = false;
dbg!(path.file_name());
if DirBuilder::new().create(path.file_name().unwrap()).is_err() { if DirBuilder::new().create(path.file_name().unwrap()).is_err() {
// todo add second parameter to save in a folder // todo add second parameter to save in a folder
eprintln!("fatal: directory already exist"); eprintln!("fatal: directory already exist");
@ -83,56 +69,38 @@ pub fn clone(remote: Values<'_>) {
} }
} }
// tokio::runtime::Runtime::new().unwrap().block_on(async { download_files(&domain, username, files);
download_files(domain, username.to_str().unwrap(), files);
// });
} }
fn download_files(domain: &str, username: &str, files: Vec<String>) -> std::io::Result<()> { fn download_files(domain: &str, username: &str, files: Vec<String>) {
dbg!("in");
let mut body: Vec<u8> = vec![];
for file in files { for file in files {
let mut url_request = String::from(domain.clone()); let mut url_request = String::from(domain.clone());
url_request.push_str(file.as_str()); url_request.push_str(file.as_str());
tokio::runtime::Runtime::new().unwrap().block_on(async { tokio::runtime::Runtime::new().unwrap().block_on(async {
match callDownload(url_request.as_str()).await { match DownloadFiles::new(url_request.as_str()).send_with_err().await {
Ok(b) => { Ok(b) => {
let mut path = Path::new(&file).strip_prefix("/remote.php/dav/files/"); let mut path = Path::new(&file).strip_prefix("/remote.php/dav/files/");
path = path.unwrap().strip_prefix(username); path = path.unwrap().strip_prefix(username);
let pathCur = env::current_dir().unwrap(); let path_cur = env::current_dir().unwrap();
let mut f = OpenOptions::new() let mut f = OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
.open(pathCur.join(path.unwrap())).unwrap(); .open(path_cur.join(path.unwrap())).unwrap();
f.write_all(&b); f.write_all(&b);
}, },
Err(MyError::IncorrectRequest(err)) => { Err(ApiError::IncorrectRequest(err)) => {
eprintln!("fatal: {}", err.status()); eprintln!("fatal: {}", err.status());
std::process::exit(1); std::process::exit(1);
}, },
Err(MyError::EmptyError(_)) => eprintln!("Failed to get body"), Err(ApiError::EmptyError(_)) => eprintln!("Failed to get body"),
Err(MyError::RequestError(err)) => { Err(ApiError::RequestError(err)) => {
eprintln!("fatal: {}", err); eprintln!("fatal: {}", err);
std::process::exit(1); std::process::exit(1);
} }
} }
}); });
//f.write_all(body.clone())?;
dbg!(file);
}
Ok(())
}
async fn callDownload(url: &str) -> Result<Vec<u8>, MyError> {
let res = DownloadFiles::new(url).send().await.map_err(MyError::RequestError)?;
if res.status().is_success() {
let body = res.bytes().await.map_err(MyError::EmptyError)?;
Ok(body.to_vec())
} else {
Err(MyError::IncorrectRequest(res))
} }
} }
@ -166,19 +134,74 @@ fn get_objects_xml(xml: String) -> Vec<String> {
objects objects
} }
enum MyError { // todo allow http
IncorrectRequest(reqwest::Response), fn get_url_props(url: &str) -> (String, Option<&str>, &str) {
EmptyError(reqwest::Error), let mut username = None;
RequestError(reqwest::Error), let mut domain = "";
let mut path = "";
if url.find("@").is_some() {
let re = Regex::new(r"(.*)@(.+?)(/remote\.php/dav/files/.+?)?(/.*)").unwrap();
match re.captures_iter(url).last() {
Some(cap) => {
domain = cap.get(2).expect("").as_str();
username = Some(cap.get(1).expect("").as_str());
path = cap.get(4).expect("").as_str();
}
None => (),
}
} else if url.find("?").is_some() {
let re = Regex::new(r"((https?://)?.+?)/.+dir=(.+?)&").unwrap();
match re.captures_iter(url).last() {
Some(cap) => {
domain = cap.get(1).expect("").as_str();
path = cap.get(3).expect("").as_str();
}
None => (),
}
} else {
let re = Regex::new(r"((https?://)?.+?)(/remote\.php/dav/files/(.+?))?(/.*)").unwrap();
match re.captures_iter(url).last() {
Some(cap) => {
domain = cap.get(1).expect("").as_str();
username = match cap.get(4) {
Some(u) => Some(u.as_str()),
None => None,
};
path = cap.get(5).expect("").as_str();
}
None => (),
}
}
let re = Regex::new(r"(^https?://)?").unwrap();
let secure_domain = re.replace(domain, "https://").to_string();
(secure_domain, username, path)
} }
async fn call(url: &str) -> Result<String, MyError> { #[cfg(test)]
let res = ListFolders::new(url).send().await.map_err(MyError::RequestError)?; mod tests {
if res.status().is_success() { use super::*;
let body = res.text().await.map_err(MyError::EmptyError)?;
Ok(body) #[test]
} else { fn test_get_url_props() {
Err(MyError::IncorrectRequest(res)) let p = "/foo/bar";
let u = Some("user");
let d = String::from("https://nextcloud.com");
let ld = String::from("https://nextcloud.example.com");
assert_eq!(get_url_props("user@nextcloud.com/remote.php/dav/files/user/foo/bar"), (d.clone(), u, p));
assert_eq!(get_url_props("user@nextcloud.com/foo/bar"), (d.clone(), u, p));
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.example.com/foo/bar"), (ld.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("https://nextcloud.com/apps/files/?dir=/foo/bar&fileid=166666"), (d.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("https://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("https://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));
} }
} }

View File

@ -4,6 +4,12 @@ use reqwest::{Response, Error, IntoUrl, Method};
use std::env; use std::env;
use dotenv::dotenv; use dotenv::dotenv;
pub enum ApiError {
IncorrectRequest(reqwest::Response),
EmptyError(reqwest::Error),
RequestError(reqwest::Error),
}
pub struct ApiBuilder { pub struct ApiBuilder {
client: Client, client: Client,
request: Option<RequestBuilder>, request: Option<RequestBuilder>,

View File

@ -1,4 +1,4 @@
use crate::services::api::ApiBuilder; use crate::services::api::{ApiBuilder, ApiError};
use reqwest::{Method, IntoUrl, Response, Error}; use reqwest::{Method, IntoUrl, Response, Error};
pub struct DownloadFiles { pub struct DownloadFiles {
@ -16,4 +16,14 @@ impl DownloadFiles {
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> {
let res = self.send().await.map_err(ApiError::RequestError)?;
if res.status().is_success() {
let body = res.bytes().await.map_err(ApiError::EmptyError)?;
Ok(body.to_vec())
} else {
Err(ApiError::IncorrectRequest(res))
}
}
} }

View File

@ -1,4 +1,4 @@
use crate::services::api::ApiBuilder; use crate::services::api::{ApiBuilder, ApiError};
use reqwest::{Method, IntoUrl, Response, Error}; use reqwest::{Method, IntoUrl, Response, Error};
pub struct ListFolders { pub struct ListFolders {
@ -16,4 +16,32 @@ impl ListFolders {
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> {
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(self) -> String {
match self.send_with_err().await {
Ok(body) => body,
Err(ApiError::IncorrectRequest(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);
}
}
}
} }