diff --git a/src/commands/clone.rs b/src/commands/clone.rs index 8f0282c..a060a89 100644 --- a/src/commands/clone.rs +++ b/src/commands/clone.rs @@ -1,6 +1,14 @@ +use super::init::{self, InitArgs}; use crate::config::config::Config; -use crate::services::{service::Service, enumerator::Enumerator}; +use crate::services::{downloader::Downloader, enumerator::Enumerator, service::Service}; +use crate::store::{ + nsobject::NsObject, + structs::{self, to_obj_path}, +}; use regex::Regex; +use std::fs; +use std::io::{self, BufRead}; +use std::path::PathBuf; pub struct CloneArgs { pub remote: String, @@ -9,35 +17,57 @@ pub struct CloneArgs { } pub async fn exec(args: CloneArgs, config: Config) { + init::init(&config); + structs::init(config.get_root_unsafe()); + let mut url_props = get_url_props(&args.remote); if args.force_insecure { url_props.is_secure = false; } + if url_props.user == String::new() { + println!("Please enter the username of the webdav instance: "); + let stdin = io::stdin(); + url_props.user = stdin.lock().lines().next().unwrap().unwrap(); + } + let service = Service::from(&url_props); let Ok((files, folders)) = Enumerator::new(&service) .set_path(url_props.path.to_string()) // .set_depth(args.depth) .get_properties(vec![]) - .enumerate().await else { todo!() }; + .enumerate() + .await + else { + todo!() + }; dbg!(&files); dbg!(&folders); for folder in folders { - // create folder - // save folder + dbg!(folder.abs_path()); + // TODO + if folder.abs_path() == "/admin/tmp_test" { + continue; + } + dbg!(folder.obj_path()); + fs::create_dir(folder.obj_path()).unwrap(); + NsObject::from_local_path(&folder.obj_path()) + .save() + .unwrap(); } - // let downloader = Downloader::new(&service, config.get_root_unsafe()) - // .set_files(files.map(|file| todo!())) - // .download(); + let downloader = Downloader::new(&service) + .set_files(files.into_iter().map(|file| file.href).collect()) + .download(); } pub struct UrlProps<'a> { pub is_secure: bool, pub domain: &'a str, pub path: &'a str, + pub user: String, } impl UrlProps<'_> { @@ -46,6 +76,7 @@ impl UrlProps<'_> { is_secure: true, domain: "", path: "", + user: String::new(), } } } diff --git a/src/commands/init.rs b/src/commands/init.rs index 700541d..c680c80 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -18,6 +18,10 @@ pub struct InitArgs {} /// /// This function will panic if it cannot create the mentioned files or directories. pub fn exec(_: InitArgs, config: Config) { + init(&config); +} + +pub fn init(config: &Config) { let mut path: PathBuf = config.execution_path.clone(); path.push(".nextsync"); diff --git a/src/services.rs b/src/services.rs index cf5f687..bff88a9 100644 --- a/src/services.rs +++ b/src/services.rs @@ -1,4 +1,5 @@ pub mod downloader; +pub mod download; pub mod enumerator; pub mod req_props; pub mod service; diff --git a/src/services/download.rs b/src/services/download.rs new file mode 100644 index 0000000..0710f6b --- /dev/null +++ b/src/services/download.rs @@ -0,0 +1,27 @@ +use crate::services::service::{Request, Service}; +use reqwest::Error; +use reqwest::{header::HeaderMap, Method, Url}; +use reqwest::{Client, ClientBuilder, RequestBuilder}; + +pub struct Download<'a> { + request: Request<'a>, +} + +impl<'a> Download<'a> { + pub fn new(service: &'a Service) -> Self { + Download { + request: Request::new(service), + } + } + + pub fn set_obj_path(mut self, obj_path: String) -> Self { + self.request.get(obj_path); + self + } + + pub async fn send(&mut self) -> Result<(), reqwest::Error> { + let res = self.request.send().await; + let body = res.unwrap().text().await?; + Ok(()) + } +} diff --git a/src/services/downloader.rs b/src/services/downloader.rs index e69de29..586e40a 100644 --- a/src/services/downloader.rs +++ b/src/services/downloader.rs @@ -0,0 +1,26 @@ +use super::{download::Download, service::Service}; + +pub struct Downloader<'a> { + service: &'a Service, + files: Vec, +} + +impl<'a> Downloader<'a> { + pub fn new(service: &'a Service) -> Self { + Downloader { + service, + files: Vec::new(), + } + } + + pub fn set_files(mut self, files: Vec) -> Self { + self.files = files; + self + } + + pub fn download(&self) { + for file in self.files.clone() { + Download::new(self.service).set_obj_path(file).send(); + } + } +} diff --git a/src/services/req_props.rs b/src/services/req_props.rs index d788d23..9d89041 100644 --- a/src/services/req_props.rs +++ b/src/services/req_props.rs @@ -1,8 +1,12 @@ use crate::services::service::{Request, Service}; -use crate::store::object::Obj; +use crate::store::{ + object::Obj, + structs::{to_obj_path, ObjPath}, +}; use crate::utils::path; use serde::Deserialize; use serde_xml_rs::from_str; +use std::path::PathBuf; #[derive(Clone)] pub enum Props { @@ -69,6 +73,13 @@ impl Response { } } + pub fn obj_path(&self, ) -> ObjPath { + let mut path = self.abs_path(); + // TODO + path = path.strip_prefix("/admin/tmp_test/").unwrap(); + to_obj_path(&PathBuf::from(path)) + } + pub fn path_depth(&self) -> u16 { path::get_depth(self.abs_path()) } @@ -136,7 +147,8 @@ impl<'a> ReqProps<'a> { pub async fn send(&mut self) -> Result { let res = self.request.send().await; let xml = res.unwrap().text().await?; - let multistatus: Multistatus = from_str(&xml).unwrap(); + let multistatus: Multistatus = + from_str(&xml).expect("Failed to unwrap xml response from req_props"); Ok(multistatus) } } diff --git a/src/services/service.rs b/src/services/service.rs index 99a29ab..00c4f8e 100644 --- a/src/services/service.rs +++ b/src/services/service.rs @@ -38,6 +38,7 @@ impl ClientConfig { pub struct Service { /// http[s]://host.xz/remote.php/dav/files url_base: String, + user: String, } impl From<&UrlProps<'_>> for Service { @@ -50,7 +51,7 @@ impl From<&UrlProps<'_>> for Service { url_base.push_str(url_props.domain); url_base.push_str("/remote.php/dav/files"); - Service { url_base } + Service { url_base, user: url_props.user.clone() } } } @@ -88,6 +89,12 @@ impl<'a> Request<'a> { self } + pub fn get(&mut self, url: String) -> &mut Self { + self.method = Some(Method::GET); + self.url = Some(url); + self + } + pub async fn send(&mut self) -> Result { self.service .authenticate( diff --git a/src/store/nsobject.rs b/src/store/nsobject.rs index 3accdad..7203259 100644 --- a/src/store/nsobject.rs +++ b/src/store/nsobject.rs @@ -2,7 +2,10 @@ use crate::store::{ object::{Obj, ObjMetadata, ObjType}, structs::{NsObjPath, ObjPath}, }; +use std::fs::{self, File, OpenOptions}; +use std::io::{self, Write}; use std::sync::OnceLock; +use std::time::{SystemTime, UNIX_EPOCH}; type NsObjectChilds = Vec>; @@ -81,15 +84,99 @@ impl NsObject { /// * if it is a Tree obj after an empty line there will be the definition /// of its subobjs (one line by subobj) * /// obj_type + hash - pub fn save(&self) -> Result<(), ()> { + pub fn save(&self) -> io::Result<()> { if !self.get_obj_path().exists() { - // delete current obj - // delete reference on parent + self.delete_nsobj(); } else { + dbg!(self.get_nsobj_path()); + if self.get_nsobj_path().exists() { + self.edit_nsobj(); + } else { + self.create_nsobj()?; + } } Ok(()) } + fn delete_nsobj(&self) { + todo!("Delete nsobj"); + // delete current obj + // delete reference on parent + } + + fn create_nsobj(&self) -> io::Result<()> { + let nsobj_path = self.get_nsobj_path(); + let mut file = { + let mut nsobj_dir = nsobj_path.clone(); + nsobj_dir.pop(); + if !nsobj_dir.exists() { + std::fs::create_dir_all(nsobj_dir)?; + } + File::create(&nsobj_path)? + }; + + // Write type + file.write(&[self.obj_type.clone().into()])?; + + if self.obj_type == ObjType::Blob { + if let Some(metadata) = self.get_metadata() { + // Write size + file.write(&metadata.size.to_le_bytes())?; + + // Write modified + file.write( + &metadata + .modified + .expect(&format!( + "Expect 'modified' in metadata of {} to save obj", + self.get_obj_path().as_path().display() + )) + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + .to_le_bytes(), + )?; + } else { + todo!("Cannot load metadata") + } + } + + file.write_all(b"\n")?; + // Write Path + file.write_all(self.get_obj_path().to_str().unwrap().as_bytes())?; + file.write_all(b"\n")?; + + file.flush()?; + + // Save itself inside its parent + let mut parent_path = self.get_obj_path().clone(); + parent_path.pop(); + let parent_obj = NsObject::from_local_path(&parent_path); + parent_obj.add_child(&self)?; + Ok(()) + } + + fn edit_nsobj(&self) { + todo!("Edit nsobj"); + } + + + fn add_child(&self, child: &NsObject) -> io::Result<()> { + let mut file = OpenOptions::new() + .write(true) + .create(true) + .append(true) + .open(self.get_nsobj_path())?; + + let child_type = &[child.obj_type.clone().into()]; + let child_path = child.get_obj_path().to_str().unwrap().as_bytes(); + file.write(child_type)?; + file.write(child_path)?; + file.write(b"\n")?; + file.flush()?; + Ok(()) + } + pub fn get_metadata(&self) -> Option { todo!() } diff --git a/src/store/object.rs b/src/store/object.rs index cf0bf61..8172d5f 100644 --- a/src/store/object.rs +++ b/src/store/object.rs @@ -13,8 +13,8 @@ use std::time::{SystemTime, UNIX_EPOCH}; const MAX_SIZE_TO_USE_HASH: u64 = 12 * 1024 * 1024; pub struct ObjMetadata { - size: u64, - modified: Option, + pub size: u64, + pub modified: Option, } #[derive(PartialEq, Clone, Debug)] @@ -164,75 +164,7 @@ impl Obj { } pub fn save(&self) -> io::Result<()> { - let nsobj_path = self.get_nsobj_path(); - let mut file = { - if nsobj_path.exists() { - todo!("Edition of object") - } else { - let mut nsobj_dir = nsobj_path.clone(); - nsobj_dir.pop(); - if !nsobj_dir.exists() { - std::fs::create_dir_all(nsobj_dir)?; - } - File::create(&nsobj_path)? - } - }; - - // Write type - file.write(&[self.obj_type.clone().into()])?; - - if self.obj_type == ObjType::Blob { - if let Some(metadata) = self.get_metadata() { - // Write size - file.write(&metadata.size.to_le_bytes())?; - - // Write modified - file.write( - &metadata - .modified - .expect(&format!( - "Expect 'modified' in metadata of {} to save obj", - self.cpy_path() - )) - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs() - .to_le_bytes(), - )?; - } else { - todo!("Cannot load metadata") - } - } - - file.write_all(b"\n")?; - // Write Path - file.write_all(self.obj_path.to_str().unwrap().as_bytes())?; - file.write_all(b"\n")?; - - file.flush()?; - - // Save itself inside its parent - let mut parent_path = self.obj_path.clone(); - parent_path.pop(); - let parent_obj = Obj::from_local_path(&parent_path); - parent_obj.add_child(&self)?; - - Ok(()) - } - - fn add_child(&self, child: &Obj) -> io::Result<()> { - let mut file = OpenOptions::new() - .write(true) - .create(true) - .append(true) - .open(self.get_nsobj_path())?; - - let child_type = &[child.get_obj_type().clone().into()]; - let child_path = child.obj_path.to_str().unwrap().as_bytes(); - file.write(child_type)?; - file.write(child_path)?; - file.write(b"\n")?; - file.flush()?; + self.get_nsobj().save()?; Ok(()) } diff --git a/src/store/structs.rs b/src/store/structs.rs index eea400b..aaa910e 100644 --- a/src/store/structs.rs +++ b/src/store/structs.rs @@ -102,7 +102,7 @@ impl Into for PathBuf { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct NsObjPath { path: PathBuf, }