nextsync-rust/src/commands/clone.rs
2024-02-25 17:34:16 +01:00

200 lines
7.5 KiB
Rust

use std::io;
use std::io::prelude::*;
use std::fs::DirBuilder;
use std::path::{Path, PathBuf};
use clap::Values;
use regex::Regex;
use crate::services::downloader::Downloader;
use crate::utils::api::ApiProps;
use crate::utils::path::path_buf_to_string;
use crate::utils::remote::{enumerate_remote, EnumerateOptions};
use crate::global::global::{DIR_PATH, set_dir_path};
use crate::services::api::ApiError;
use crate::services::api_call::ApiCall;
use crate::services::req_props::{ReqProps, ObjProps};
use crate::store::object::{tree::Tree, blob::Blob};
use crate::commands::config;
use crate::commands::init;
pub const DEPTH: &str = "3";
pub struct CloneArgs<'a> {
pub remote: Values<'a>,
pub depth: Option<String>,
}
pub fn clone(args: CloneArgs) {
let d = DIR_PATH.lock().unwrap().clone();
let url = args.remote.clone().next().unwrap();
let (host, tmp_user, dist_path_str) = get_url_props(url);
let username = match tmp_user {
Some(u) => u.to_string(),
None => {
println!("Please enter the username of the webdav instance: ");
let stdin = io::stdin();
stdin.lock().lines().next().unwrap().unwrap()
}
};
let api_props = ApiProps {
host: host.clone(),
username,
root: dist_path_str.to_string(),
};
let ref_path = match d.clone() {
Some(dir) => Path::new(&dir).to_owned(),
None => {
let iter = Path::new(dist_path_str).iter();
let dest_dir = iter.last().unwrap();
let lp = std::env::current_dir().unwrap().join(dest_dir);
set_dir_path(path_buf_to_string(lp.clone()));
lp
},
};
// try to create root folder
if DirBuilder::new().recursive(true).create(ref_path.clone()).is_err() {
eprintln!("fatal: unable to create the destination directory");
std::process::exit(1);
} else {
init::init();
// set remote origin in config file
let mut remote_url = api_props.username.clone();
remote_url.push_str("@");
remote_url.push_str(api_props.host.strip_prefix("https://").unwrap());
remote_url.push_str(&api_props.root);
if config::add_remote("origin", &remote_url).is_err()
{
eprintln!("err: not able to save remote");
}
}
let depth = &args.depth.clone().unwrap_or(DEPTH.to_string());
let (folders, files) = enumerate_remote(
|a| req(&api_props, depth, a),
None,
EnumerateOptions {
depth: Some(depth.to_owned()),
relative_s: None
});
for folder in folders {
// create folder
let p = ref_path.clone().join(Path::new(&folder.relative_s.unwrap()));
if let Err(err) = DirBuilder::new().recursive(true).create(p.clone()) {
eprintln!("err: cannot create directory {} ({})", p.display(), err);
}
// add tree
let path_folder = p.strip_prefix(ref_path.clone()).unwrap();
let lastmodified = folder.lastmodified.unwrap().timestamp_millis();
if let Err(err) = Tree::from_path(path_folder.to_path_buf()).create(&lastmodified.to_string(), false) {
eprintln!("err: saving ref of {} ({})", path_folder.display(), err);
}
}
Downloader::new()
.set_api_props(api_props.clone())
.set_files(files)
.should_log()
.download(ref_path.clone(), Some(&save_blob));
}
fn save_blob(obj: ObjProps) {
let relative_s = &obj.clone().relative_s.unwrap();
let relative_p = PathBuf::from(&relative_s);
let lastmodified = obj.clone().lastmodified.unwrap().timestamp_millis();
if let Err(err) = Blob::from_path(relative_p).create(&lastmodified.to_string(), false) {
eprintln!("err: saving ref of {} ({})", relative_s.clone(), err);
}
}
fn req(api_props: &ApiProps, depth: &str, relative_s: &str) -> Result<Vec<ObjProps>, ApiError> {
ReqProps::new()
.set_request(relative_s, &api_props)
.set_depth(depth)
.gethref()
.getcontentlength()
.getlastmodified()
.send_req_multiple()
}
pub fn get_url_props(url: &str) -> (String, Option<&str>, &str) {
let mut username = None;
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() { // from browser url
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"^http://").unwrap();
if !re.is_match(domain) {
let re = Regex::new(r"(^https?://)?").unwrap();
let secure_domain = re.replace(domain, "https://").to_string();
return (secure_domain, username, path);
}
(domain.to_string(), username, path)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_url_props() {
let p = "/foo/bar";
let u = Some("user");
let d = String::from("http://nextcloud.com");
let sd = String::from("https://nextcloud.com");
let sld = String::from("https://nextcloud.example.com");
let ld = String::from("http://nextcloud.example.com");
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.com/foo/bar"), (sd.clone(), u, 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("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("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("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"), (sld.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"), (sld.clone(), None, p));
}
}