From 498fada9ec94c9eca613c7f2b44146f5a77b3b84 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Thu, 24 Aug 2023 20:59:41 +0200 Subject: [PATCH] push modification --- src/commands/clone.rs | 5 +- src/commands/push.rs | 18 +++- src/commands/push/modified.rs | 79 ++++++++++++++++ src/commands/push/new.rs | 4 - src/commands/push/push_factory.rs | 4 +- src/commands/status.rs | 22 ++++- src/store/object.rs | 3 +- src/store/object/blob.rs | 149 +++++++++++++++++++++++++++--- 8 files changed, 254 insertions(+), 30 deletions(-) create mode 100644 src/commands/push/modified.rs diff --git a/src/commands/clone.rs b/src/commands/clone.rs index 3e31133..db07d28 100644 --- a/src/commands/clone.rs +++ b/src/commands/clone.rs @@ -6,6 +6,7 @@ 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; @@ -46,7 +47,7 @@ pub fn clone(args: CloneArgs) { 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(lp.to_str().unwrap().to_string()); + set_dir_path(path_buf_to_string(lp.clone())); lp }, }; @@ -93,7 +94,7 @@ pub fn clone(args: CloneArgs) { let downloader = Downloader::new() .set_api_props(api_props.clone()) .set_files(files) - .should_log() + //.should_log() .download(ref_path.clone(), Some(&save_blob)); } diff --git a/src/commands/push.rs b/src/commands/push.rs index 45664e4..219ef7a 100644 --- a/src/commands/push.rs +++ b/src/commands/push.rs @@ -1,12 +1,16 @@ use std::path::PathBuf; use crate::commands::{status, config}; use crate::commands::push::push_factory::{PushFactory, PushState}; +use crate::store::index; + +use super::status::LocalObj; pub mod push_factory; pub mod new; pub mod new_dir; pub mod rm_dir; pub mod deleted; +pub mod modified; pub fn push() { // todo err when pushing new folder @@ -52,20 +56,24 @@ pub fn push() { match push_factory.can_push(&mut whitelist) { PushState::Valid => { match push_factory.push() { - Ok(()) => (), + Ok(()) => remove_obj_from_index(obj.clone()), Err(err) => { eprintln!("err: pushing {}: {}", obj.name, err); } } }, - PushState::Done => (), + PushState::Done => remove_obj_from_index(obj.clone()), PushState::Conflict => { // download file } - _ => todo!(), + PushState::Error => (), } } } - // read index - // if dir upload dir +} + +fn remove_obj_from_index(obj: LocalObj) { + if let Err(err) = index::rm_line(obj.path.to_str().unwrap()) { + eprintln!("err: removing {} from index: {}", obj.name, err); + } } diff --git a/src/commands/push/modified.rs b/src/commands/push/modified.rs new file mode 100644 index 0000000..e223bda --- /dev/null +++ b/src/commands/push/modified.rs @@ -0,0 +1,79 @@ +use std::path::PathBuf; +use std::io; +use crate::services::api::ApiError; +use crate::services::req_props::ReqProps; +use crate::services::upload_file::UploadFile; +use crate::commands::status::LocalObj; +use crate::commands::push::push_factory::{PushState, PushChange, PushFlowState}; +use crate::store::object::blob::Blob; + +pub struct Modified { + pub obj: LocalObj, +} + +impl PushChange for Modified { + 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::Valid, + PushFlowState::Error => PushState::Error, + } + } + + fn push(&self) -> io::Result<()> { + let obj = &self.obj; + let res = UploadFile::new() + .set_url(obj.path.to_str().unwrap()) + .set_file(obj.path.clone()) + .send_with_err(); + + match res { + Err(ApiError::IncorrectRequest(err)) => { + eprintln!("fatal: error pushing file {}: {}", obj.name, err.status()); + std::process::exit(1); + }, + Err(ApiError::RequestError(_)) => { + eprintln!("fatal: request error pushing 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(); + + // update blob + Blob::new(obj.path.clone()).update(&lastmodified.to_string())?; + + 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 4ac2173..a3f8382 100644 --- a/src/commands/push/new.rs +++ b/src/commands/push/new.rs @@ -3,7 +3,6 @@ use std::io; use crate::services::api::ApiError; use crate::services::req_props::ReqProps; use crate::services::upload_file::UploadFile; -use crate::store::index; use crate::store::object::blob::Blob; use crate::commands::status::LocalObj; use crate::commands::push::push_factory::{PushState, PushChange, PushFlowState}; @@ -70,9 +69,6 @@ impl PushChange for New { // create new blob Blob::new(obj.path.clone()).create(&lastmodified.to_string(), true)?; - // remove index - index::rm_line(obj.path.to_str().unwrap())?; - Ok(()) } diff --git a/src/commands/push/push_factory.rs b/src/commands/push/push_factory.rs index 6eac4f4..da14084 100644 --- a/src/commands/push/push_factory.rs +++ b/src/commands/push/push_factory.rs @@ -8,6 +8,7 @@ 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; #[derive(Debug)] pub enum PushState { @@ -54,6 +55,7 @@ pub trait PushChange { if err.status() == 404 { Ok(None) } else { + eprintln!("err: when requesting properties of {} ({})", obj.name, err.status()); Err(()) } }, @@ -84,7 +86,7 @@ impl PushFactory { pub fn new(&self, obj: LocalObj) -> Box { match obj.state { State::New => Box::new(New { obj }), - State::Modified => todo!(), + State::Modified => Box::new(Modified { obj }), State::Deleted => Box::new(Deleted { obj }), State::Default => todo!(), _ => todo!(), diff --git a/src/commands/status.rs b/src/commands/status.rs index 3c150cf..e218c15 100644 --- a/src/commands/status.rs +++ b/src/commands/status.rs @@ -134,12 +134,24 @@ pub struct LocalObj { } pub fn get_all_staged() -> Vec { - let (mut new_objs_hashes, mut del_objs_hashes, mut objs_modified) = get_diff(); - // get copy, modified - let staged_objs = get_staged(&mut new_objs_hashes); + let mut lines: Vec = vec![]; - staged_objs.clone() - // todo opti getting staged and then finding differences ? + if let Ok(entries) = index::read_line() { + for entry in entries { + lines.push(entry.unwrap()); + } + } + + let mut staged_objs = vec![]; + + for line in lines { + let obj = Blob::new(PathBuf::from(line)).get_local_obj(); + if obj.state != State::Default { + staged_objs.push(obj); + } + } + + staged_objs } fn get_staged(hashes: &mut HashMap) -> Vec { diff --git a/src/store/object.rs b/src/store/object.rs index 5147c23..b49d338 100644 --- a/src/store/object.rs +++ b/src/store/object.rs @@ -222,6 +222,7 @@ fn create_obj(name: String, content: &str) -> io::Result<()> { Ok(()) } +// get the last time a blob synced with remote pub fn get_timestamp(path_s: String) -> Option { let mut obj_p = path::objects(); @@ -235,7 +236,7 @@ pub fn get_timestamp(path_s: String) -> Option { Some(Ok(line)) => { let mut data = line.rsplit(' '); if data.clone().count() >= 2 { - Some(data.next().unwrap().parse::().unwrap()) + Some(data.nth_back(1).unwrap().parse::().unwrap()) } else { None } diff --git a/src/store/object/blob.rs b/src/store/object/blob.rs index 7229f73..8d16429 100644 --- a/src/store/object/blob.rs +++ b/src/store/object/blob.rs @@ -1,12 +1,13 @@ use std::io::{self, Read}; -use std::fs::File; +use std::fs::{self, File}; use std::io::Write; use std::fs::OpenOptions; use std::path::PathBuf; use std::time::SystemTime; -use std::fs; use crypto::sha1::Sha1; use crypto::digest::Digest; +use crate::commands::status::{LocalObj, State}; +use crate::utils::path::path_buf_to_string; use crate::utils::{path, read}; use crate::store::head; use crate::store::object::{update_dates, add_node, rm_node}; @@ -82,7 +83,6 @@ impl Blob { content.push_str(&secs.to_string()); content.push_str(" "); content.push_str(&self.get_file_hash()); - content.push_str(" "); let binding = self.obj_p.clone(); let child = binding.file_name(); @@ -118,6 +118,7 @@ impl Blob { // create a file in .nextsync/refs with the hash of this blob that // redirect to the relative path fn create_hash_ref(&mut self) -> io::Result<()> { + // todo check if the file has been modified for moved and copy let refs_p = self.get_file_ref(); let mut file = OpenOptions::new() @@ -126,7 +127,6 @@ impl Blob { .open(refs_p)?; // todo deal with duplicate content - writeln!(file, "{}", self.r_path.clone().to_str().unwrap())?; Ok(()) } @@ -186,6 +186,50 @@ impl Blob { Ok(()) } + pub fn update(&mut self, ts_remote: &str) -> io::Result<()> { + + // remove old hash ref + let mut refs_p = path::refs(); + let binding = self.saved_hash(); + let (dir, res) = binding.split_at(2); + refs_p.push(dir); + refs_p.push(res); + if let Err(err) = fs::remove_file(refs_p) { + eprintln!("err: removing hash ref of {}: {}", self.r_path.clone().display(), err); + } + + // creating new hash ref + if let Err(err) = self.create_hash_ref() { + eprintln!("err: saving hash ref of {}: {}", self.r_path.clone().display(), err); + } + + // updating content of blob's ref + let metadata = fs::metadata(self.a_path.clone())?; + let secs = metadata + .modified() + .unwrap() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + + let mut content = self.saved_filename(); + content.push_str(" "); + content.push_str(ts_remote); + content.push_str(" "); + content.push_str(&metadata.len().to_string()); + content.push_str(" "); + content.push_str(&secs.to_string()); + content.push_str(" "); + content.push_str(&self.get_file_hash()); + + let mut file = OpenOptions::new() + .write(true) + .open(self.obj_p.clone())?; + + writeln!(file, "{}", &content)?; + Ok(()) + } + pub fn read_data(&mut self) { if self.data.len() == 0 { if let Ok(mut file) = File::open(self.obj_p.clone()) { @@ -196,19 +240,69 @@ impl Blob { self.data.push(String::from(e)); } self.data.reverse(); + + if let Some(last) = self.data.last_mut() { + if last.ends_with("\n") { + last.pop(); + } + } } } } + fn saved_filename(&mut self) -> String { + self.read_data(); + if self.data.len() >= 1 { + self.data[0].clone() + } else { + String::new() + } + } + + fn saved_remote_ts(&mut self) -> String { + self.read_data(); + if self.data.len() >= 2 { + self.data[1].clone() + } else { + String::new() + } + } + + fn saved_local_size(&mut self) -> String { + self.read_data(); + if self.data.len() >= 3 { + self.data[2].clone() + } else { + String::new() + } + } + + fn saved_local_ts(&mut self) -> u64 { + self.read_data(); + if self.data.len() >= 4 { + self.data[3].parse::().unwrap() + } else { + 0 + } + } + + fn saved_hash(&mut self) -> String { + self.read_data(); + if self.data.len() >= 5 { + self.data[4].clone() + } else { + String::new() + } + } + fn has_same_size(&mut self) -> bool { let metadata = match fs::metadata(self.a_path.clone()) { Ok(m) => m, Err(_) => return true, }; - self.read_data(); - if self.data.len() < 3 { return true; } - metadata.len().to_string() == self.data[2] + if self.saved_local_size() == String::new() { return true; } + metadata.len().to_string() == self.saved_local_size() } fn is_newer(&mut self) -> bool { @@ -217,26 +311,57 @@ impl Blob { Err(_) => return true, }; - self.read_data(); let secs = metadata .modified() .unwrap() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_secs(); - if self.data.len() < 4 { return true; } - secs > self.data[3].parse::().unwrap() + if self.saved_local_ts() == 0 { return true; } + secs > self.saved_local_ts() } fn has_same_hash(&mut self) -> bool { self.read_data(); - if self.data.len() < 5 { return false; } + if self.saved_hash() == String::new() { return false; } let file_hash = self.get_file_hash().clone(); - self.data[4] == file_hash + self.saved_hash() == file_hash } pub fn has_change(&mut self) -> bool { !self.has_same_size() || (self.is_newer() && !self.has_same_hash()) } + + pub fn get_local_obj(&mut self) -> LocalObj { + 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 + } 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 + } + } 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, + state + } + } }