refactor(blob): use object trait to create blob

This commit is contained in:
grimhilt 2024-02-24 18:52:00 +01:00
parent 642c358737
commit faf7341525
17 changed files with 666 additions and 396 deletions

View File

@ -107,7 +107,7 @@ fn save_blob(obj: ObjProps) {
let relative_s = &obj.clone().relative_s.unwrap(); let relative_s = &obj.clone().relative_s.unwrap();
let relative_p = PathBuf::from(&relative_s); let relative_p = PathBuf::from(&relative_s);
let lastmodified = obj.clone().lastmodified.unwrap().timestamp_millis(); 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); eprintln!("err: saving ref of {} ({})", relative_s.clone(), err);
} }
} }

View File

@ -50,7 +50,7 @@ fn update_blob(obj: ObjProps) {
let relative_p = PathBuf::from(&relative_s); let relative_p = PathBuf::from(&relative_s);
let lastmodified = obj.clone().lastmodified.unwrap().timestamp_millis(); let lastmodified = obj.clone().lastmodified.unwrap().timestamp_millis();
// todo update function // 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); eprintln!("err: saving ref of {} ({})", relative_s.clone(), err);
} }
} }

View File

@ -70,7 +70,7 @@ impl PushChange for Copied {
let lastmodified = prop.lastmodified.unwrap().timestamp_millis(); let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
// create destination blob // 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); eprintln!("err: creating ref of {}: {}", obj.name.clone(), err);
} }

View File

@ -43,7 +43,7 @@ impl PushChange for Deleted {
// update tree // update tree
// todo date // todo date
Blob::new(obj.path.clone()).rm()?; Blob::from_path(obj.path.clone()).rm()?;
// remove index // remove index
index::rm_line(obj.path.to_str().unwrap())?; index::rm_line(obj.path.to_str().unwrap())?;

View File

@ -68,7 +68,7 @@ impl PushChange for Modified {
let lastmodified = prop.lastmodified.unwrap().timestamp_millis(); let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
// update blob // update blob
Blob::new(obj.path.clone()).update(&lastmodified.to_string())?; Blob::from_path(obj.path.clone()).update(&lastmodified.to_string())?;
Ok(()) Ok(())
} }

View File

@ -70,10 +70,10 @@ impl PushChange for Moved {
let lastmodified = prop.lastmodified.unwrap().timestamp_millis(); let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
// delete source and create destination blob // 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); 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); eprintln!("err: removing ref of {}: {}", obj.name.clone(), err);
} }

View File

@ -68,7 +68,7 @@ impl PushChange for New {
let lastmodified = prop.lastmodified.unwrap().timestamp_millis(); let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
// create new blob // 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(()) Ok(())
} }

View File

@ -73,7 +73,7 @@ pub trait PushChange {
}; };
// check if remote is newest // 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() .saved_remote_ts()
.parse::<i64>().unwrap(); .parse::<i64>().unwrap();
let remote_ts = obj_data.lastmodified.unwrap().timestamp_millis(); let remote_ts = obj_data.lastmodified.unwrap().timestamp_millis();

View File

@ -11,6 +11,7 @@ use crate::store::object::blob::Blob;
use crate::utils::read::{read_folder, read_lines}; use crate::utils::read::{read_folder, read_lines};
use crate::store::object::tree; use crate::store::object::tree;
use crate::store::index; use crate::store::index;
use crate::store::object::object::ObjMethods;
pub struct StatusArgs { pub struct StatusArgs {
pub nostyle: bool, pub nostyle: bool,
@ -97,7 +98,7 @@ fn should_retain(hasher: &mut Sha1, key: String, obj: LocalObj, move_copy_hashes
{ {
return true; return true;
} }
let mut blob = Blob::new(obj.path.clone()); let mut blob = Blob::from_path(obj.path.clone());
let mut flag = true; let mut flag = true;
let identical_blobs = blob.get_all_identical_blobs(); let identical_blobs = blob.get_all_identical_blobs();
@ -172,7 +173,7 @@ pub fn get_all_staged() -> Vec<LocalObj> {
let mut staged_objs = vec![]; let mut staged_objs = vec![];
for line in lines { 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 { if obj.state != State::Default {
staged_objs.push(obj); staged_objs.push(obj);
} }
@ -266,7 +267,7 @@ fn get_diff() -> (HashMap<String, LocalObj>, HashMap<String, LocalObj>, Vec<Stri
let diff = remove_duplicate(&mut hashes, &mut objs, RemoveSide::Both); let diff = remove_duplicate(&mut hashes, &mut objs, RemoveSide::Both);
obj_to_analyse.append(&mut diff.clone()); obj_to_analyse.append(&mut diff.clone());
} else { } else {
if Blob::new(cur_path).has_change() { if Blob::from_path(cur_path).has_changes() {
objs_modified.push(cur_obj); objs_modified.push(cur_obj);
} }
} }

View File

@ -9,6 +9,7 @@ use crate::utils::{read, path};
pub mod tree; pub mod tree;
pub mod blob; pub mod blob;
pub mod object;
pub struct Object { pub struct Object {
path: PathBuf, path: PathBuf,

View File

@ -4,76 +4,202 @@ use std::io::Write;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::SystemTime; use std::time::SystemTime;
use crypto::sha1::Sha1; use crate::commands::status::{State};
use crypto::digest::Digest;
use crate::commands::status::{LocalObj, State};
use crate::utils::into::IntoPathBuf; use crate::utils::into::IntoPathBuf;
use crate::utils::path::path_buf_to_string;
use crate::utils::{path, read}; use crate::utils::{path, read};
use crate::store::head; use crate::store::head;
use crate::store::object::{update_dates, add_node, rm_node}; use crate::store::object::{update_dates, add_node, rm_node};
use crate::store::object::object::ObjMethods;
use crate::store::object::object::Obj;
const HASH_EMPTY: &str = "d41d8cd98f00b204e9800998ecf8427e"; const HASH_EMPTY: &str = "d41d8cd98f00b204e9800998ecf8427e";
pub struct Blob { pub struct Blob {
r_path: PathBuf, // relative path pub obj: Obj,
a_path: PathBuf, // absolute path data: Vec<String>, // content of the ref file
hash: String, // hash of relative path file_hash: Option<String>, // hash of the file's content
file_hash: Option<String>,
obj_p: PathBuf, // path of the object file
data: Vec<String>, // content of the blob
} }
//pub struct Blob {
// r_path: PathBuf, // relative path
// a_path: PathBuf, // absolute path
// hash: String, // hash of relative path
// file_hash: Option<String>,
// obj_p: PathBuf, // path of the object file
// data: Vec<String>, // content of the blob
//}
impl Blob { impl Blob {
pub fn new<S>(r_path: S) -> Blob where S: IntoPathBuf { pub fn new(obj: Obj) -> Self {
let r_path = r_path.into(); Self {
if r_path.is_dir() { obj,
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,
data: vec![], data: vec![],
} file_hash: None,
}
} }
pub fn from_path<S>(r_path: S) -> Blob where S: IntoPathBuf {
fn get_line_filename(&mut self) -> (String, String) { let r_path = r_path.into();
let file_name = self.r_path.file_name().unwrap().to_str().unwrap().to_owned(); Self {
let mut line = String::from("blob"); obj: Obj::from_path(r_path),
line.push_str(" "); data: vec![],
line.push_str(&self.hash); file_hash: None,
line.push_str(" "); }
line.push_str(&file_name);
(line, file_name)
} }
fn get_file_hash(&mut self) -> String { fn get_file_hash(&mut self) -> String {
if self.file_hash.is_none() { 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); let hash = md5::compute(&bytes);
self.file_hash = Some(format!("{:x}", hash)) self.file_hash = Some(format!("{:x}", hash))
} }
self.file_hash.clone().unwrap() self.file_hash.clone().unwrap()
} }
fn create_blob_ref(&mut self, file_name: String, ts_remote: &str) -> io::Result<()> { /// read line of blob to get all informations and store them in self.data
let metadata = fs::metadata(self.a_path.clone())?; 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::<Vec<_>>();
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::<u64>().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<String> {
// 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<String> = 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<PathBuf>) -> 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 let secs = metadata
.modified() .modified()
.unwrap() .unwrap()
@ -81,30 +207,28 @@ impl Blob {
.unwrap() .unwrap()
.as_secs(); .as_secs();
let mut content = file_name.clone(); // build line with all needed properties
content.push_str(" "); let content = format!("{} {} {} {} {}",
content.push_str(ts_remote); self.get_name(),
content.push_str(" "); ts_remote,
content.push_str(&metadata.len().to_string()); metadata.len().to_string(),
content.push_str(" "); secs.to_string(),
content.push_str(&secs.to_string()); self.get_file_hash());
content.push_str(" ");
content.push_str(&self.get_file_hash());
let binding = self.obj_p.clone(); // create parent dir if needed
let child = binding.file_name(); let mut obj_path = self.get_obj_path();
self.obj_p.pop(); obj_path.pop();
if !self.obj_p.clone().exists() { if !obj_path.exists() {
fs::create_dir_all(self.obj_p.clone())?; fs::create_dir_all(obj_path)?;
} }
self.obj_p.push(child.unwrap().to_str().unwrap());
// open ref file
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.create_new(true) .create_new(true)
.write(true) .write(true)
.open(self.obj_p.clone())?; .open(self.get_obj_path())?;
writeln!(file, "{}", &content)?; writeln!(file, "{}", content)?;
Ok(()) Ok(())
} }
@ -134,261 +258,101 @@ impl Blob {
.open(refs_p)?; .open(refs_p)?;
// todo deal with duplicate content // 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(()) Ok(())
} }
pub fn get_all_identical_blobs(&mut self) -> Vec<String> { // build line for parent reference
// an empty file is a new file not the copy of another empty file fn get_line(&mut self) -> String {
if self.get_file_hash() == HASH_EMPTY { format!("blob {} {}", self.get_hash_path(), self.get_name())
return vec![];
}
let refs_p = self.get_file_ref();
let mut blobs: Vec<String> = 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 create(&mut self, ts_remote: &str, up_parent: bool) -> io::Result<()> { 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 // 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)?; head::add_line(line)?;
} else { } 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()) { if let Err(err) = self.create_blob_ref(ts_remote.clone()) {
eprintln!("err: saving blob ref of {}: {}", self.r_path.clone().display(), err); eprintln!("err: saving blob ref of {}: {}", self.get_relative_file_path().display(), err);
} }
if let Err(err) = self.create_hash_ref() { 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 // update date for all parent
if up_parent { if up_parent {
if let Err(err) = update_dates(self.r_path.clone(), ts_remote) { if let Err(err) = update_dates(self.get_relative_file_path(), ts_remote) {
eprintln!("err: updating parent date of {}: {}", self.r_path.clone().display(), err); eprintln!("err: updating parent date of {}: {}", self.get_relative_file_path().display(), err);
} }
} }
Ok(()) Ok(())
} }
/// remove object
pub fn rm(&mut self) -> io::Result<()> { pub fn rm(&mut self) -> io::Result<()> {
let (line, _) = self.get_line_filename(); let line = self.get_line();
// remove blob reference to parent // 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)?; head::rm_line(&line)?;
} else { } else {
rm_node(self.r_path.parent().unwrap(), &line)?; rm_node(self.get_relative_file_path().parent().unwrap(), &line)?;
} }
// remove blob object // remove blob object
fs::remove_file(self.obj_p.clone())?; fs::remove_file(self.get_obj_path())?;
Ok(()) Ok(())
} }
pub fn update(&mut self, ts_remote: &str) -> io::Result<()> { pub fn update(&mut self, ts_remote: &str) -> io::Result<()> {
// remove old hash ref // // remove old hash ref
let mut refs_p = path::refs(); // let mut refs_p = path::refs();
let binding = self.saved_hash(); // let binding = self.saved_hash();
let (dir, res) = binding.split_at(2); // let (dir, res) = binding.split_at(2);
refs_p.push(dir); // refs_p.push(dir);
refs_p.push(res); // refs_p.push(res);
if let Err(err) = fs::remove_file(refs_p) { // if let Err(err) = fs::remove_file(refs_p) {
eprintln!("err: removing hash ref of {}: {}", self.r_path.clone().display(), err); // eprintln!("err: removing hash ref of {}: {}", self.r_path.clone().display(), err);
} // }
//
// creating new hash ref // // creating new hash ref
if let Err(err) = self.create_hash_ref() { // 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.r_path.clone().display(), err);
} // }
//
// updating content of blob's ref // // updating content of blob's ref
let metadata = fs::metadata(self.a_path.clone())?; // let metadata = fs::metadata(self.a_path.clone())?;
let secs = metadata // let secs = metadata
.modified() // .modified()
.unwrap() // .unwrap()
.duration_since(SystemTime::UNIX_EPOCH) // .duration_since(SystemTime::UNIX_EPOCH)
.unwrap() // .unwrap()
.as_secs(); // .as_secs();
//
let mut content = self.saved_filename(); // let mut content = self.saved_filename();
content.push_str(" "); // content.push_str(" ");
content.push_str(ts_remote); // content.push_str(ts_remote);
content.push_str(" "); // content.push_str(" ");
content.push_str(&metadata.len().to_string()); // content.push_str(&metadata.len().to_string());
content.push_str(" "); // content.push_str(" ");
content.push_str(&secs.to_string()); // content.push_str(&secs.to_string());
content.push_str(" "); // content.push_str(" ");
content.push_str(&self.get_file_hash()); // content.push_str(&self.get_file_hash());
//
let mut file = OpenOptions::new() // let mut file = OpenOptions::new()
.write(true) // .write(true)
.open(self.obj_p.clone())?; // .open(self.obj_p.clone())?;
//
writeln!(file, "{}", &content)?; // writeln!(file, "{}", &content)?;
Ok(()) 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::<Vec<_>>();
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::<u64>().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<PathBuf>) -> 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
}
}
} }

207
src/store/object/object.rs Normal file
View File

@ -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<PathBuf>,
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<dyn FnOnce(Self)> {
// match kind {
// "Blob" => Box::new(|_| Blob::new(self.clone())),
// "Tree" => Box::new(|_| Tree::new(self.clone())),
// _ => unreachable!(),
// }
//}
}

View File

@ -1,106 +1,161 @@
use std::fs::File; use crate::store::object::object::Obj;
use std::io;
use std::path::PathBuf; use std::path::PathBuf;
use crate::utils::path::path_buf_to_string; use crate::store::object::blob::Blob;
use crate::utils::{read, path}; use std::iter::{Iter, Empty};
use crate::store::head; use crate::store::object::object::ObjMethods;
use crate::store::object::{self, update_dates, parse_path, hash_obj, add_node, create_obj}; 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<Lines<BufReader<File>>>,
pub lines_iter: Option<Iter<String, Lines<BufReader<File>>>>,
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(); impl Tree {
for line in lines {
let (ftype, hash, _) = parse_line(line.unwrap()); pub fn from_head() -> Self {
if ftype == String::from("blob") { Tree {
object::rm(&hash)?; obj: Obj::from_head(),
} else { lines: None,
rm_hash(hash)?; lines_iter: None
} }
} }
Ok(())
}
fn rm_hash(hash: String) -> io::Result<()> { pub fn from_path(path: PathBuf) -> Self {
let mut obj_p = path::objects(); Tree {
let (dir, res) = hash.split_at(2); obj: Obj::from_path(path),
obj_p.push(dir); lines: None,
obj_p.push(res); lines_iter: None
}
}
match read::read_lines(obj_p) { pub fn read_data(&mut self) {
Ok(mut reader) => { if self.lines.is_none() {
reader.next(); if let Ok(mut file) = File::open(self.get_obj_path()) {
for line in reader { self.lines = Some(BufReader::new(file).lines());
let (ftype, hash, _) = parse_line(line.unwrap()); self.lines_iter = Some(self.lines.iter());
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<io::BufReader<File>>)> {
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(); fn get_iter(&mut self) -> Iter<String, Lines<BufReader<File>>> {
let hash = split.next().unwrap(); if let Some(iter) = self.lines_iter {
let ftype = split.next().unwrap(); iter
(String::from(ftype), String::from(hash), String::from(name)) }
std::process::exit(4);
}
pub fn next(&mut self) -> Option<Obj> {
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<io::BufReader<File>>)> {
// 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))
//}

View File

@ -208,7 +208,7 @@ fn get_non_new_local_element(iter: &mut dyn Iterator<Item = &PathBuf>) -> Option
!Object::new(el.unwrap().clone().to_str().unwrap()).exists() !Object::new(el.unwrap().clone().to_str().unwrap()).exists()
} else { } else {
// ignore newly created file (not sync) // 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(); el = iter.next();

View File

@ -1,5 +1,4 @@
use rand::{distributions::Alphanumeric, Rng}; use rand::{distributions::Alphanumeric, Rng};
use std::env;
mod utils; mod utils;
use utils::{server::ServerTest, client::ClientTest}; use utils::{server::ServerTest, client::ClientTest};
@ -61,4 +60,46 @@ mod push_tests {
client.clean(); client.clean();
server.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();
}
} }

View File

@ -23,8 +23,6 @@ impl ClientTest {
let mut exe_path = env::current_dir().unwrap(); let mut exe_path = env::current_dir().unwrap();
exe_path = exe_path.join("target/debug/nextsync"); exe_path = exe_path.join("target/debug/nextsync");
// let _ = env::set_current_dir(vol.clone());
// build the client // build the client
ClientTest { ClientTest {
user: String::from("admin"), 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); 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 self
} }
@ -80,6 +73,14 @@ impl ClientTest {
return output; 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<()> { pub fn add_file(&mut self, name: &str, content: &str) -> std::io::Result<()> {
let mut path = self.volume.clone(); let mut path = self.volume.clone();
path.push_str("/"); path.push_str("/");

View File

@ -93,7 +93,7 @@ impl ServerTest {
let full_path = self.volume.clone().join(file); let full_path = self.volume.clone().join(file);
if !full_path.exists() { if !full_path.exists() {
eprintln!("File '{}' does't exists on the server", file); eprintln!("File '{}' doesn't exists on the server", file);
return false; return false;
} }