refactor(blob): use object trait to create blob
This commit is contained in:
parent
642c358737
commit
faf7341525
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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())?;
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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
207
src/store/object/object.rs
Normal 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!(),
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
//}
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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("/");
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user