From 23908c135c294f73f61552b7b518d8848cc080d2 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Sat, 24 Jun 2023 16:07:53 +0200 Subject: [PATCH] better architecture of PushChanges and applied to new --- src/commands/add.rs | 3 +- src/commands/init.rs | 10 ++-- src/commands/push.rs | 34 ++++++++++--- src/commands/push/new.rs | 50 ++++++++----------- src/commands/push/push_factory.rs | 79 ++++++++++++++++++++++++++++--- src/commands/status.rs | 41 ++++++++-------- src/store/index.rs | 6 ++- 7 files changed, 152 insertions(+), 71 deletions(-) diff --git a/src/commands/add.rs b/src/commands/add.rs index 0a63e35..40fe46a 100644 --- a/src/commands/add.rs +++ b/src/commands/add.rs @@ -10,6 +10,8 @@ pub struct AddArgs<'a> { pub force: bool, } +// todo match deleted files +// todo match weird reg expression pub fn add(args: AddArgs) { let mut index_file = store::index::open(); let mut added_files: Vec = vec![]; @@ -27,7 +29,6 @@ pub fn add(args: AddArgs) { match path.exists() { true => { if path.is_dir() { - added_files.push(String::from(path.to_str().unwrap())); add_folder_content(path.to_path_buf(), &mut added_files); } else { diff --git a/src/commands/init.rs b/src/commands/init.rs index e063d7f..f6f75e1 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -16,27 +16,27 @@ pub fn init() { // .nextsync folder path.push(".nextsync"); match builder.create(path.clone()) { - Ok(()) => println!("Directory successfuly created"), + Ok(()) => (), Err(_) => println!("Error: cannot create directory"), }; path.push("objects"); match builder.create(path.clone()) { - Ok(()) => println!("Directory successfuly created"), + Ok(()) => (), Err(_) => println!("Error: cannot create directory"), }; path.pop(); path.push("HEAD"); match File::create(path.clone()) { - Ok(_) => println!("File successfuly created"), + Ok(_) => (), Err(_) => println!("Error: cannot create .nextsyncignore"), } path.pop(); path.push("index"); match File::create(path.clone()) { - Ok(_) => println!("File successfuly created"), + Ok(_) => (), Err(_) => println!("Error: cannot create .nextsyncignore"), } @@ -45,7 +45,7 @@ pub fn init() { path.push(".nextsyncignore"); match File::create(path) { - Ok(_) => println!("File successfuly created"), + Ok(_) => (), Err(_) => println!("Error: cannot create .nextsyncignore"), } } diff --git a/src/commands/push.rs b/src/commands/push.rs index 950a98e..f0d3428 100644 --- a/src/commands/push.rs +++ b/src/commands/push.rs @@ -1,3 +1,4 @@ +use std::path::Path; use crate::services::api::ApiError; use crate::services::upload_file::UploadFile; use crate::services::delete_path::DeletePath; @@ -5,12 +6,13 @@ use crate::services::req_props::{ReqProps, ObjProps}; use crate::store::index; use crate::store::object::{add_blob, rm_blob}; use crate::commands::{status, config}; -use crate::commands::status::{State, Obj}; +use crate::commands::status::{State, LocalObj}; use crate::commands::push::push_factory::{PushFactory, PushState}; pub mod push_factory; pub mod new; -pub mod deleted; +//pub mod new_dir; +//pub mod deleted; pub fn push() { dbg!(status::get_all_staged()); @@ -19,25 +21,45 @@ pub fn push() { Some(r) => r, None => { eprintln!("fatal: no remote set in configuration"); - std::process::exit(1); + //std::process::exit(1); + String::from("") } }; let staged_objs = status::get_all_staged(); // todo sort folder first + + // path that certify that all its children can be push whithout hesistation + // (e.g if remote dir has no changes since last sync all children + // can be pushed without verification) + let whitelist: Option<&Path> = None; + for obj in staged_objs { if obj.otype == String::from("tree") { - dbg!("should push folder"); + //let push_factory = PushFactory.new_dir(obj.clone()); + //let res = match push_factory.can_push(whitelist.clone()) { + // PushState::Valid => push_factory.push(), + // PushState::Done => (), + // PushState::Conflict => (), + // _ => todo!(), + //}; + + //match res { + // + //} + //dbg!("should push folder"); } else { let push_factory = PushFactory.new(obj.clone()); - match push_factory.can_push() { + match push_factory.can_push(whitelist.clone()) { PushState::Valid => push_factory.push(), PushState::Done => (), + PushState::Conflict => { + // download file + } _ => todo!(), } } } // read index // if dir upload dir - } diff --git a/src/commands/push/new.rs b/src/commands/push/new.rs index 61fc275..e062aa4 100644 --- a/src/commands/push/new.rs +++ b/src/commands/push/new.rs @@ -1,45 +1,33 @@ +use std::path::Path; 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::add_blob; -use crate::commands::status::Obj; -use crate::commands::push::push_factory::{PushState, PushChange, PushFactory}; +use crate::commands::status::LocalObj; +use crate::commands::push::push_factory::{PushState, PushChange, PushFactory, PushFlowState}; pub struct New { - pub obj: Obj, + pub obj: LocalObj, } impl PushChange for New { - fn can_push(&self) -> PushState { - // check if exist on server - let res = ReqProps::new() - .set_url(&self.obj.path.to_str().unwrap()) - .getlastmodified() - .send_req_single(); + fn can_push(&self, whitelist: Option<&Path>) -> PushState { + match self.flow(&self.obj, whitelist) { + PushFlowState::Whitelisted => PushState::Valid, + PushFlowState::NotOnRemote => PushState::Valid, + PushFlowState::RemoteIsNewer => PushState::Conflict, + PushFlowState::LocalIsNewer => PushState::Valid, + PushFlowState::Error => PushState::Error, + } + } - let file_infos = match res { - Ok(obj) => Ok(Some(obj)), - Err(ApiError::IncorrectRequest(err)) => { - if err.status() == 404 { - Ok(None) - } else { - Err(()) - } - }, - Err(_) => Err(()), - }; - - if let Ok(infos) = file_infos { - if let Some(info) = infos { - // file doesn't exist on remote - PushState::Valid - } else { - // todo check date - PushState::Conflict - } - } else { - PushState::Error + fn try_push(&self, whitelist: Option<&Path>) { + match self.can_push(whitelist) { + PushState::Valid => self.push(), + PushState::Conflict => todo!(), //download + PushState::Done => (), + PushState::Error => (), } } diff --git a/src/commands/push/push_factory.rs b/src/commands/push/push_factory.rs index 2d2e0ce..34cd76e 100644 --- a/src/commands/push/push_factory.rs +++ b/src/commands/push/push_factory.rs @@ -1,6 +1,10 @@ -use crate::commands::status::{State, Obj}; +use std::path::Path; +use crate::commands::status::{State, LocalObj}; +use crate::services::api::ApiError; +use crate::services::req_props::{ObjProps, ReqProps}; use crate::commands::push::new::New; -use crate::commands::push::deleted::Deleted; +//use crate::commands::push::new_dir::NewDir; +//use crate::commands::push::deleted::Deleted; #[derive(Debug)] pub enum PushState { @@ -10,20 +14,83 @@ pub enum PushState { Error, } +pub enum PushFlowState { + Whitelisted, + NotOnRemote, + RemoteIsNewer, + LocalIsNewer, + Error, +} + pub trait PushChange { - fn can_push(&self) -> PushState; + fn can_push(&self, whitelist: Option<&Path>) -> PushState; + fn try_push(&self, whitelist: Option<&Path>); fn push(&self); + + fn is_whitelisted(&self, obj: &LocalObj, path: Option<&Path>) -> bool { + match path { + Some(p) => obj.path.starts_with(p), + None => false, + } + } + + fn flow(&self, obj: &LocalObj, whitelist: Option<&Path>) -> PushFlowState { + if self.is_whitelisted(obj, whitelist) { + return PushFlowState::Whitelisted; + } + + // check if exist on server + let res = ReqProps::new() + .set_url(obj.path.to_str().unwrap()) + .getlastmodified() + .send_req_single(); + + let file_infos = match res { + Ok(obj) => Ok(Some(obj)), + Err(ApiError::IncorrectRequest(err)) => { + if err.status() == 404 { + Ok(None) + } else { + Err(()) + } + }, + Err(_) => Err(()), + }; + + let infos = match file_infos { + Ok(Some(info)) => info, + Ok(None) => return PushFlowState::NotOnRemote, + Err(_) => return PushFlowState::Error, + }; + + // check if remote is newest + // set timestamp from remote stuff + // get from file + todo!() + } } pub struct PushFactory; impl PushFactory { - pub fn new(&self, obj: Obj) -> Box { + pub fn new(&self, obj: LocalObj) -> Box { match obj.state { - State::New => Box::new(New { obj: obj.clone() }), + State::New => Box::new(New { obj }), State::Renamed => todo!(), State::Modified => todo!(), - State::Deleted => Box::new(Deleted { obj: obj.clone() }), + State::Deleted => todo!(), + //State::Deleted => Box::new(Deleted {}), + State::Default => todo!(), + } + } + + pub fn new_dir(&self, obj: LocalObj) -> Box { + match obj.state { + //State::New => Box::new(NewDir {}), + State::New => todo!(), + State::Renamed => todo!(), + State::Modified => todo!(), + State::Deleted => todo!(), State::Default => todo!(), } } diff --git a/src/commands/status.rs b/src/commands/status.rs index 09f51ba..ca5784b 100644 --- a/src/commands/status.rs +++ b/src/commands/status.rs @@ -38,14 +38,14 @@ pub fn status() { } #[derive(Debug, Clone)] -pub struct Obj { +pub struct LocalObj { pub otype: String, pub name: String, pub path: PathBuf, pub state: State, } -pub fn get_all_staged() -> Vec { +pub fn get_all_staged() -> Vec { // todo opti getting staged and then finding differences ? // todo opti return folder let (mut new_objs, mut del_objs) = get_diff(); @@ -58,18 +58,17 @@ pub fn get_all_staged() -> Vec { staged_objs } -fn get_renamed(_new_obj: &mut Vec, _del_obj: &mut Vec) -> Vec { +fn get_renamed(_new_obj: &mut Vec, _del_obj: &mut Vec) -> Vec { // get hash of all new obj, compare to hash of all del let renamed_objs = vec![]; renamed_objs } -fn get_staged(objs: &mut Vec) -> Vec { +fn get_staged(objs: &mut Vec) -> Vec { let mut indexes = HashSet::new(); - let mut staged_objs: Vec = vec![]; + let mut staged_objs: Vec = vec![]; - let nextsync_path = utils::path::nextsync().unwrap(); - if let Ok(entries) = store::index::read_line(nextsync_path.clone()) { + if let Ok(entries) = store::index::read_line() { for entry in entries { indexes.insert(entry.unwrap()); } @@ -87,7 +86,7 @@ fn get_staged(objs: &mut Vec) -> Vec { staged_objs } -fn get_diff() -> (Vec, Vec) { +fn get_diff() -> (Vec, Vec) { let mut hashes = HashMap::new(); let mut objs: Vec = vec![]; @@ -138,8 +137,8 @@ fn get_diff() -> (Vec, Vec) { } - let del_objs: Vec = hashes.iter().map(|x| { - Obj { + let del_objs: Vec = hashes.iter().map(|x| { + LocalObj { otype: x.1.otype.clone(), name: x.1.name.clone(), path: x.1.path.clone(), @@ -147,10 +146,10 @@ fn get_diff() -> (Vec, Vec) { } }).collect(); - let new_objs: Vec = objs.iter().map(|x| { + let new_objs: Vec = objs.iter().map(|x| { let p = PathBuf::from(x.to_string()); // todo name - Obj { + LocalObj { otype: get_type(p.clone()), name: x.to_string(), path: p, @@ -168,14 +167,14 @@ fn get_type(p: PathBuf) -> String { } } -fn add_to_hashmap(lines: Lines>, hashes: &mut HashMap, path: PathBuf) { +fn add_to_hashmap(lines: Lines>, hashes: &mut HashMap, path: PathBuf) { for line in lines { if let Ok(ip) = line { if ip.clone().len() > 5 { let (ftype, hash, name) = object::parse_line(ip); let mut p = path.clone(); p.push(name.clone()); - hashes.insert(String::from(hash), Obj{ + hashes.insert(String::from(hash), LocalObj{ otype: String::from(ftype), name: String::from(name), path: p, @@ -196,7 +195,7 @@ fn add_to_vec(entries: Vec, objects: &mut Vec, root: PathBuf) { } -fn print_status(staged_objs: Vec, objs: Vec) { +fn print_status(staged_objs: Vec, objs: Vec) { dbg!(staged_objs.clone()); dbg!(objs.clone()); if staged_objs.len() == 0 && objs.len() == 0 { @@ -224,31 +223,31 @@ fn print_status(staged_objs: Vec, objs: Vec) { } } -fn print_object(obj: Obj) { +fn print_object(obj: LocalObj) { if obj.state == State::Deleted { println!(" {} {}", String::from("deleted:").red(), obj.name.red()); } else if obj.state == State::Renamed { println!(" {} {}", String::from("renamed:").red(), obj.name.red()); } else if obj.state == State::New { - println!(" {} {}", String::from("new file:").red(), obj.name.red()); + println!(" {} {}", String::from("new:").red(), obj.name.red()); } else if obj.state == State::Modified { println!(" {} {}", String::from("modified:").red(), obj.name.red()); } } -fn print_staged_object(obj: Obj) { +fn print_staged_object(obj: LocalObj) { if obj.state == State::Deleted { println!(" {} {}", String::from("deleted:").green(), obj.name.green()); } else if obj.state == State::Renamed { println!(" {} {}", String::from("renamed:").green(), obj.name.green()); } else if obj.state == State::New { - println!(" {} {}", String::from("new file:").green(), obj.name.green()); + println!(" {} {}", String::from("new:").green(), obj.name.green()); } else if obj.state == State::Modified { println!(" {} {}", String::from("modified:").green(), obj.name.green()); } } -fn remove_duplicate(hashes: &mut HashMap, objects: &mut Vec, remove_option: RemoveSide) -> Vec { +fn remove_duplicate(hashes: &mut HashMap, objects: &mut Vec, remove_option: RemoveSide) -> Vec { let mut hasher = Sha1::new(); let mut duplicate = vec![]; @@ -306,7 +305,7 @@ mod tests { hasher.reset(); let mut hashes = HashMap::new(); - let default_obj = Obj { + let default_obj = LocalObj { otype: String::from("tree"), name: String::from("test"), path: PathBuf::from(""), diff --git a/src/store/index.rs b/src/store/index.rs index 6209935..73f264b 100644 --- a/src/store/index.rs +++ b/src/store/index.rs @@ -26,7 +26,11 @@ pub fn open() -> File { .open(path).expect("Cannot open index file") } -pub fn read_line(mut path: PathBuf) -> io::Result>> { +pub fn read_line() -> io::Result>> { + let mut path = match path::nextsync() { + Some(p) => p, + None => todo!(), + }; path.push("index"); read::read_lines(path) }