From a45c953397dea9f21ce27394ba49c846abb2fc8e Mon Sep 17 00:00:00 2001 From: grimhilt Date: Sun, 4 Jun 2023 20:51:36 +0200 Subject: [PATCH] enumerate all objects on server when cloning --- Cargo.lock | 34 ++++++++++ Cargo.toml | 2 + src/commands/clone.rs | 121 ++++++++++++++++++++++++++++++----- src/services/list_folders.rs | 2 +- 4 files changed, 141 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8bc798..aae0cb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -514,9 +523,11 @@ dependencies = [ "clap", "colored", "dotenv", + "regex", "reqwest", "rust-crypto", "tokio", + "xml-rs", ] [[package]] @@ -709,6 +720,23 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + [[package]] name = "reqwest" version = "0.11.18" @@ -1378,3 +1406,9 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] + +[[package]] +name = "xml-rs" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" diff --git a/Cargo.toml b/Cargo.toml index 09590b1..a852e37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,5 @@ dotenv ="0.15.0" clap = "2.33" rust-crypto = "0.2.36" colored = "2.0.0" +xml-rs = "0.8.0" +regex = "1.8.3" diff --git a/src/commands/clone.rs b/src/commands/clone.rs index dccaff4..1255d5b 100644 --- a/src/commands/clone.rs +++ b/src/commands/clone.rs @@ -1,28 +1,115 @@ use clap::Values; +use std::io::Cursor; +use xml::reader::{EventReader, XmlEvent}; use std::fs::{self, DirBuilder}; use std::path::Path; use crate::services::list_folders::ListFolders; -use std::error::Error; +use regex::Regex; pub fn clone(remote: Values<'_>) { let url = remote.clone().next().unwrap(); - let path = Path::new(url); - - tokio::runtime::Runtime::new().unwrap().block_on(async { - call(url).await - }); - //DirBuilder::new() - // .create(path.parent()); -} + let mut path = Path::new(url); -pub async fn call(url: &str) -> Result> { - let response = ListFolders::new(url).send().await?; - if response.status().is_success() { - let body = response.text().await?; - println!("Response body: {}", body); - } else { - println!("Request failed with status code: {}", response.status()); + let domain_regex = Regex::new(r"(https?:\/\/.+?)\/").unwrap(); + let domain = match domain_regex.captures_iter(url).last() { + Some(capture) => capture.get(1).expect("Domain not found").as_str(), + None => { + eprintln!("fatal: no domain found"); + std::process::exit(1); + }, + }; + let url_without_domain = domain_regex.replace(url, "/").to_string(); + + let mut folders = vec![url_without_domain]; + let mut url_request; + while folders.len() > 0 { + + url_request = String::from(domain.clone()); + url_request.push_str(folders.last().unwrap().as_str()); + let mut body = Default::default(); + tokio::runtime::Runtime::new().unwrap().block_on(async { + match call(url_request.as_str()).await { + Ok(b) => body = b.clone(), + Err(MyError::IncorrectRequest(err)) => { + 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); + } + } + }); + folders.pop(); + if (folders.len() == 0) { + if DirBuilder::new().create(path.parent()).is_err() { + // todo add second parameter to save in a folder + eprintln!("fatal: directory already exist"); + // destination path 'path' already exists and is not an empty directory. + std::process::exit(1); + } + + + } + + let objects = get_objects_xml(body); + let mut iter = objects.iter(); + iter.next(); // jump first element which the folder fetched + for object in iter { + dbg!(object); + if object.chars().last().unwrap() == '/' { + folders.push(object.to_string()); + dbg!("folder"); + } + } + } + +} + +fn get_objects_xml(xml: String) -> Vec { + let cursor = Cursor::new(xml); + let parser = EventReader::new(cursor); + + let mut should_get = false; + let mut objects: Vec = 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 +} + +enum MyError { + IncorrectRequest(reqwest::Response), + EmptyError(reqwest::Error), + RequestError(reqwest::Error), +} + +async fn call(url: &str) -> Result { + let res = ListFolders::new(url).send().await.map_err(MyError::RequestError)?; + if res.status().is_success() { + let body = res.text().await.map_err(MyError::EmptyError)?; + Ok(body) + } else { + Err(MyError::IncorrectRequest(res)) } - Ok(()) } diff --git a/src/services/list_folders.rs b/src/services/list_folders.rs index f627109..0d319b3 100644 --- a/src/services/list_folders.rs +++ b/src/services/list_folders.rs @@ -14,6 +14,6 @@ impl ListFolders { } pub async fn send(&mut self) -> Result { - Ok(self.api_builder.send().await?) + self.api_builder.send().await } }