From d323ae30705c1f6a10fe44f044ae0a563dcbe0ac Mon Sep 17 00:00:00 2001 From: grimhilt Date: Fri, 25 Aug 2023 16:09:28 +0200 Subject: [PATCH] push move file --- src/commands/push.rs | 3 +- src/commands/push/moved.rs | 86 +++++++++++++++++++++++++++++++ src/commands/push/new.rs | 2 +- src/commands/push/push_factory.rs | 9 +++- src/services/move.rs | 61 ++++++++++++++++++++++ src/store/object/blob.rs | 42 +++++++++------ 6 files changed, 184 insertions(+), 19 deletions(-) create mode 100644 src/commands/push/moved.rs create mode 100644 src/services/move.rs diff --git a/src/commands/push.rs b/src/commands/push.rs index 219ef7a..6a7c84d 100644 --- a/src/commands/push.rs +++ b/src/commands/push.rs @@ -11,14 +11,15 @@ pub mod new_dir; pub mod rm_dir; pub mod deleted; pub mod modified; +pub mod moved; pub fn push() { // todo err when pushing new folder - // todo let _remote = match config::get("remote") { Some(r) => r, None => { eprintln!("fatal: no remote set in configuration"); + // todo debug //std::process::exit(1); String::new() } diff --git a/src/commands/push/moved.rs b/src/commands/push/moved.rs new file mode 100644 index 0000000..6aacc9a --- /dev/null +++ b/src/commands/push/moved.rs @@ -0,0 +1,86 @@ +use std::path::PathBuf; +use std::io; +use crate::services::api::ApiError; +use crate::services::r#move::Move; +use crate::services::req_props::ReqProps; +use crate::commands::status::LocalObj; +use crate::commands::push::push_factory::{PushState, PushChange, PushFlowState}; +use crate::store::object::blob::Blob; +use crate::utils::path::path_buf_to_string; + +pub struct Moved { + pub obj: LocalObj, +} + +impl PushChange for Moved { + fn can_push(&self, whitelist: &mut Option) -> PushState { + match self.flow(&self.obj, whitelist.clone()) { + PushFlowState::Whitelisted => PushState::Done, + PushFlowState::NotOnRemote => PushState::Valid, + PushFlowState::RemoteIsNewer => PushState::Conflict, + PushFlowState::LocalIsNewer => PushState::Conflict, + PushFlowState::Error => PushState::Error, + } + } + + fn push(&self) -> io::Result<()> { + let obj = &self.obj; + let res = Move::new() + .set_url( + &path_buf_to_string(obj.path_from.clone().unwrap()), + obj.path.to_str().unwrap()) + .send_with_err(); + + match res { + Err(ApiError::IncorrectRequest(err)) => { + eprintln!("fatal: error moving file {}: {}", obj.name, err.status()); + std::process::exit(1); + }, + Err(ApiError::RequestError(_)) => { + eprintln!("fatal: request error moving file {}", obj.name); + std::process::exit(1); + } + _ => (), + } + + // get lastmodified props to update it + let props = ReqProps::new() + .set_url(obj.path.to_str().unwrap()) + .getlastmodified() + .send_req_single(); + + let prop = match props { + Ok(o) => o, + Err(ApiError::IncorrectRequest(err)) => { + eprintln!("fatal: {}", err.status()); + std::process::exit(1); + }, + Err(ApiError::EmptyError(_)) => { + eprintln!("Failed to get body"); + std::process::exit(1); + } + Err(ApiError::RequestError(err)) => { + eprintln!("fatal: {}", err); + std::process::exit(1); + }, + Err(ApiError::Unexpected(_)) => todo!() + }; + + let lastmodified = prop.lastmodified.unwrap().timestamp_millis(); + + // delete source and create destination blob + if let Err(err) = Blob::new(obj.path.clone()).create(&lastmodified.to_string(), false) { + eprintln!("err: creating ref of {}: {}", obj.name.clone(), err); + } + if let Err(err) = Blob::new(obj.path_from.clone().unwrap()).rm() { + eprintln!("err: removing ref of {}: {}", obj.name.clone(), err); + } + + Ok(()) + } + + // download file with .distant at the end + fn conflict(&self) { + todo!() + } +} diff --git a/src/commands/push/new.rs b/src/commands/push/new.rs index a3f8382..d7ae645 100644 --- a/src/commands/push/new.rs +++ b/src/commands/push/new.rs @@ -67,7 +67,7 @@ impl PushChange for New { let lastmodified = prop.lastmodified.unwrap().timestamp_millis(); // create new blob - Blob::new(obj.path.clone()).create(&lastmodified.to_string(), true)?; + Blob::new(obj.path.clone()).create(&lastmodified.to_string(), false)?; Ok(()) } diff --git a/src/commands/push/push_factory.rs b/src/commands/push/push_factory.rs index da14084..28dd7f1 100644 --- a/src/commands/push/push_factory.rs +++ b/src/commands/push/push_factory.rs @@ -2,13 +2,14 @@ use std::path::PathBuf; use std::io; use crate::commands::status::{State, LocalObj}; use crate::services::api::ApiError; -use crate::store::object; use crate::services::req_props::ReqProps; use crate::commands::push::new::New; use crate::commands::push::new_dir::NewDir; use crate::commands::push::rm_dir::RmDir; use crate::commands::push::deleted::Deleted; use crate::commands::push::modified::Modified; +use crate::commands::push::moved::Moved; +use crate::store::object::blob::Blob; #[derive(Debug)] pub enum PushState { @@ -39,6 +40,7 @@ pub trait PushChange { } fn flow(&self, obj: &LocalObj, whitelist: Option) -> PushFlowState { + // todo moved: from same file, destination doesn't exist but parent do if self.is_whitelisted(obj, whitelist) { return PushFlowState::Whitelisted; } @@ -69,7 +71,9 @@ pub trait PushChange { }; // check if remote is newest - let last_sync_ts = object::get_timestamp(obj.path.to_str().unwrap().to_string()).unwrap(); + let last_sync_ts = Blob::new(obj.path.clone()) + .saved_remote_ts() + .parse::().unwrap(); let remote_ts = obj_data.lastmodified.unwrap().timestamp_millis(); if last_sync_ts < remote_ts { @@ -88,6 +92,7 @@ impl PushFactory { State::New => Box::new(New { obj }), State::Modified => Box::new(Modified { obj }), State::Deleted => Box::new(Deleted { obj }), + State::Moved => Box::new(Moved { obj }), State::Default => todo!(), _ => todo!(), } diff --git a/src/services/move.rs b/src/services/move.rs new file mode 100644 index 0000000..38d3bde --- /dev/null +++ b/src/services/move.rs @@ -0,0 +1,61 @@ +use reqwest::{Method, Response, Error, header::HeaderValue}; +use crate::services::api::{ApiBuilder, ApiError}; +use crate::clone::get_url_props; +use crate::commands::config; + +pub struct Move { + api_builder: ApiBuilder, +} + +impl Move { + pub fn new() -> Self { + Move { + api_builder: ApiBuilder::new(), + } + } + + pub fn set_url(&mut self, url: &str, destination: &str) -> &mut Move { + self.api_builder.build_request(Method::from_bytes(b"MOVE").unwrap(), url); + + let remote = match config::get("remote") { + Some(r) => r, + None => { + eprintln!("fatal: unable to find a remote"); + std::process::exit(1); + } + }; + let (host, username, root) = get_url_props(&remote); + let mut url = String::from(host); + url.push_str("/remote.php/dav/files/"); + url.push_str(username.unwrap()); + url.push_str(&root); + url.push_str("/"); + if destination != "/" { + url.push_str(destination); + } + self.api_builder.set_header("Destination", HeaderValue::from_str(&url).unwrap()); + self + } + + pub async fn send(&mut self) -> Result { + self.api_builder.send().await + } + + pub fn overwrite(&mut self, overwrite: bool) -> &mut Move { + self.api_builder.set_header("Overwrite", HeaderValue::from_str({ + if overwrite { "T" } else { "F" } + }).unwrap()); + self + } + + pub fn send_with_err(&mut self) -> Result<(), ApiError> { + let res = tokio::runtime::Runtime::new().unwrap().block_on(async { + self.send().await + }).map_err(ApiError::RequestError)?; + if res.status().is_success() { + Ok(()) + } else { + Err(ApiError::IncorrectRequest(res)) + } + } +} diff --git a/src/store/object/blob.rs b/src/store/object/blob.rs index 79eabc8..8b69a56 100644 --- a/src/store/object/blob.rs +++ b/src/store/object/blob.rs @@ -162,16 +162,18 @@ impl Blob { } if let Err(err) = self.create_blob_ref(file_name.clone(), ts_remote.clone()) { - eprintln!("err: saving blob ref of {}: {}", self.obj_p.clone().display(), err); + eprintln!("err: saving blob ref of {}: {}", self.r_path.clone().display(), err); } if let Err(err) = self.create_hash_ref() { - eprintln!("err: saving hash ref of {}: {}", self.obj_p.clone().display(), err); + eprintln!("err: saving hash ref of {}: {}", self.r_path.clone().display(), err); } // update date for all parent if up_parent { - update_dates(self.r_path.clone(), ts_remote)?; + if let Err(err) = update_dates(self.r_path.clone(), ts_remote) { + eprintln!("err: updating parent date of {}: {}", self.r_path.clone().display(), err); + } } Ok(()) @@ -266,7 +268,7 @@ impl Blob { } } - fn saved_remote_ts(&mut self) -> String { + pub fn saved_remote_ts(&mut self) -> String { self.read_data(); if self.data.len() >= 2 { self.data[1].clone() @@ -329,7 +331,6 @@ impl Blob { } fn has_same_hash(&mut self) -> bool { - self.read_data(); if self.saved_hash() == String::new() { return false; } let file_hash = self.get_file_hash().clone(); self.saved_hash() == file_hash @@ -340,33 +341,44 @@ impl Blob { } pub fn get_local_obj(&mut self) -> LocalObj { + let mut path_from = None; let state = { let has_obj_ref = self.obj_p.clone().exists(); let blob_exists = self.a_path.clone().exists(); + if has_obj_ref && !blob_exists { State::Deleted } else if !has_obj_ref && blob_exists { - State::New + let identical_blobs = self.get_all_identical_blobs(); + if identical_blobs.len() != 0 { + let identical_blob = Blob::new(identical_blobs[0].clone().into()) + .get_local_obj(); + if identical_blob.state == State::Deleted { + path_from = Some(identical_blob.path); + State::Moved + } else if identical_blob.state == State::Default { + path_from = Some(identical_blob.path); + State::Copied + } else { + State::New + } + } else { + State::New + } } else if !has_obj_ref && !blob_exists { State::Default } else if self.has_change() { - // todo - if false { - State::Moved - } else if false { - State::Copied - } else { - State::Modified - } + State::Modified } else { State::Default } }; + LocalObj { otype: String::from("blob"), name: path_buf_to_string(self.r_path.clone()), path: self.r_path.clone(), - path_from: None, + path_from, state } }