diff --git a/src/commands/clone.rs b/src/commands/clone.rs index e2bb303..a7aff56 100644 --- a/src/commands/clone.rs +++ b/src/commands/clone.rs @@ -107,7 +107,7 @@ fn save_blob(obj: ObjProps) { let relative_s = &obj.clone().relative_s.unwrap(); let relative_p = PathBuf::from(&relative_s); let lastmodified = obj.clone().lastmodified.unwrap().timestamp_millis(); - if let Err(err) = Blob::new(relative_p).create(&lastmodified.to_string(), false) { + if let Err(err) = Blob::from_path(relative_p).create(&lastmodified.to_string(), false) { eprintln!("err: saving ref of {} ({})", relative_s.clone(), err); } } diff --git a/src/commands/pull.rs b/src/commands/pull.rs index 3df3a01..acdee0c 100644 --- a/src/commands/pull.rs +++ b/src/commands/pull.rs @@ -50,7 +50,7 @@ fn update_blob(obj: ObjProps) { let relative_p = PathBuf::from(&relative_s); let lastmodified = obj.clone().lastmodified.unwrap().timestamp_millis(); // todo update function - if let Err(err) = Blob::new(relative_p).create(&lastmodified.to_string(), false) { + if let Err(err) = Blob::from_path(relative_p).create(&lastmodified.to_string(), false) { eprintln!("err: saving ref of {} ({})", relative_s.clone(), err); } } diff --git a/src/commands/push/copied.rs b/src/commands/push/copied.rs index d8ba916..a51af79 100644 --- a/src/commands/push/copied.rs +++ b/src/commands/push/copied.rs @@ -70,7 +70,7 @@ impl PushChange for Copied { let lastmodified = prop.lastmodified.unwrap().timestamp_millis(); // create destination blob - if let Err(err) = Blob::new(obj.path.clone()).create(&lastmodified.to_string(), false) { + if let Err(err) = Blob::from_path(obj.path.clone()).create(&lastmodified.to_string(), false) { eprintln!("err: creating ref of {}: {}", obj.name.clone(), err); } diff --git a/src/commands/push/deleted.rs b/src/commands/push/deleted.rs index 134fb66..38f53c2 100644 --- a/src/commands/push/deleted.rs +++ b/src/commands/push/deleted.rs @@ -43,7 +43,7 @@ impl PushChange for Deleted { // update tree // todo date - Blob::new(obj.path.clone()).rm()?; + Blob::from_path(obj.path.clone()).rm()?; // remove index index::rm_line(obj.path.to_str().unwrap())?; diff --git a/src/commands/push/modified.rs b/src/commands/push/modified.rs index 4119f22..1f18ae5 100644 --- a/src/commands/push/modified.rs +++ b/src/commands/push/modified.rs @@ -68,7 +68,7 @@ impl PushChange for Modified { let lastmodified = prop.lastmodified.unwrap().timestamp_millis(); // update blob - Blob::new(obj.path.clone()).update(&lastmodified.to_string())?; + Blob::from_path(obj.path.clone()).update(&lastmodified.to_string())?; Ok(()) } diff --git a/src/commands/push/moved.rs b/src/commands/push/moved.rs index 916ae8d..a5632f1 100644 --- a/src/commands/push/moved.rs +++ b/src/commands/push/moved.rs @@ -70,10 +70,10 @@ impl PushChange for Moved { 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) { + if let Err(err) = Blob::from_path(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() { + if let Err(err) = Blob::from_path(obj.path_from.clone().unwrap()).rm() { eprintln!("err: removing ref of {}: {}", obj.name.clone(), err); } diff --git a/src/commands/push/new.rs b/src/commands/push/new.rs index df6cb73..10066cc 100644 --- a/src/commands/push/new.rs +++ b/src/commands/push/new.rs @@ -68,7 +68,7 @@ impl PushChange for New { let lastmodified = prop.lastmodified.unwrap().timestamp_millis(); // create new blob - Blob::new(obj.path.clone()).create(&lastmodified.to_string(), false)?; + Blob::from_path(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 98d4d01..9765e15 100644 --- a/src/commands/push/push_factory.rs +++ b/src/commands/push/push_factory.rs @@ -73,7 +73,7 @@ pub trait PushChange { }; // check if remote is newest - let last_sync_ts = Blob::new(obj.path.clone()) + let last_sync_ts = Blob::from_path(obj.path.clone()) .saved_remote_ts() .parse::().unwrap(); let remote_ts = obj_data.lastmodified.unwrap().timestamp_millis(); diff --git a/src/commands/status.rs b/src/commands/status.rs index 021c6e2..28ea6dc 100644 --- a/src/commands/status.rs +++ b/src/commands/status.rs @@ -11,6 +11,7 @@ use crate::store::object::blob::Blob; use crate::utils::read::{read_folder, read_lines}; use crate::store::object::tree; use crate::store::index; +use crate::store::object::object::ObjMethods; pub struct StatusArgs { pub nostyle: bool, @@ -97,7 +98,7 @@ fn should_retain(hasher: &mut Sha1, key: String, obj: LocalObj, move_copy_hashes { return true; } - let mut blob = Blob::new(obj.path.clone()); + let mut blob = Blob::from_path(obj.path.clone()); let mut flag = true; let identical_blobs = blob.get_all_identical_blobs(); @@ -172,7 +173,7 @@ pub fn get_all_staged() -> Vec { let mut staged_objs = vec![]; for line in lines { - let obj = Blob::new(line).get_local_obj(); + let obj = Blob::from_path(line).get_local_obj(); if obj.state != State::Default { staged_objs.push(obj); } @@ -266,7 +267,7 @@ fn get_diff() -> (HashMap, HashMap, Vec, - obj_p: PathBuf, // path of the object file - data: Vec, // content of the blob + pub obj: Obj, + data: Vec, // content of the ref file + file_hash: Option, // hash of the file's content } +//pub struct Blob { +// r_path: PathBuf, // relative path +// a_path: PathBuf, // absolute path +// hash: String, // hash of relative path +// file_hash: Option, +// obj_p: PathBuf, // path of the object file +// data: Vec, // content of the blob +//} + + + impl Blob { - pub fn new(r_path: S) -> Blob where S: IntoPathBuf { - let r_path = r_path.into(); - if r_path.is_dir() { - eprintln!("{}: is a directory not a blob", r_path.display()); - } - let mut hasher = Sha1::new(); - hasher.input_str(r_path.to_str().unwrap()); - let hash = hasher.result_str(); - - let (dir, res) = hash.split_at(2); - - let mut obj_p = path::objects(); - obj_p.push(dir); - obj_p.push(res); - - let root = path::repo_root(); - let a_path = root.join(r_path.clone()); - - Blob { - r_path, - a_path, - hash, - file_hash: None, - obj_p, + pub fn new(obj: Obj) -> Self { + Self { + obj, data: vec![], - } + file_hash: None, + } } - - fn get_line_filename(&mut self) -> (String, String) { - let file_name = self.r_path.file_name().unwrap().to_str().unwrap().to_owned(); - let mut line = String::from("blob"); - line.push_str(" "); - line.push_str(&self.hash); - line.push_str(" "); - line.push_str(&file_name); - (line, file_name) + pub fn from_path(r_path: S) -> Blob where S: IntoPathBuf { + let r_path = r_path.into(); + Self { + obj: Obj::from_path(r_path), + data: vec![], + file_hash: None, + } } fn get_file_hash(&mut self) -> String { if self.file_hash.is_none() { - let bytes = std::fs::read(self.a_path.clone()).unwrap(); + let bytes = std::fs::read(self.get_file_path()).unwrap(); let hash = md5::compute(&bytes); self.file_hash = Some(format!("{:x}", hash)) } self.file_hash.clone().unwrap() } - fn create_blob_ref(&mut self, file_name: String, ts_remote: &str) -> io::Result<()> { - let metadata = fs::metadata(self.a_path.clone())?; + /// read line of blob to get all informations and store them in self.data + pub fn read_data(&mut self) { + if self.data.len() == 0 { + if let Ok(mut file) = File::open(self.get_obj_path()) { + let mut buffer = String::new(); + let _ = file.read_to_string(&mut buffer); + let data = buffer.rsplit(' ').collect::>(); + for e in data { + self.data.push(String::from(e)); + } + self.data.reverse(); + + // remove \n of last element + if let Some(last) = self.data.last_mut() { + if last.ends_with("\n") { + last.pop(); + } + } + } + } + } + + fn get_data_index(&mut self, index: usize) -> String { + self.read_data(); + if self.data.len() >= index + 1 { + self.data[index].clone() + } else { + String::new() + } + } + + fn saved_filename(&mut self) -> String { + self.get_data_index(0) + } + + pub fn saved_remote_ts(&mut self) -> String { + self.get_data_index(1) + } + + fn saved_local_size(&mut self) -> String { + self.get_data_index(2) + } + + fn saved_local_ts(&mut self) -> u64 { + match self.get_data_index(3).as_str() { + "" => 0, + str => str.parse::().unwrap() + } + } + + fn saved_hash(&mut self) -> String { + self.get_data_index(4) + } + + fn has_same_size(&mut self) -> bool { + let metadata = match fs::metadata(self.get_file_path()) { + Ok(m) => m, + Err(_) => return true, + }; + + if self.saved_local_size() == String::new() { return true; } + metadata.len().to_string() == self.saved_local_size() + } + + fn is_newer(&mut self) -> bool { + let metadata = match fs::metadata(self.get_file_path()) { + Ok(m) => m, + Err(_) => return true, + }; + + let secs = metadata + .modified() + .unwrap() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + if self.saved_local_ts() == 0 { return true; } + secs > self.saved_local_ts() + } + + fn has_same_hash(&mut self) -> bool { + if self.saved_hash() == String::new() { return false; } + let file_hash = self.get_file_hash().clone(); + self.saved_hash() == file_hash + } + + pub fn has_changes(&mut self) -> bool { + !self.has_same_size() || (self.is_newer() && !self.has_same_hash()) + } + + pub fn get_all_identical_blobs(&mut self) -> Vec { + // an empty file is a new file not the copy of another empty file + if self.get_file_hash() == HASH_EMPTY { + return vec![]; + } + + let refs_p = self.get_obj_path(); + let mut blobs: Vec = vec![]; + if let Ok(lines) = read::read_lines(refs_p) { + for line in lines { + if let Ok(l) = line { + blobs.push(l.clone()); + } + } + } + blobs + } + + pub fn status(&mut self, path_from: &mut Option) -> State { + let has_obj_ref = self.get_obj_path().exists(); + let blob_exists = self.get_file_path().exists(); + + if has_obj_ref && !blob_exists { + State::Deleted + } else if !has_obj_ref && blob_exists { + let identical_blobs = self.get_all_identical_blobs(); + if identical_blobs.len() != 0 { + let identical_blob = Blob::from_path(identical_blobs[0].clone()).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_changes() { + State::Modified + } else { + State::Default + } + } + + fn create_blob_ref(&mut self, ts_remote: &str) -> io::Result<()> { + let metadata = fs::metadata(self.get_file_path())?; let secs = metadata .modified() .unwrap() @@ -81,30 +207,28 @@ impl Blob { .unwrap() .as_secs(); - let mut content = file_name.clone(); - 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()); + // build line with all needed properties + let content = format!("{} {} {} {} {}", + self.get_name(), + ts_remote, + metadata.len().to_string(), + secs.to_string(), + self.get_file_hash()); - let binding = self.obj_p.clone(); - let child = binding.file_name(); - self.obj_p.pop(); - if !self.obj_p.clone().exists() { - fs::create_dir_all(self.obj_p.clone())?; + // create parent dir if needed + let mut obj_path = self.get_obj_path(); + obj_path.pop(); + if !obj_path.exists() { + fs::create_dir_all(obj_path)?; } - self.obj_p.push(child.unwrap().to_str().unwrap()); + // open ref file let mut file = OpenOptions::new() .create_new(true) .write(true) - .open(self.obj_p.clone())?; + .open(self.get_obj_path())?; - writeln!(file, "{}", &content)?; + writeln!(file, "{}", content)?; Ok(()) } @@ -134,261 +258,101 @@ impl Blob { .open(refs_p)?; // todo deal with duplicate content - writeln!(file, "{}", self.r_path.clone().to_str().unwrap())?; + writeln!(file, "{}", self.get_relative_file_path().to_str().unwrap())?; Ok(()) } - pub fn get_all_identical_blobs(&mut self) -> Vec { - // an empty file is a new file not the copy of another empty file - if self.get_file_hash() == HASH_EMPTY { - return vec![]; - } - - let refs_p = self.get_file_ref(); - let mut blobs: Vec = vec![]; - if let Ok(lines) = read::read_lines(refs_p) { - for line in lines { - if let Ok(l) = line { - blobs.push(l.clone()); - } - } - } - blobs + // build line for parent reference + fn get_line(&mut self) -> String { + format!("blob {} {}", self.get_hash_path(), self.get_name()) } pub fn create(&mut self, ts_remote: &str, up_parent: bool) -> io::Result<()> { - let (line, file_name) = self.get_line_filename(); // add blob reference to parent - if self.r_path.iter().count() == 1 { + let line = self.get_line(); + if self.get_relative_file_path().iter().count() == 1 { head::add_line(line)?; } else { - add_node(self.r_path.parent().unwrap(), &line)?; + add_node(self.get_relative_file_path().parent().unwrap(), &line)?; } - if let Err(err) = self.create_blob_ref(file_name.clone(), ts_remote.clone()) { - eprintln!("err: saving blob ref of {}: {}", self.r_path.clone().display(), err); + if let Err(err) = self.create_blob_ref(ts_remote.clone()) { + eprintln!("err: saving blob ref of {}: {}", self.get_relative_file_path().display(), err); } if let Err(err) = self.create_hash_ref() { - eprintln!("err: saving hash ref of {}: {}", self.r_path.clone().display(), err); + eprintln!("err: saving hash ref of {}: {}", self.get_relative_file_path().display(), err); } // update date for all parent if up_parent { - if let Err(err) = update_dates(self.r_path.clone(), ts_remote) { - eprintln!("err: updating parent date of {}: {}", self.r_path.clone().display(), err); + if let Err(err) = update_dates(self.get_relative_file_path(), ts_remote) { + eprintln!("err: updating parent date of {}: {}", self.get_relative_file_path().display(), err); } } Ok(()) } + /// remove object pub fn rm(&mut self) -> io::Result<()> { - let (line, _) = self.get_line_filename(); + let line = self.get_line(); // remove blob reference to parent - if self.r_path.iter().count() == 1 { + if self.get_relative_file_path().iter().count() == 1 { head::rm_line(&line)?; } else { - rm_node(self.r_path.parent().unwrap(), &line)?; + rm_node(self.get_relative_file_path().parent().unwrap(), &line)?; } // remove blob object - fs::remove_file(self.obj_p.clone())?; + fs::remove_file(self.get_obj_path())?; 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)?; +// // 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()) { - let mut buffer = String::new(); - let _ = file.read_to_string(&mut buffer); - let data = buffer.rsplit(' ').collect::>(); - for e in data { - self.data.push(String::from(e)); - } - self.data.reverse(); - - // remove \n of last element - 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() - } - } - - pub 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, - }; - - if self.saved_local_size() == String::new() { return true; } - metadata.len().to_string() == self.saved_local_size() - } - - fn is_newer(&mut self) -> bool { - let metadata = match fs::metadata(self.a_path.clone()) { - Ok(m) => m, - Err(_) => return true, - }; - - let secs = metadata - .modified() - .unwrap() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(); - if self.saved_local_ts() == 0 { return true; } - secs > self.saved_local_ts() - } - - fn has_same_hash(&mut self) -> bool { - if self.saved_hash() == String::new() { return false; } - let file_hash = self.get_file_hash().clone(); - 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 status(&mut self, path_from: &mut Option) -> 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 { - let identical_blobs = self.get_all_identical_blobs(); - if identical_blobs.len() != 0 { - let identical_blob = Blob::new(identical_blobs[0].clone()) - .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() { - State::Modified - } else { - State::Default - } - } - - pub fn get_local_obj(&mut self) -> LocalObj { - let mut path_from = None; - let state = self.status(&mut path_from); - - LocalObj { - otype: String::from("blob"), - name: path_buf_to_string(self.r_path.clone()), - path: self.r_path.clone(), - path_from, - state - } - } } - diff --git a/src/store/object/object.rs b/src/store/object/object.rs new file mode 100644 index 0000000..dfff112 --- /dev/null +++ b/src/store/object/object.rs @@ -0,0 +1,207 @@ +use std::path::PathBuf; +use crypto::sha1::Sha1; +use crypto::digest::Digest; +use crate::utils::path; +use crate::store::object::{blob::Blob, tree::Tree}; +use crate::store::head; +use crate::commands::status::{State, LocalObj}; + + +enum ObjType { + TREE, + BLOB, + DEFAULT +} + +pub trait ObjMethods { + fn get_type(&self) -> ObjType; + fn get_obj_path(&self) -> PathBuf; + fn get_file_path(&self) -> PathBuf; + fn get_relative_file_path(&self) -> PathBuf; + fn get_name(&self) -> String; + fn get_hash_path(&self) -> String; + fn get_local_obj(&self) -> LocalObj; +} + +pub struct Obj { + name: String, + ref_path: Option, + obj_path: PathBuf, + obj_type: ObjType, + file_path: PathBuf, // file here is used as both file and directory + relative_file_path: PathBuf, + hash_path: String, // hash of the relative path of the file + //tree: Option<&Tree>, + //blob: Option<&Blob> +} + + +impl ObjMethods for Obj { + fn get_type(&self) -> ObjType { + self.obj_type + } + + fn get_obj_path(&self) -> PathBuf { + self.obj_path + } + + fn get_file_path(&self) -> PathBuf { + self.file_path + } + + fn get_relative_file_path(&self) -> PathBuf { + self.relative_file_path + } + + fn get_local_obj(&self) -> LocalObj { + LocalObj { + otype: match self.obj_type { + ObjType::BLOB => String::from("blob"), + ObjType::TREE => String::from("tree"), + ObjType::DEFAULT => String::from("default"), + }, + name: self.name, + path: self.file_path, + path_from: None, + state: State::New + } + } + + fn get_name(&self) -> String { + self.name + } + fn get_hash_path(&self) -> String { + self.hash_path + } +} + +impl ObjMethods for Blob { + fn get_type(&self) -> ObjType { + self.obj.get_type() + } + + fn get_obj_path(&self) -> PathBuf { + self.obj.get_obj_path() + } + + fn get_file_path(&self) -> PathBuf { + self.obj.get_file_path() + } + + fn get_relative_file_path(&self) -> PathBuf { + self.obj.get_relative_file_path() + } + + fn get_local_obj(&self) -> LocalObj { + self.obj.get_local_obj() + } + + fn get_name(&self) -> String { + self.obj.get_name() + } + + fn get_hash_path(&self) -> String { + self.obj.get_hash_path() + } +} + +impl ObjMethods for Tree { + fn get_type(&self) -> ObjType { + self.obj.get_type() + } + + fn get_obj_path(&self) -> PathBuf { + self.obj.get_obj_path() + } + + fn get_file_path(&self) -> PathBuf { + self.obj.get_file_path() + } + + fn get_relative_file_path(&self) -> PathBuf { + self.obj.get_relative_file_path() + } + + fn get_local_obj(&self) -> LocalObj { + self.obj.get_local_obj() + } + + fn get_name(&self) -> String { + self.obj.get_name() + } + + fn get_hash_path(&self) -> String { + self.obj.get_hash_path() + } +} + +impl Obj { + //fn new(name: &str) -> Self { + // Obj { + // name: String::from(name), + // ref_path: None, + // obj_path: PathBuf::new(), + // file_path: PathBuf::new(), + // obj_type: ObjType::DEFAULT, + // } + //} + + pub fn from_path(path: PathBuf) -> Self { + let mut hasher = Sha1::new(); + hasher.input_str(path.to_str().unwrap()); + let hash = hasher.result_str(); + + let (dir, res) = hash.split_at(2); + let mut obj_path = path::objects(); + obj_path.push(dir); + obj_path.push(res); + + let root = path::repo_root(); + let abs_path = root.join(path.clone()); + Obj { + name: match abs_path.file_name() { + None => String::new(), + Some(name) => name.to_str().unwrap().to_owned() + }, + ref_path: None, + obj_path, + obj_type: match path.exists() { + true => match path.is_dir() { + true => ObjType::TREE, + false => ObjType::BLOB + }, + false => ObjType::DEFAULT + }, + file_path: abs_path, + relative_file_path: path, + hash_path: hash, + } + } + + pub fn from_line(line: String) -> Self { + + } + + pub fn from_head() -> Self { + Obj { + name: String::new(), + ref_path: None, + obj_path: head::path(), + obj_type: ObjType::TREE, + file_path: PathBuf::new(), + relative_file_path: PathBuf::new(), + hash_path: String::new(), + } + } + + + + //fn spawn(&self, kind: &str) -> Box { + // match kind { + // "Blob" => Box::new(|_| Blob::new(self.clone())), + // "Tree" => Box::new(|_| Tree::new(self.clone())), + // _ => unreachable!(), + // } + //} +} + diff --git a/src/store/object/tree.rs b/src/store/object/tree.rs index 39c270f..414a3c3 100644 --- a/src/store/object/tree.rs +++ b/src/store/object/tree.rs @@ -1,106 +1,161 @@ -use std::fs::File; -use std::io; +use crate::store::object::object::Obj; use std::path::PathBuf; -use crate::utils::path::path_buf_to_string; -use crate::utils::{read, path}; -use crate::store::head; -use crate::store::object::{self, update_dates, parse_path, hash_obj, add_node, create_obj}; +use crate::store::object::blob::Blob; +use std::iter::{Iter, Empty}; +use crate::store::object::object::ObjMethods; +use std::fs::{self, File, OpenOptions}; +use std::io::{self, BufRead, BufReader, Write, Lines}; +use crate::utils::read; +pub struct Tree { + pub obj: Obj, + pub lines: Option>>, + pub lines_iter: Option>>>, - -pub fn add(path: PathBuf, date: &str, up_parent: bool) -> io::Result<()> { - let (line, hash, name) = parse_path(path.clone(), false); - - // add tree reference to parent - if path.iter().count() == 1 { - head::add_line(line)?; - } else { - add_node(path.parent().unwrap(), &line)?; - } - - // create tree object - let mut content = name; - content.push_str(" "); - content.push_str(date); - create_obj(hash, &content)?; - - // update date for all parent - if up_parent { - update_dates(path, date)?; - } - - Ok(()) } -pub fn rm(path: PathBuf) -> io::Result<()> { - let (_, lines) = read(path_buf_to_string(path.to_path_buf())).unwrap(); - for line in lines { - let (ftype, hash, _) = parse_line(line.unwrap()); - if ftype == String::from("blob") { - object::rm(&hash)?; - } else { - rm_hash(hash)?; + +impl Tree { + + pub fn from_head() -> Self { + Tree { + obj: Obj::from_head(), + lines: None, + lines_iter: None } } - Ok(()) -} -fn rm_hash(hash: String) -> io::Result<()> { - let mut obj_p = path::objects(); - let (dir, res) = hash.split_at(2); - obj_p.push(dir); - obj_p.push(res); + pub fn from_path(path: PathBuf) -> Self { + Tree { + obj: Obj::from_path(path), + lines: None, + lines_iter: None + } + } - match read::read_lines(obj_p) { - Ok(mut reader) => { - reader.next(); - for line in reader { - let (ftype, hash, _) = parse_line(line.unwrap()); - if ftype == String::from("blob") { - object::rm(&hash)?; - } else { - rm_hash(hash)?; - } + pub fn read_data(&mut self) { + if self.lines.is_none() { + if let Ok(mut file) = File::open(self.get_obj_path()) { + self.lines = Some(BufReader::new(file).lines()); + self.lines_iter = Some(self.lines.iter()); } - }, - Err(err) => { - eprintln!("error reading tree: {}", err); - }, - } - Ok(()) -} - -pub fn read(tree: String) -> Option<(String, io::Lines>)> { - let mut obj_p = path::objects(); - - let (dir, res) = hash_obj(&tree); - obj_p.push(dir); - obj_p.push(res); - - match read::read_lines(obj_p) { - Ok(mut reader) => { - let name = match reader.next() { - Some(Ok(line)) => line, - _ => String::new(), - }; - Some((name, reader)) - }, - Err(err) => { - eprintln!("error reading tree: {}", err); - None - }, - } -} - -pub fn parse_line(line: String) -> (String, String, String) { - let mut split = line.rsplit(' '); - if split.clone().count() != 3 { - eprintln!("fatal: invalid object(s)"); - std::process::exit(1); + } } - let name = split.next().unwrap(); - let hash = split.next().unwrap(); - let ftype = split.next().unwrap(); - (String::from(ftype), String::from(hash), String::from(name)) + fn get_iter(&mut self) -> Iter>> { + if let Some(iter) = self.lines_iter { + iter + } + std::process::exit(4); + } + + pub fn next(&mut self) -> Option { + self.read_data(); + let iter = self.get_iter(); + match iter.next() { + Some(line) => Some(Obj::from_line(line)), + None => None + } + } + + + // create } + + +//pub fn add(path: PathBuf, date: &str, up_parent: bool) -> io::Result<()> { +// let (line, hash, name) = parse_path(path.clone(), false); +// +// // add tree reference to parent +// if path.iter().count() == 1 { +// head::add_line(line)?; +// } else { +// add_node(path.parent().unwrap(), &line)?; +// } +// +// // create tree object +// let mut content = name; +// content.push_str(" "); +// content.push_str(date); +// create_obj(hash, &content)?; +// +// // update date for all parent +// if up_parent { +// update_dates(path, date)?; +// } +// +// Ok(()) +//} +// +//pub fn rm(path: PathBuf) -> io::Result<()> { +// let (_, lines) = read(path_buf_to_string(path.to_path_buf())).unwrap(); +// for line in lines { +// let (ftype, hash, _) = parse_line(line.unwrap()); +// if ftype == String::from("blob") { +// object::rm(&hash)?; +// } else { +// rm_hash(hash)?; +// } +// } +// Ok(()) +//} +// +//fn rm_hash(hash: String) -> io::Result<()> { +// let mut obj_p = path::objects(); +// let (dir, res) = hash.split_at(2); +// obj_p.push(dir); +// obj_p.push(res); +// +// match read::read_lines(obj_p) { +// Ok(mut reader) => { +// reader.next(); +// for line in reader { +// let (ftype, hash, _) = parse_line(line.unwrap()); +// if ftype == String::from("blob") { +// object::rm(&hash)?; +// } else { +// rm_hash(hash)?; +// } +// } +// }, +// Err(err) => { +// eprintln!("error reading tree: {}", err); +// }, +// } +// Ok(()) +//} +// +//pub fn read(tree: String) -> Option<(String, io::Lines>)> { +// let mut obj_p = path::objects(); +// +// let (dir, res) = hash_obj(&tree); +// obj_p.push(dir); +// obj_p.push(res); +// +// match read::read_lines(obj_p) { +// Ok(mut reader) => { +// let name = match reader.next() { +// Some(Ok(line)) => line, +// _ => String::new(), +// }; +// Some((name, reader)) +// }, +// Err(err) => { +// eprintln!("error reading tree: {}", err); +// None +// }, +// } +//} +// +//pub fn parse_line(line: String) -> (String, String, String) { +// let mut split = line.rsplit(' '); +// if split.clone().count() != 3 { +// eprintln!("fatal: invalid object(s)"); +// std::process::exit(1); +// } +// +// let name = split.next().unwrap(); +// let hash = split.next().unwrap(); +// let ftype = split.next().unwrap(); +// (String::from(ftype), String::from(hash), String::from(name)) +//} diff --git a/src/utils/remote.rs b/src/utils/remote.rs index 577b78a..b89d272 100644 --- a/src/utils/remote.rs +++ b/src/utils/remote.rs @@ -208,7 +208,7 @@ fn get_non_new_local_element(iter: &mut dyn Iterator) -> Option !Object::new(el.unwrap().clone().to_str().unwrap()).exists() } else { // ignore newly created file (not sync) - Blob::new(el.unwrap().clone()).status(&mut None) == State::New + Blob::from_path(el.unwrap().clone()).status(&mut None) == State::New } } { el = iter.next(); diff --git a/tests/push.rs b/tests/push.rs index eeb5af5..8462440 100644 --- a/tests/push.rs +++ b/tests/push.rs @@ -1,5 +1,4 @@ use rand::{distributions::Alphanumeric, Rng}; -use std::env; mod utils; use utils::{server::ServerTest, client::ClientTest}; @@ -61,4 +60,46 @@ mod push_tests { client.clean(); server.clean(); } + + #[test] + fn push_dir() { + let id = get_random_test_id(); + let mut server = ServerTest::new(id.clone()).init(); + let mut client = ClientTest::new(id).init(); + + let _ = client.add_dir("dir"); + let _ = client.add_file("dir/file2", "bar"); + + // push dir and file2 + client.run_cmd_ok("add file2"); + client.run_cmd_ok("push"); + + // tests + assert!(server.has_file("dir/file2", "bar")); + + client.clean(); + server.clean(); + } + + #[test] + fn push_all() { + let id = get_random_test_id(); + let mut server = ServerTest::new(id.clone()).init(); + let mut client = ClientTest::new(id).init(); + + let _ = client.add_file("file1", "foo"); + let _ = client.add_dir("dir"); + let _ = client.add_file("dir/file2", "bar"); + + // push dir and file2 + client.run_cmd_ok("add *"); + client.run_cmd_ok("push"); + + // tests + assert!(server.has_file("file1", "foo")); + assert!(server.has_file("dir/file2", "bar")); + + client.clean(); + server.clean(); + } } diff --git a/tests/utils/client.rs b/tests/utils/client.rs index 2f4072b..89acad1 100644 --- a/tests/utils/client.rs +++ b/tests/utils/client.rs @@ -23,8 +23,6 @@ impl ClientTest { let mut exe_path = env::current_dir().unwrap(); exe_path = exe_path.join("target/debug/nextsync"); - // let _ = env::set_current_dir(vol.clone()); - // build the client ClientTest { user: String::from("admin"), @@ -50,13 +48,8 @@ impl ClientTest { } - pub fn clean(mut self) -> Self { + pub fn clean(self) -> Self { let _ = fs::remove_dir_all(&self.volume); - - // set back the current dir - self.exe_path.pop(); - dbg!(self.exe_path.clone()); - let _ = env::set_current_dir(self.exe_path.clone()); self } @@ -80,6 +73,14 @@ impl ClientTest { return output; } + pub fn add_dir(&mut self, name: &str) -> std::io::Result<()> { + let mut path = self.volume.clone(); + path.push_str("/"); + path.push_str(name); + let _ = fs::create_dir_all(path)?; + Ok(()) + } + pub fn add_file(&mut self, name: &str, content: &str) -> std::io::Result<()> { let mut path = self.volume.clone(); path.push_str("/"); diff --git a/tests/utils/server.rs b/tests/utils/server.rs index 38a6409..71600e4 100644 --- a/tests/utils/server.rs +++ b/tests/utils/server.rs @@ -93,7 +93,7 @@ impl ServerTest { let full_path = self.volume.clone().join(file); if !full_path.exists() { - eprintln!("File '{}' does't exists on the server", file); + eprintln!("File '{}' doesn't exists on the server", file); return false; }