Compare commits

...

2 Commits

Author SHA1 Message Date
grimhilt
487a94f829 feat(enumerator): working enumerator with depth 2024-09-29 22:56:51 +02:00
grimhilt
6989e87a56 feat(enumerator): try to have an aux functions for the task 2024-09-29 22:36:53 +02:00
6 changed files with 108 additions and 43 deletions

View File

@ -20,7 +20,9 @@ pub async fn exec(args: CloneArgs, config: Config) {
.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

View File

@ -2,15 +2,16 @@ use super::{
req_props::{Props, ReqProps, Response},
service::Service,
};
use crate::utils::path;
use std::sync::Arc;
use tokio::sync::Mutex;
use tokio::{sync::Mutex, task::JoinSet};
pub const DEFAULT_DEPTH: usize = 3;
pub const DEFAULT_DEPTH: u16 = 3;
pub struct Enumerator<'a> {
service: &'a Service,
path: String,
depth: usize,
depth: u16,
properties: Vec<Props>,
}
@ -29,7 +30,7 @@ impl<'a> Enumerator<'a> {
self
}
pub fn set_depth(mut self, depth: usize) -> Self {
pub fn set_depth(mut self, depth: u16) -> Self {
self.depth = depth;
self
}
@ -44,44 +45,55 @@ impl<'a> Enumerator<'a> {
let files = Arc::new(Mutex::new(Vec::new()));
let folders = Arc::new(Mutex::new(Vec::new()));
let service = Arc::from(self.service.clone());
let tasks_active = Arc::new(Mutex::new(0));
tx.send(self.path.clone());
let mut handles = vec![];
let mut taskSet = JoinSet::new();
while let Some(path) = rx.recv().await {
let tx_clone = tx.clone();
let files_clone = Arc::clone(&files);
let folders_clone = Arc::clone(&folders);
let service_clone = Arc::clone(&service);
let properties = self.properties.clone();
loop {
if let Ok(path) = rx.try_recv() {
let current_depth = path::get_depth(&path);
*tasks_active.lock().await += 1;
let tx_clone = tx.clone();
let files_clone = Arc::clone(&files);
let folders_clone = Arc::clone(&folders);
let service_clone = Arc::clone(&service);
let properties = self.properties.clone();
let depth = self.depth.clone();
let tasks_active_clone = Arc::clone(&tasks_active);
let handle = tokio::task::spawn_blocking(move || async move {
let res = ReqProps::new(&service_clone)
.set_path(path)
// .set_depth(self.depth)
.get_properties(properties)
.send()
.await
.unwrap();
let task = taskSet.spawn(async move {
let res = ReqProps::new(&service_clone)
.set_path(path.clone())
.set_depth(depth)
.get_properties(properties)
.send()
.await
.unwrap();
for obj in res.responses {
if obj.is_dir() {
todo!("Deal to have good href");
tx_clone.send(obj.href.clone());
folders_clone.lock().await.push(obj);
} else {
files_clone.lock().await.push(obj);
for obj in res.responses {
if obj.is_dir() {
// Avoid enumerating the same folder multiple times
if obj.abs_path() != &path {
// depth deeper than current + self.depth
if obj.path_depth() >= current_depth + depth {
tx_clone.send(obj.abs_path().to_owned()).unwrap();
}
folders_clone.lock().await.push(obj);
}
} else {
files_clone.lock().await.push(obj);
}
}
}
});
handles.push(handle);
*tasks_active_clone.lock().await -= 1;
});
} else if *tasks_active.lock().await <= 0 {
break;
}
}
// Wait for all tasks to complete
for handle in handles {
handle.await;
}
taskSet.join_all().await;
Ok((
Arc::try_unwrap(files).unwrap().into_inner(),

View File

@ -1,5 +1,6 @@
use crate::services::service::{Request, Service};
use crate::store::object::Obj;
use crate::utils::path;
use serde::Deserialize;
use serde_xml_rs::from_str;
@ -49,7 +50,27 @@ pub struct Response {
impl Response {
pub fn is_dir(&self) -> bool {
todo!("is dir reponse")
self.href.ends_with("/")
}
pub fn abs_path(&self) -> &str {
let path = self
.href
.strip_prefix("/remote.php/dav/files")
.expect(&format!(
"Unexpected result when requesting props. Cannot strip from {}",
self.href
));
if path.ends_with('/') {
path.strip_suffix('/').expect("Checked before")
} else {
path
}
}
pub fn path_depth(&self) -> u16 {
path::get_depth(self.abs_path())
}
}
@ -87,6 +108,11 @@ impl<'a> ReqProps<'a> {
self
}
pub fn set_depth(mut self, depth: u16) -> Self {
self.request.headers.insert("Depth", depth.into());
self
}
pub fn get_properties(mut self, properties: Vec<Props>) -> Self {
self.properties.extend(properties);
self

View File

@ -1,6 +1,6 @@
use crate::commands::clone::UrlProps;
use reqwest::{header::HeaderMap, Method, Url};
use reqwest::{Client, ClientBuilder};
use reqwest::{Client, ClientBuilder, RequestBuilder};
const USER_AGENT: &str = "Nextsync";
@ -36,29 +36,37 @@ impl ClientConfig {
#[derive(Clone)]
pub struct Service {
/// http[s]://host.xz/remote.php/dav/files
url_base: String,
}
impl From<&UrlProps<'_>> for Service {
fn from(url_props: &UrlProps) -> Self {
todo!("Auth");
let mut url_base = if url_props.is_secure {
String::from("https://")
} else {
String::from("http://")
};
url_base.push_str(url_props.domain);
url_base.push_str("/remote.php/dav/files");
Service { url_base }
}
}
impl Service {
fn authenticate(&self, request: RequestBuilder) -> RequestBuilder {
request
.bearer_auth("rK5ud2NmrR8p586Th7v272HRgUcZcEKIEluOGjzQQRj7gWMMAISFTiJcFnnmnNiu2VVlENks")
}
}
pub struct Request<'a> {
service: &'a Service,
client: ClientConfig,
method: Option<Method>,
url: Option<String>,
headers: HeaderMap,
pub headers: HeaderMap,
body: Option<String>,
}
@ -81,7 +89,19 @@ impl<'a> Request<'a> {
}
pub async fn send(&mut self) -> Result<reqwest::Response, reqwest::Error> {
todo!()
self.service
.authenticate(
self.client
.build()
.request(self.method.clone().expect("Method must be set"), {
let mut url = self.service.url_base.clone();
url.push_str(&self.url.clone().expect("An url must be set"));
url
})
.headers(self.headers.clone()),
)
.send()
.await
// let mut url = self
// .config
// .clone()
@ -95,7 +115,6 @@ impl<'a> Request<'a> {
// self.client
// .build()
// .request(self.method.clone().expect("Method must be set"), url)
// .bearer_auth("rK5ud2NmrR8p586Th7v272HRgUcZcEKIEluOGjzQQRj7gWMMAISFTiJcFnnmnNiu2VVlENks")
// .send()
// .await
}

View File

@ -1,4 +1,4 @@
use clap::{Arg, ArgMatches, Command};
use clap::{Arg, ArgAction, ArgMatches, Command};
use crate::commands;
use crate::config::config::Config;
@ -22,8 +22,9 @@ pub fn create() -> Command {
)
.arg(
Arg::new("force_insecure")
.long("force-insecure")
.short('f')
.long("force-insecure")
.action(ArgAction::SetTrue)
.help("Force the connection to nextcloud to be in http (not https)")
)
.arg(
@ -48,7 +49,7 @@ pub async fn handler(args: &ArgMatches) {
commands::clone::CloneArgs {
remote: remote.to_string(),
depth: args.get_one::<String>("depth").cloned(),
force_insecure: args.contains_id("force_insecure"),
force_insecure: *args.get_one::<bool>("force_insecure").unwrap(),
},
Config::from(args.get_one::<String>("directory")),
)

View File

@ -54,3 +54,8 @@ pub fn normalize_path<P: AsRef<Path>>(path: P) -> PathBuf {
}
normalized
}
/// Calculate depth of a path
pub fn get_depth(path: &str) -> u16 {
path.split("/").count() as u16
}