Compare commits
No commits in common. "06bb51476b1177b6910e1b6f51e3dccc5ecf79d0" and "211e3702a38365e759f399f8f5435a6b4d88b384" have entirely different histories.
06bb51476b
...
211e3702a3
@ -12,4 +12,4 @@ timestamp2: timestamp of file locally to know when the file has changed on the s
|
||||
folder_name timestamp
|
||||
tree hash_path folder_name
|
||||
blob hash_path file_name
|
||||
```
|
||||
```
|
@ -12,7 +12,7 @@ use crate::global::global::{DIR_PATH, set_dir_path};
|
||||
use crate::services::api::ApiError;
|
||||
use crate::services::api_call::ApiCall;
|
||||
use crate::services::req_props::{ReqProps, ObjProps};
|
||||
use crate::store::object::{tree::Tree, blob::Blob};
|
||||
use crate::store::object::{tree, blob::Blob};
|
||||
use crate::commands::config;
|
||||
use crate::commands::init;
|
||||
|
||||
@ -91,7 +91,7 @@ pub fn clone(args: CloneArgs) {
|
||||
// add tree
|
||||
let path_folder = p.strip_prefix(ref_path.clone()).unwrap();
|
||||
let lastmodified = folder.lastmodified.unwrap().timestamp_millis();
|
||||
if let Err(err) = Tree::from_path(path_folder.to_path_buf()).create(&lastmodified.to_string(), false) {
|
||||
if let Err(err) = tree::add(path_folder.to_path_buf(), &lastmodified.to_string(), false) {
|
||||
eprintln!("err: saving ref of {} ({})", path_folder.display(), err);
|
||||
}
|
||||
}
|
||||
@ -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::from_path(relative_p).create(&lastmodified.to_string(), false) {
|
||||
if let Err(err) = Blob::new(relative_p).create(&lastmodified.to_string(), false) {
|
||||
eprintln!("err: saving ref of {} ({})", relative_s.clone(), err);
|
||||
}
|
||||
}
|
||||
|
@ -30,13 +30,14 @@ pub fn config_set(args: ConfigSetArgs) {
|
||||
|
||||
|
||||
pub fn find_option_in_cat(category: &str, option: &str) -> Option<String> {
|
||||
let mut config = path::nextsync();
|
||||
config.push("config");
|
||||
let mut root = path::nextsync();
|
||||
root.push("config");
|
||||
|
||||
let mut in_target_category = false;
|
||||
if let Ok(mut lines) = read::read_lines(config) {
|
||||
if let Ok(mut lines) = read::read_lines(root) {
|
||||
|
||||
for line in lines {
|
||||
|
||||
if let Ok(line) = line {
|
||||
let trimmed_line = line.trim();
|
||||
|
||||
@ -57,14 +58,14 @@ pub fn find_option_in_cat(category: &str, option: &str) -> Option<String> {
|
||||
}
|
||||
|
||||
pub fn write_option_in_cat(category: &str, option: &str, value: &str) -> io::Result<()> {
|
||||
let mut config = path::nextsync();
|
||||
config.push("config");
|
||||
let mut root = path::nextsync();
|
||||
root.push("config");
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&config)?;
|
||||
.open(&root)?;
|
||||
|
||||
let mut in_target_category = false;
|
||||
let mut option_found = false;
|
||||
@ -128,7 +129,8 @@ pub fn write_option_in_cat(category: &str, option: &str, value: &str) -> io::Res
|
||||
}
|
||||
|
||||
pub fn add_remote(name: &str, url: &str) -> io::Result<()> {
|
||||
let mut config = path::config();
|
||||
let mut root = path::nextsync();
|
||||
root.push("config");
|
||||
|
||||
// check if there is already a remote with this name
|
||||
if get_remote(name).is_some()
|
||||
@ -142,7 +144,7 @@ pub fn add_remote(name: &str, url: &str) -> io::Result<()> {
|
||||
.write(true)
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(config)?;
|
||||
.open(root)?;
|
||||
|
||||
writeln!(file, "[remote \"{}\"]", name)?;
|
||||
writeln!(file, "\turl = {}", url)?;
|
||||
@ -154,55 +156,20 @@ pub fn get_remote(name: &str) -> Option<String> {
|
||||
find_option_in_cat(&format!("remote \"{}\"", name), "url")
|
||||
}
|
||||
|
||||
/// return a vector of remote found in config file (e.g: ("origin", "https://example.com"))
|
||||
pub fn get_all_remote() -> Vec<(String, String)> {
|
||||
let config = path::config();
|
||||
|
||||
let mut remotes: Vec<(String, String)> = vec![];
|
||||
|
||||
let mut in_remote = false;
|
||||
let mut remote_name = String::new();
|
||||
if let Ok(lines) = read::read_lines(config) {
|
||||
|
||||
for line in lines {
|
||||
if let Ok(line) = line {
|
||||
let trimmed_line = line.trim();
|
||||
|
||||
if trimmed_line.starts_with("[remote ") {
|
||||
in_remote = true;
|
||||
remote_name = trimmed_line.strip_prefix("[remote \"").unwrap().strip_suffix("\"]").unwrap().to_string();
|
||||
}
|
||||
else if trimmed_line.starts_with('[')
|
||||
{
|
||||
in_remote = false;
|
||||
}
|
||||
else if in_remote {
|
||||
let parts: Vec<&str> = trimmed_line.splitn(2, '=').collect();
|
||||
if parts.len() == 2 {
|
||||
remotes.push((remote_name.to_string(), parts[1].trim().to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
remotes
|
||||
}
|
||||
|
||||
pub fn get_core(name: &str) -> Option<String> {
|
||||
find_option_in_cat("core", name)
|
||||
}
|
||||
|
||||
pub fn add_core(name: &str, value: &str) -> io::Result<()> {
|
||||
let mut config = path::nextsync();
|
||||
config.push("config");
|
||||
let mut root = path::nextsync();
|
||||
root.push("config");
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(config)?;
|
||||
.open(root)?;
|
||||
|
||||
writeln!(file, "[core]")?;
|
||||
writeln!(file, "\t{} = {}", name, value)?;
|
||||
@ -211,9 +178,10 @@ pub fn add_core(name: &str, value: &str) -> io::Result<()> {
|
||||
}
|
||||
|
||||
pub fn get(var: &str) -> Option<String> {
|
||||
let mut config = path::config();
|
||||
let mut root = path::nextsync();
|
||||
root.push("config");
|
||||
|
||||
if let Ok(lines) = read::read_lines(config) {
|
||||
if let Ok(lines) = read::read_lines(root) {
|
||||
for line in lines {
|
||||
if let Ok(l) = line {
|
||||
if l.starts_with(var) {
|
||||
|
@ -4,7 +4,7 @@ use std::fs::DirBuilder;
|
||||
use crate::services::downloader::Downloader;
|
||||
use crate::services::req_props::ObjProps;
|
||||
use crate::store::object::blob::Blob;
|
||||
use crate::store::object::tree::Tree;
|
||||
use crate::store::object::tree;
|
||||
use crate::utils::api::get_api_props;
|
||||
use crate::utils::path;
|
||||
use crate::commands::remote_diff::get_diff;
|
||||
@ -29,7 +29,7 @@ pub fn pull() {
|
||||
// add tree
|
||||
let path_folder = p.strip_prefix(ref_p.clone()).unwrap();
|
||||
let lastmodified = folder.lastmodified.unwrap().timestamp_millis();
|
||||
if let Err(err) = Tree::from_path(path_folder.clone()).create(&lastmodified.to_string(), false) {
|
||||
if let Err(err) = tree::add(path_folder.to_path_buf(), &lastmodified.to_string(), false) {
|
||||
eprintln!("err: saving ref of {} ({})", path_folder.display(), err);
|
||||
}
|
||||
}
|
||||
@ -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::from_path(relative_p).create(&lastmodified.to_string(), false) {
|
||||
if let Err(err) = Blob::new(relative_p).create(&lastmodified.to_string(), false) {
|
||||
eprintln!("err: saving ref of {} ({})", relative_s.clone(), err);
|
||||
}
|
||||
}
|
||||
|
@ -40,11 +40,9 @@ pub fn push() {
|
||||
let mut whitelist: Option<PathBuf> = None;
|
||||
|
||||
for obj in staged_objs {
|
||||
dbg!(obj.clone());
|
||||
if obj.otype == String::from("tree") {
|
||||
let push_factory = PushFactory.new_dir(obj.clone());
|
||||
let res = push_factory.can_push(&mut whitelist);
|
||||
dbg!(&res);
|
||||
match res {
|
||||
PushState::Valid => {
|
||||
match push_factory.push() {
|
||||
|
@ -70,7 +70,7 @@ impl PushChange for Copied {
|
||||
let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
|
||||
|
||||
// create destination blob
|
||||
if let Err(err) = Blob::from_path(obj.path.clone()).create(&lastmodified.to_string(), false) {
|
||||
if let Err(err) = Blob::new(obj.path.clone()).create(&lastmodified.to_string(), false) {
|
||||
eprintln!("err: creating ref of {}: {}", obj.name.clone(), err);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ use crate::store::index;
|
||||
use crate::store::object::blob::Blob;
|
||||
use crate::commands::status::LocalObj;
|
||||
use crate::commands::push::push_factory::{PushState, PushChange, PushFlowState};
|
||||
use crate::store::object::object::ObjMethods;
|
||||
|
||||
pub struct Deleted {
|
||||
pub obj: LocalObj
|
||||
@ -44,7 +43,7 @@ impl PushChange for Deleted {
|
||||
|
||||
// update tree
|
||||
// todo date
|
||||
Blob::from_path(obj.path.clone()).rm()?;
|
||||
Blob::new(obj.path.clone()).rm()?;
|
||||
|
||||
// remove index
|
||||
index::rm_line(obj.path.to_str().unwrap())?;
|
||||
|
@ -68,7 +68,7 @@ impl PushChange for Modified {
|
||||
let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
|
||||
|
||||
// update blob
|
||||
Blob::from_path(obj.path.clone()).update(&lastmodified.to_string())?;
|
||||
Blob::new(obj.path.clone()).update(&lastmodified.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ use crate::commands::status::LocalObj;
|
||||
use crate::commands::push::push_factory::{PushState, PushChange, PushFlowState};
|
||||
use crate::store::object::blob::Blob;
|
||||
use crate::utils::path::path_buf_to_string;
|
||||
use crate::store::object::object::ObjMethods;
|
||||
|
||||
pub struct Moved {
|
||||
pub obj: LocalObj,
|
||||
@ -71,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::from_path(obj.path.clone()).create(&lastmodified.to_string(), false) {
|
||||
if let Err(err) = Blob::new(obj.path.clone()).create(&lastmodified.to_string(), false) {
|
||||
eprintln!("err: creating ref of {}: {}", obj.name.clone(), err);
|
||||
}
|
||||
if let Err(err) = Blob::from_path(obj.path_from.clone().unwrap()).rm() {
|
||||
if let Err(err) = Blob::new(obj.path_from.clone().unwrap()).rm() {
|
||||
eprintln!("err: removing ref of {}: {}", obj.name.clone(), err);
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ impl PushChange for New {
|
||||
let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
|
||||
|
||||
// create new blob
|
||||
Blob::from_path(obj.path.clone()).create(&lastmodified.to_string(), false)?;
|
||||
Blob::new(obj.path.clone()).create(&lastmodified.to_string(), false)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use crate::services::api_call::ApiCall;
|
||||
use crate::services::req_props::ReqProps;
|
||||
use crate::services::create_folder::CreateFolder;
|
||||
use crate::store::index;
|
||||
use crate::store::object::tree::Tree;
|
||||
use crate::store::object::tree;
|
||||
use crate::commands::status::LocalObj;
|
||||
use crate::commands::push::push_factory::{PushState, PushChange, PushFlowState};
|
||||
|
||||
@ -75,7 +75,7 @@ impl PushChange for NewDir {
|
||||
let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
|
||||
|
||||
// update tree
|
||||
Tree::from_path(obj.path.clone()).create(&lastmodified.to_string(), true)?;
|
||||
tree::add(obj.path.clone(), &lastmodified.to_string(), true)?;
|
||||
|
||||
// remove index
|
||||
index::rm_line(obj.path.to_str().unwrap())?;
|
||||
|
@ -1,4 +1,3 @@
|
||||
use std::error::Error;
|
||||
use std::path::PathBuf;
|
||||
use std::io;
|
||||
use crate::commands::status::{State, LocalObj};
|
||||
@ -74,7 +73,7 @@ pub trait PushChange {
|
||||
};
|
||||
|
||||
// check if remote is newest
|
||||
let last_sync_ts = Blob::from_path(obj.path.clone())
|
||||
let last_sync_ts = Blob::new(obj.path.clone())
|
||||
.saved_remote_ts()
|
||||
.parse::<i64>().unwrap();
|
||||
let remote_ts = obj_data.lastmodified.unwrap().timestamp_millis();
|
||||
|
@ -4,10 +4,9 @@ use crate::services::api::ApiError;
|
||||
use crate::services::api_call::ApiCall;
|
||||
use crate::services::delete_path::DeletePath;
|
||||
use crate::store::index;
|
||||
use crate::store::object::tree::Tree;
|
||||
use crate::store::object::tree;
|
||||
use crate::commands::status::LocalObj;
|
||||
use crate::commands::push::push_factory::{PushState, PushChange, PushFlowState};
|
||||
use crate::store::object::object::ObjMethods;
|
||||
|
||||
pub struct RmDir {
|
||||
pub obj: LocalObj
|
||||
@ -50,7 +49,7 @@ impl PushChange for RmDir {
|
||||
|
||||
// update tree
|
||||
// todo update date
|
||||
Tree::from_path(obj.path.clone()).rm()?;
|
||||
tree::rm(obj.path.clone())?;
|
||||
|
||||
// remove index
|
||||
index::rm_line(obj.path.to_str().unwrap())?;
|
||||
|
@ -2,8 +2,6 @@ use clap::Values;
|
||||
|
||||
use crate::commands::config;
|
||||
|
||||
use super::config::get_all_remote;
|
||||
|
||||
pub struct RemoteArgs<'a> {
|
||||
pub name: Option<Values<'a>>,
|
||||
pub url: Option<Values<'a>>,
|
||||
@ -21,17 +19,3 @@ pub fn remote_add(args: RemoteArgs) {
|
||||
let _ = config::add_remote(name, url);
|
||||
}
|
||||
|
||||
pub fn remote_list(verbose: bool) {
|
||||
let remotes = get_all_remote();
|
||||
for remote in remotes {
|
||||
if verbose
|
||||
{
|
||||
println!("{} {}", remote.0, remote.1);
|
||||
}
|
||||
else
|
||||
{
|
||||
println!("{}", remote.0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,16 @@
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
use std::io::{Lines, BufReader};
|
||||
use std::collections::HashMap;
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha1::Sha1;
|
||||
use colored::Colorize;
|
||||
use crate::utils::path::{self, path_buf_to_string};
|
||||
use crate::store::head;
|
||||
use crate::store::object::blob::Blob;
|
||||
use crate::store::object::object::Obj;
|
||||
use crate::store::object::tree::Tree;
|
||||
use crate::utils::read::read_folder;
|
||||
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,
|
||||
@ -96,7 +97,7 @@ fn should_retain(hasher: &mut Sha1, key: String, obj: LocalObj, move_copy_hashes
|
||||
{
|
||||
return true;
|
||||
}
|
||||
let mut blob = Blob::from_path(obj.path.clone());
|
||||
let mut blob = Blob::new(obj.path.clone());
|
||||
let mut flag = true;
|
||||
let identical_blobs = blob.get_all_identical_blobs();
|
||||
|
||||
@ -160,15 +161,20 @@ pub struct LocalObj {
|
||||
}
|
||||
|
||||
pub fn get_all_staged() -> Vec<LocalObj> {
|
||||
let mut staged_objs = vec![];
|
||||
let mut lines: Vec<String> = vec![];
|
||||
|
||||
if let Ok(entries) = index::read_line() {
|
||||
for line in entries {
|
||||
for entry in entries {
|
||||
lines.push(entry.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
let obj = Obj::from_path(line.unwrap()).get_local_obj();
|
||||
if obj.state != State::Default {
|
||||
staged_objs.push(obj);
|
||||
}
|
||||
let mut staged_objs = vec![];
|
||||
|
||||
for line in lines {
|
||||
let obj = Blob::new(line).get_local_obj();
|
||||
if obj.state != State::Default {
|
||||
staged_objs.push(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,27 +227,20 @@ fn get_staged(hashes: &mut HashMap<String, LocalObj>) -> Vec<LocalObj> {
|
||||
staged_objs
|
||||
}
|
||||
|
||||
fn read_tree_to_hashmap(tree: &mut Tree, hashes: &mut HashMap<String, LocalObj>, path: PathBuf) {
|
||||
while let Some(child) = tree.next() {
|
||||
hashes.insert(String::from(child.get_hash_path()), child.get_local_obj());
|
||||
};
|
||||
}
|
||||
|
||||
fn get_diff() -> (HashMap<String, LocalObj>, HashMap<String, LocalObj>, Vec<String>) {
|
||||
let mut hashes = HashMap::new();
|
||||
let mut objs: Vec<String> = vec![];
|
||||
let mut objs_modified: Vec<String> = vec![];
|
||||
|
||||
let root = path::repo_root();
|
||||
|
||||
|
||||
let current_p = path::current().unwrap();
|
||||
// todo use repo_root instead of current
|
||||
let dist_path = current_p.strip_prefix(root.clone()).unwrap().to_path_buf();
|
||||
|
||||
read_tree_to_hashmap(&mut Tree::from_head(), &mut hashes, dist_path.clone());
|
||||
//if let Ok(lines) = read_lines(head::path()) {
|
||||
// add_to_hashmap(lines, &mut hashes, dist_path.clone());
|
||||
//}
|
||||
|
||||
if let Ok(lines) = read_lines(head::path()) {
|
||||
add_to_hashmap(lines, &mut hashes, dist_path.clone());
|
||||
}
|
||||
|
||||
if let Ok(entries) = read_folder(root.clone()) {
|
||||
add_to_vec(entries, &mut objs, root.clone());
|
||||
@ -256,27 +255,22 @@ fn get_diff() -> (HashMap<String, LocalObj>, HashMap<String, LocalObj>, Vec<Stri
|
||||
let obj_path = root.clone().join(cur_path.clone());
|
||||
|
||||
if obj_path.is_dir() {
|
||||
// read virtual tree
|
||||
read_tree_to_hashmap(&mut Tree::from_path(cur_obj.clone()), &mut hashes, dist_path.clone());
|
||||
//let mut tree = Tree::from_path(cur_obj.clone());
|
||||
//if let Some(lines) = tree.get_children() {
|
||||
//add_to_hashmap(lines, &mut hashes, cur_path.clone());
|
||||
//}
|
||||
if let Some((_, lines)) = tree::read(cur_obj.clone()) {
|
||||
add_to_hashmap(lines, &mut hashes, cur_path.clone());
|
||||
}
|
||||
|
||||
// read physical tree
|
||||
if let Ok(entries) = read_folder(obj_path.clone()) {
|
||||
add_to_vec(entries, &mut objs, root.clone());
|
||||
}
|
||||
|
||||
// remove duplicate
|
||||
let diff = remove_duplicate(&mut hashes, &mut objs, RemoveSide::Both);
|
||||
obj_to_analyse.append(&mut diff.clone());
|
||||
} else {
|
||||
if Blob::from_path(cur_path).has_changes() {
|
||||
if Blob::new(cur_path).has_change() {
|
||||
objs_modified.push(cur_obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
for (_, elt) in &mut hashes {
|
||||
@ -314,24 +308,24 @@ fn get_otype(p: PathBuf) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
//fn add_to_hashmap(lines: Lines<BufReader<File>>, hashes: &mut HashMap<String, LocalObj>, path: PathBuf) {
|
||||
// for line in lines {
|
||||
// if let Ok(ip) = line {
|
||||
// if ip.clone().len() > 5 {
|
||||
// let (ftype, hash, name) = tree::parse_line(ip);
|
||||
// let mut p = path.clone();
|
||||
// p.push(name.clone());
|
||||
// hashes.insert(String::from(hash), LocalObj{
|
||||
// otype: String::from(ftype),
|
||||
// name: String::from(name),
|
||||
// path: p,
|
||||
// path_from: None,
|
||||
// state: State::Default,
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
fn add_to_hashmap(lines: Lines<BufReader<File>>, hashes: &mut HashMap<String, LocalObj>, path: PathBuf) {
|
||||
for line in lines {
|
||||
if let Ok(ip) = line {
|
||||
if ip.clone().len() > 5 {
|
||||
let (ftype, hash, name) = tree::parse_line(ip);
|
||||
let mut p = path.clone();
|
||||
p.push(name.clone());
|
||||
hashes.insert(String::from(hash), LocalObj{
|
||||
otype: String::from(ftype),
|
||||
name: String::from(name),
|
||||
path: p,
|
||||
path_from: None,
|
||||
state: State::Default,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_to_vec(entries: Vec<PathBuf>, objects: &mut Vec<String>, root: PathBuf) {
|
||||
for entry in entries {
|
||||
|
@ -1,10 +1,9 @@
|
||||
use std::error::Error;
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::Mutex;
|
||||
use reqwest::Client;
|
||||
use reqwest::RequestBuilder;
|
||||
use reqwest::multipart::Form;
|
||||
use reqwest::{Response, Method};
|
||||
use reqwest::{Response, Error, Method};
|
||||
use reqwest::header::{HeaderValue, CONTENT_TYPE, HeaderMap, IntoHeaderName};
|
||||
use crate::utils::api::ApiProps;
|
||||
use crate::commands::config;
|
||||
@ -185,7 +184,7 @@ impl ApiBuilder {
|
||||
self.set_request_manager();
|
||||
}
|
||||
|
||||
let res_req = tokio::runtime::Runtime::new().unwrap().block_on(async {
|
||||
let res = tokio::runtime::Runtime::new().unwrap().block_on(async {
|
||||
match self.request.take() {
|
||||
None => {
|
||||
eprintln!("fatal: incorrect request");
|
||||
@ -200,16 +199,7 @@ impl ApiBuilder {
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
// handle request error
|
||||
let res = match res_req {
|
||||
Err(err) => {
|
||||
eprintln!("fatal: {}", err.source().unwrap());
|
||||
std::process::exit(1);
|
||||
},
|
||||
Ok(res) => res,
|
||||
};
|
||||
}).map_err(ApiError::RequestError)?;
|
||||
|
||||
if res.status().is_success() {
|
||||
if need_text {
|
||||
@ -225,7 +215,7 @@ impl ApiBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn old_send(&mut self) -> Result<Response, reqwest::Error> {
|
||||
pub async fn old_send(&mut self) -> Result<Response, Error> {
|
||||
let mut request_manager = get_request_manager().lock().unwrap();
|
||||
let request_manager = request_manager.as_mut().unwrap();
|
||||
if !self.host.is_none()
|
||||
@ -247,9 +237,9 @@ impl ApiBuilder {
|
||||
Some(req) => {
|
||||
if let Some(headers) = &self.headers {
|
||||
req.headers(headers.clone())
|
||||
.send().await.map_err(reqwest::Error::from)
|
||||
.send().await.map_err(Error::from)
|
||||
} else {
|
||||
req.send().await.map_err(reqwest::Error::from)
|
||||
req.send().await.map_err(Error::from)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ use crate::utils::{read, path};
|
||||
|
||||
pub mod tree;
|
||||
pub mod blob;
|
||||
pub mod object;
|
||||
|
||||
pub struct Object {
|
||||
path: PathBuf,
|
||||
|
@ -4,202 +4,76 @@ use std::io::Write;
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
use crate::commands::status::{State};
|
||||
use crypto::sha1::Sha1;
|
||||
use crypto::digest::Digest;
|
||||
use crate::commands::status::{LocalObj, State};
|
||||
use crate::utils::into::IntoPathBuf;
|
||||
use crate::utils::path::path_buf_to_string;
|
||||
use crate::utils::{path, read};
|
||||
use crate::store::head;
|
||||
use crate::store::object::{update_dates, add_node, rm_node};
|
||||
|
||||
use crate::store::object::object::ObjMethods;
|
||||
use crate::store::object::object::Obj;
|
||||
|
||||
const HASH_EMPTY: &str = "d41d8cd98f00b204e9800998ecf8427e";
|
||||
|
||||
pub struct Blob {
|
||||
pub obj: Obj,
|
||||
data: Vec<String>, // content of the ref file
|
||||
file_hash: Option<String>, // hash of the file's content
|
||||
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
|
||||
}
|
||||
|
||||
//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 {
|
||||
pub fn new(obj: Obj) -> Self {
|
||||
Self {
|
||||
obj,
|
||||
data: vec![],
|
||||
file_hash: None,
|
||||
}
|
||||
}
|
||||
pub fn from_path<S>(r_path: S) -> Blob where S: IntoPathBuf {
|
||||
pub fn new<S>(r_path: S) -> Blob where S: IntoPathBuf {
|
||||
let r_path = r_path.into();
|
||||
Self {
|
||||
obj: Obj::from_path(r_path),
|
||||
data: vec![],
|
||||
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,
|
||||
data: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn get_file_hash(&mut self) -> String {
|
||||
if self.file_hash.is_none() {
|
||||
let bytes = std::fs::read(self.get_file_path()).unwrap();
|
||||
let bytes = std::fs::read(self.a_path.clone()).unwrap();
|
||||
let hash = md5::compute(&bytes);
|
||||
self.file_hash = Some(format!("{:x}", hash))
|
||||
}
|
||||
self.file_hash.clone().unwrap()
|
||||
}
|
||||
|
||||
/// 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::<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())?;
|
||||
fn create_blob_ref(&mut self, file_name: String, ts_remote: &str) -> io::Result<()> {
|
||||
let metadata = fs::metadata(self.a_path.clone())?;
|
||||
let secs = metadata
|
||||
.modified()
|
||||
.unwrap()
|
||||
@ -207,28 +81,30 @@ impl Blob {
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
// 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 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());
|
||||
|
||||
// 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)?;
|
||||
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())?;
|
||||
}
|
||||
self.obj_p.push(child.unwrap().to_str().unwrap());
|
||||
|
||||
// open ref file
|
||||
let mut file = OpenOptions::new()
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.open(self.get_obj_path())?;
|
||||
.open(self.obj_p.clone())?;
|
||||
|
||||
writeln!(file, "{}", content)?;
|
||||
writeln!(file, "{}", &content)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -258,74 +134,261 @@ impl Blob {
|
||||
.open(refs_p)?;
|
||||
|
||||
// todo deal with duplicate content
|
||||
writeln!(file, "{}", self.get_relative_file_path().to_str().unwrap())?;
|
||||
writeln!(file, "{}", self.r_path.clone().to_str().unwrap())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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_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<()> {
|
||||
let (line, file_name) = self.get_line_filename();
|
||||
|
||||
// add blob reference to parent
|
||||
self.add_ref_to_parent();
|
||||
if self.r_path.iter().count() == 1 {
|
||||
head::add_line(line)?;
|
||||
} else {
|
||||
add_node(self.r_path.parent().unwrap(), &line)?;
|
||||
}
|
||||
|
||||
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_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_hash_ref() {
|
||||
eprintln!("err: saving hash ref of {}: {}", self.get_relative_file_path().display(), err);
|
||||
eprintln!("err: saving hash ref of {}: {}", self.r_path.clone().display(), err);
|
||||
}
|
||||
|
||||
// update date for all parent
|
||||
if up_parent {
|
||||
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);
|
||||
if let Err(err) = update_dates(self.r_path.clone(), ts_remote) {
|
||||
eprintln!("err: updating parent date of {}: {}", self.r_path.clone().display(), err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update(&mut self, ts_remote: &str) -> io::Result<()> {
|
||||
pub fn rm(&mut self) -> io::Result<()> {
|
||||
let (line, _) = self.get_line_filename();
|
||||
|
||||
// remove blob reference to parent
|
||||
if self.r_path.iter().count() == 1 {
|
||||
head::rm_line(&line)?;
|
||||
} else {
|
||||
rm_node(self.r_path.parent().unwrap(), &line)?;
|
||||
}
|
||||
|
||||
// remove blob object
|
||||
fs::remove_file(self.obj_p.clone())?;
|
||||
|
||||
// // 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 update(&mut self, ts_remote: &str) -> io::Result<()> {
|
||||
|
||||
// remove old hash ref
|
||||
let mut refs_p = path::refs();
|
||||
let binding = self.saved_hash();
|
||||
let (dir, res) = binding.split_at(2);
|
||||
refs_p.push(dir);
|
||||
refs_p.push(res);
|
||||
if let Err(err) = fs::remove_file(refs_p) {
|
||||
eprintln!("err: removing hash ref of {}: {}", self.r_path.clone().display(), err);
|
||||
}
|
||||
|
||||
// creating new hash ref
|
||||
if let Err(err) = self.create_hash_ref() {
|
||||
eprintln!("err: saving hash ref of {}: {}", self.r_path.clone().display(), err);
|
||||
}
|
||||
|
||||
// updating content of blob's ref
|
||||
let metadata = fs::metadata(self.a_path.clone())?;
|
||||
let secs = metadata
|
||||
.modified()
|
||||
.unwrap()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
let mut content = self.saved_filename();
|
||||
content.push_str(" ");
|
||||
content.push_str(ts_remote);
|
||||
content.push_str(" ");
|
||||
content.push_str(&metadata.len().to_string());
|
||||
content.push_str(" ");
|
||||
content.push_str(&secs.to_string());
|
||||
content.push_str(" ");
|
||||
content.push_str(&self.get_file_hash());
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.obj_p.clone())?;
|
||||
|
||||
writeln!(file, "{}", &content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_data(&mut self) {
|
||||
if self.data.len() == 0 {
|
||||
if let Ok(mut file) = File::open(self.obj_p.clone()) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,349 +0,0 @@
|
||||
use std::io;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use crate::utils::path;
|
||||
use crate::store::head;
|
||||
use crate::store::object::{add_node, rm_node};
|
||||
use crypto::sha1::Sha1;
|
||||
use crypto::digest::Digest;
|
||||
use crate::utils::into::IntoPathBuf;
|
||||
use crate::store::object::{blob::Blob, tree::Tree};
|
||||
use crate::commands::status::{State, LocalObj};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
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;
|
||||
fn get_line(&self) -> String;
|
||||
fn add_ref_to_parent(&self) -> io::Result<()>;
|
||||
fn rm(&mut self) -> io::Result<()>;
|
||||
fn rm_node(&mut self) -> io::Result<()>;
|
||||
fn rm_node_down(&mut self) -> io::Result<()>;
|
||||
}
|
||||
|
||||
pub struct Obj {
|
||||
name: String,
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
impl ObjMethods for Obj {
|
||||
fn get_type(&self) -> ObjType {
|
||||
self.obj_type
|
||||
}
|
||||
|
||||
fn get_obj_path(&self) -> PathBuf {
|
||||
self.obj_path.clone()
|
||||
}
|
||||
|
||||
fn get_file_path(&self) -> PathBuf {
|
||||
self.file_path.clone()
|
||||
}
|
||||
|
||||
fn get_relative_file_path(&self) -> PathBuf {
|
||||
self.relative_file_path.clone()
|
||||
}
|
||||
|
||||
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.get_name(),
|
||||
path: self.get_file_path(),
|
||||
path_from: None,
|
||||
state: State::New
|
||||
}
|
||||
}
|
||||
|
||||
fn get_name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
fn get_hash_path(&self) -> String {
|
||||
self.hash_path.clone()
|
||||
}
|
||||
|
||||
// build line for parent reference
|
||||
fn get_line(&self) -> String {
|
||||
format!("tree {} {}", self.get_hash_path(), self.get_name())
|
||||
}
|
||||
|
||||
fn add_ref_to_parent(&self) -> io::Result<()> {
|
||||
let line = self.get_line();
|
||||
if self.get_relative_file_path().iter().count() == 1 {
|
||||
head::add_line(line)?;
|
||||
} else {
|
||||
add_node(self.get_relative_file_path().parent().unwrap(), &line)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rm_node(&mut self) -> io::Result<()> {
|
||||
// remove self object and children object
|
||||
self.rm_node_down();
|
||||
|
||||
// remove parent reference to self
|
||||
let line = self.get_line();
|
||||
if self.get_relative_file_path().iter().count() == 1 {
|
||||
head::rm_line(&line)?;
|
||||
} else {
|
||||
rm_node(self.get_relative_file_path().parent().unwrap(), &line)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rm_node_down(&mut self) -> io::Result<()> {
|
||||
eprintln!("rm_node_down: tried to do this on Obj");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rm(&mut self) -> io::Result<()> {
|
||||
eprintln!("rm: tried to do this on Obj");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
fn get_line(&self) -> String {
|
||||
self.obj.get_line()
|
||||
}
|
||||
|
||||
fn add_ref_to_parent(&self) -> io::Result<()> {
|
||||
self.obj.add_ref_to_parent()
|
||||
}
|
||||
|
||||
fn rm_node(&mut self) -> io::Result<()> {
|
||||
self.obj.rm_node()
|
||||
}
|
||||
|
||||
fn rm_node_down(&mut self) -> io::Result<()> {
|
||||
// remove reference to self
|
||||
fs::remove_file(self.get_obj_path())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rm(&mut self) -> io::Result<()> {
|
||||
// remove all references, including children's one
|
||||
self.rm_node()?;
|
||||
|
||||
// remove file
|
||||
fs::remove_file(self.get_file_path())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
fn get_line(&self) -> String {
|
||||
self.obj.get_line()
|
||||
}
|
||||
|
||||
fn add_ref_to_parent(&self) -> io::Result<()> {
|
||||
self.obj.add_ref_to_parent()
|
||||
}
|
||||
|
||||
fn rm_node(&mut self) -> io::Result<()> {
|
||||
self.obj.rm_node()
|
||||
}
|
||||
|
||||
/// remove objects and children but not parent reference to self
|
||||
fn rm_node_down(&mut self) -> io::Result<()> {
|
||||
// remove children
|
||||
while let Some(mut child) = self.next() {
|
||||
match child.get_type() {
|
||||
ObjType::TREE => child.rm_node_down(),
|
||||
ObjType::BLOB => child.rm_node_down(),
|
||||
_ => Ok(())
|
||||
}?;
|
||||
};
|
||||
|
||||
// remove reference to self
|
||||
fs::remove_file(self.get_obj_path())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rm(&mut self) -> io::Result<()> {
|
||||
// remove all references, including children's one
|
||||
self.rm_node()?;
|
||||
|
||||
// remove directory and all subfiles
|
||||
fs::remove_dir_all(self.get_file_path())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj {
|
||||
fn new() -> Self {
|
||||
Obj {
|
||||
name: String::new(),
|
||||
obj_path: PathBuf::new(),
|
||||
file_path: PathBuf::new(),
|
||||
obj_type: ObjType::DEFAULT,
|
||||
hash_path: String::new(),
|
||||
relative_file_path: PathBuf::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_path<S>(path: S) -> Obj where S: IntoPathBuf {
|
||||
let path = path.into();
|
||||
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()
|
||||
},
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/// load from the information line stored in the object
|
||||
pub fn from_line(line: String, base_dir: Option<PathBuf>) -> Box<dyn ObjMethods> {
|
||||
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_path = split.next().unwrap();
|
||||
let obj_type = split.next().unwrap();
|
||||
|
||||
let (dir, res) = hash_path.split_at(2);
|
||||
let mut obj_path = path::objects();
|
||||
obj_path.push(dir);
|
||||
obj_path.push(res);
|
||||
|
||||
let path = match base_dir {
|
||||
Some(dir) => dir.join(name),
|
||||
None => PathBuf::from(name),
|
||||
};
|
||||
|
||||
let root = path::repo_root();
|
||||
let abs_path = root.join(path.clone());
|
||||
|
||||
let obj = Obj {
|
||||
name: String::from(name),
|
||||
obj_path,
|
||||
obj_type: match obj_type {
|
||||
"tree" => ObjType::TREE,
|
||||
"blob" => ObjType::BLOB,
|
||||
_ => ObjType::DEFAULT
|
||||
},
|
||||
file_path: abs_path,
|
||||
relative_file_path: path,
|
||||
hash_path: String::from(hash_path),
|
||||
};
|
||||
|
||||
match obj.obj_type {
|
||||
ObjType::TREE => Box::new(Tree::new(obj)),
|
||||
ObjType::BLOB => Box::new(Blob::new(obj)),
|
||||
ObjType::DEFAULT => Box::new(Tree::new(obj))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_head() -> Self {
|
||||
Obj {
|
||||
name: String::new(),
|
||||
obj_path: head::path(),
|
||||
obj_type: ObjType::TREE,
|
||||
file_path: PathBuf::new(),
|
||||
relative_file_path: PathBuf::new(),
|
||||
hash_path: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,101 +1,106 @@
|
||||
use crate::utils::into::IntoPathBuf;
|
||||
use crate::store::object::object::Obj;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use crate::store::object::object::ObjMethods;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{self, BufRead, BufReader, Write, Lines};
|
||||
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};
|
||||
|
||||
pub struct Tree {
|
||||
pub obj: Obj,
|
||||
pub buf_reader: Option<BufReader<File>>,
|
||||
is_head: bool,
|
||||
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
impl Tree {
|
||||
pub fn new(obj: Obj) -> Self {
|
||||
Tree {
|
||||
obj,
|
||||
buf_reader: None,
|
||||
is_head: false,
|
||||
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(())
|
||||
}
|
||||
|
||||
pub fn from_head() -> Self {
|
||||
Tree {
|
||||
obj: Obj::from_head(),
|
||||
buf_reader: None,
|
||||
is_head: true,
|
||||
}
|
||||
}
|
||||
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<S>(r_path: S) -> Tree where S: IntoPathBuf {
|
||||
Tree {
|
||||
obj: Obj::from_path(r_path.into()),
|
||||
buf_reader: None,
|
||||
is_head: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self) {
|
||||
if self.buf_reader.is_none() {
|
||||
if let Ok(file) = File::open(self.get_obj_path()) {
|
||||
self.buf_reader = Some(BufReader::new(file));
|
||||
|
||||
// skip first line if is head
|
||||
if !self.is_head {
|
||||
self.next();
|
||||
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);
|
||||
},
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<Box<dyn ObjMethods>> {
|
||||
self.read();
|
||||
//if let Some(ref mut file) = self.buf_reader {
|
||||
// let mut line = String::new();
|
||||
// match file.read_line(&mut line) {
|
||||
// Ok(0) => Ok(None), // End of file
|
||||
// Ok(_) => Ok(Some(line.trim_end().len())), // Return length of line
|
||||
// Err(e) => Err(e),
|
||||
// }
|
||||
//} else {
|
||||
// Ok(None) // If file is None, return None
|
||||
//}
|
||||
match self.buf_reader {
|
||||
Some(ref mut file) => {
|
||||
let mut line = String::new();
|
||||
match file.read_line(&mut line) {
|
||||
Ok(0) => None,
|
||||
Ok(_) => Some(Obj::from_line(line, Some(self.get_relative_file_path()))),
|
||||
Err(e) => {
|
||||
eprintln!("tree::next: failed to read next line: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(&self, date: &str, up_parent: bool) -> io::Result<()> {
|
||||
// add tree reference to parent
|
||||
self.add_ref_to_parent();
|
||||
|
||||
// create tree object
|
||||
let mut content = format!("{} {}", self.get_name(), date);
|
||||
//create_obj(self.get_hash_path(), &content)?;
|
||||
todo!();
|
||||
|
||||
// update date for all parent
|
||||
todo!();
|
||||
//if up_parent {
|
||||
// update_dates(path, date)?;
|
||||
//}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
// create
|
||||
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))
|
||||
}
|
||||
|
@ -22,14 +22,6 @@ pub fn create() -> App<'static, 'static> {
|
||||
)
|
||||
.about("Add a new remote to this repository")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("verbose")
|
||||
.short("v")
|
||||
.long("verbose")
|
||||
.required(false)
|
||||
.takes_value(false)
|
||||
.help("Be a little more verbose and show remote url after name.")
|
||||
)
|
||||
}
|
||||
|
||||
pub fn handler(args: &ArgMatches<'_>) {
|
||||
@ -40,8 +32,6 @@ pub fn handler(args: &ArgMatches<'_>) {
|
||||
url: add_matches.values_of("url"),
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
commands::remote::remote_list(args.is_present("verbose"));
|
||||
}
|
||||
_ => println!("Invalid or missing subcommand for 'remote'"),
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub trait IntoPathBuf {
|
||||
fn into(self) -> PathBuf;
|
||||
@ -10,12 +10,6 @@ impl IntoPathBuf for PathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPathBuf for &Path {
|
||||
fn into(self) -> PathBuf {
|
||||
PathBuf::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPathBuf for String {
|
||||
fn into(self) -> PathBuf {
|
||||
PathBuf::from(self)
|
||||
|
@ -114,13 +114,6 @@ pub fn nextsync() -> PathBuf {
|
||||
path
|
||||
}
|
||||
|
||||
pub fn config() -> PathBuf {
|
||||
let mut path = repo_root();
|
||||
path.push(".nextsync");
|
||||
path.push("config");
|
||||
path
|
||||
}
|
||||
|
||||
pub fn objects() -> PathBuf {
|
||||
let mut path = repo_root();
|
||||
path.push(".nextsync");
|
||||
|
@ -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()
|
||||
} else {
|
||||
// ignore newly created file (not sync)
|
||||
Blob::from_path(el.unwrap().clone()).status(&mut None) == State::New
|
||||
Blob::new(el.unwrap().clone()).status(&mut None) == State::New
|
||||
}
|
||||
} {
|
||||
el = iter.next();
|
||||
|
105
tests/push.rs
105
tests/push.rs
@ -1,105 +0,0 @@
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
|
||||
mod utils;
|
||||
use utils::{server::ServerTest, client::ClientTest};
|
||||
|
||||
|
||||
fn get_random_test_id() -> String {
|
||||
let mut id: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(7)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
id.push_str("_nextsync");
|
||||
id.to_owned()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod push_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn simple_push() {
|
||||
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");
|
||||
client.run_cmd_ok("add file1");
|
||||
client.run_cmd_ok("push");
|
||||
|
||||
// tests
|
||||
assert!(server.has_file("file1", "foo"));
|
||||
|
||||
client.clean();
|
||||
server.clean();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_update() {
|
||||
let id = get_random_test_id();
|
||||
let mut server = ServerTest::new(id.clone()).init();
|
||||
let mut client = ClientTest::new(id).init();
|
||||
|
||||
// init content of file1
|
||||
let _ = client.add_file("file1", "foo");
|
||||
client.run_cmd_ok("add file1");
|
||||
client.run_cmd_ok("push");
|
||||
|
||||
// tests
|
||||
assert!(server.has_file("file1", "foo"));
|
||||
|
||||
// change content of file1
|
||||
let _ = client.add_file("file1", "bar");
|
||||
client.run_cmd_ok("add file1");
|
||||
client.run_cmd_ok("push");
|
||||
|
||||
// tests
|
||||
assert!(server.has_file("file1", "bar"));
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
216
tests/tests.rs
Normal file
216
tests/tests.rs
Normal file
@ -0,0 +1,216 @@
|
||||
use std::process::{Command, Output};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use rand::{distributions::Alphanumeric, Rng}; // 0.8
|
||||
use std::fs::{self, File, Permissions};
|
||||
use std::io::{Write, BufReader, BufRead};
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
struct ServerTest {
|
||||
user: String,
|
||||
volume: PathBuf,
|
||||
test_id: String
|
||||
}
|
||||
|
||||
impl ServerTest {
|
||||
fn new(id: String) -> Self {
|
||||
let mut volume = env::current_dir().unwrap();
|
||||
volume = volume.join("tests/data/admin/files");
|
||||
|
||||
ServerTest {
|
||||
user: String::from("admin"),
|
||||
volume,
|
||||
test_id: id
|
||||
}
|
||||
}
|
||||
|
||||
fn init(mut self) -> Self {
|
||||
self.add_dir(self.test_id.clone());
|
||||
self.volume = self.volume.join(self.test_id.clone());
|
||||
self.sync_test()
|
||||
}
|
||||
|
||||
fn clean(mut self) -> Self {
|
||||
self.remove_dir(self.test_id.clone());
|
||||
self.sync_root()
|
||||
}
|
||||
|
||||
fn add_dir(&mut self, path: String) -> &mut ServerTest {
|
||||
let mut full_path = self.volume.clone();
|
||||
full_path.push(path);
|
||||
|
||||
match fs::create_dir(&full_path) {
|
||||
Ok(_) => {
|
||||
// Set permissions to 777 to allow nextcloud to access it (workaround avoiding to
|
||||
// set group and owner to www-data)
|
||||
if let Err(e) = fs::set_permissions(&full_path, Permissions::from_mode(0o777)) {
|
||||
eprintln!("Error setting permissions: {}", e);
|
||||
}
|
||||
},
|
||||
Err(e) => eprintln!("Error creating directory: {}", e),
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn remove_dir(&mut self, path: String) -> &mut ServerTest {
|
||||
let mut full_path = self.volume.clone();
|
||||
full_path.push(path);
|
||||
|
||||
let _ = fs::remove_dir_all(&full_path);
|
||||
self
|
||||
}
|
||||
|
||||
fn sync_root(self) -> Self {
|
||||
self.sync("")
|
||||
}
|
||||
|
||||
fn sync_test(self) -> Self {
|
||||
let test_id = self.test_id.clone();
|
||||
self.sync(&test_id)
|
||||
}
|
||||
|
||||
fn sync(self, path: &str) -> Self {
|
||||
// perform the occ files:scan command inside the nextcloud docker container
|
||||
|
||||
let nextcloud_docker = "master-nextcloud-1";
|
||||
let mut args = String::from("exec -ti --user www-data");
|
||||
args.push_str(nextcloud_docker);
|
||||
args.push_str("/var/www/html/occ files:scan --path=/");
|
||||
args.push_str(&self.user);
|
||||
args.push_str("files/");
|
||||
args.push_str(path);
|
||||
|
||||
let _output = Command::new("docker")
|
||||
.args(args.split(" "))
|
||||
.output()
|
||||
.expect("Could not execute nextsync command");
|
||||
self
|
||||
}
|
||||
|
||||
fn has_file(&mut self, file: &str, content: &str) -> bool {
|
||||
let full_path = self.volume.clone().join(file);
|
||||
|
||||
if !full_path.exists() {
|
||||
eprintln!("File '{}' does't exists", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
let file = File::open(full_path).unwrap();
|
||||
for line in BufReader::new(file).lines(){
|
||||
if let Ok(line) = line {
|
||||
return line == content;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct ClientTest {
|
||||
user: String,
|
||||
volume: String,
|
||||
test_id: String,
|
||||
exe_path: PathBuf,
|
||||
}
|
||||
|
||||
impl ClientTest {
|
||||
fn new(id: String) -> Self {
|
||||
|
||||
// create a directory in /tmp with the given id
|
||||
let mut vol = String::from("/tmp/");
|
||||
vol.push_str(&id);
|
||||
let _ = fs::create_dir(vol.clone());
|
||||
|
||||
// get nextsync path
|
||||
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"),
|
||||
volume: vol,
|
||||
test_id: id,
|
||||
exe_path
|
||||
}
|
||||
}
|
||||
|
||||
fn init(mut self) -> Self {
|
||||
self.run_cmd_ok("init");
|
||||
|
||||
// set remote url
|
||||
let url = String::from(format!("{}@nextcloud.local/{}", self.user, self.test_id));
|
||||
self.run_cmd_ok(&format!("remote add origin {}", url));
|
||||
|
||||
// set force_unsecure as debug server has not certificate
|
||||
self.run_cmd_ok("config set force_insecure true");
|
||||
|
||||
// set token for request
|
||||
self.run_cmd_ok(&format!("credential add {} {}", self.user, self.user));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn clean(self) -> Self {
|
||||
let _ = fs::remove_dir_all(&self.volume);
|
||||
self
|
||||
}
|
||||
|
||||
fn run_cmd_ok(&mut self, args: &str) -> Output {
|
||||
let output = self.run_cmd(args);
|
||||
if !output.status.success() {
|
||||
println!("Failed to execute: '{}'", args);
|
||||
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
||||
}
|
||||
assert!(output.status.success());
|
||||
output
|
||||
}
|
||||
|
||||
fn run_cmd(&mut self, args: &str) -> Output {
|
||||
let output = Command::new(self.exe_path.to_str().unwrap())
|
||||
.args(args.split(" "))
|
||||
.output()
|
||||
.expect("Could not execute nextsync command");
|
||||
return output;
|
||||
}
|
||||
|
||||
fn add_file(&mut self, name: &str, content: &str) -> std::io::Result<()> {
|
||||
let mut path = self.volume.clone();
|
||||
path.push_str("/");
|
||||
path.push_str(name);
|
||||
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(content.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_random_test_id() -> String {
|
||||
let mut id: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(7)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
id.push_str("_nextsync");
|
||||
id.to_owned()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test1() {
|
||||
let id = get_random_test_id();
|
||||
dbg!(id.clone());
|
||||
let mut server = ServerTest::new(id.clone()).init();
|
||||
let mut client = ClientTest::new(id).init();
|
||||
|
||||
let _ = client.add_file("file1", "foo");
|
||||
client.run_cmd_ok("add file1");
|
||||
client.run_cmd_ok("push");
|
||||
|
||||
// tests
|
||||
assert!(server.has_file("file1", "foo"));
|
||||
|
||||
client.clean();
|
||||
server.clean();
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
#[path = "utils/server.rs"]
|
||||
pub mod server;
|
||||
|
||||
#[path = "utils/client.rs"]
|
||||
pub mod client;
|
@ -1,93 +0,0 @@
|
||||
use std::process::{Command, Output};
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct ClientTest {
|
||||
user: String, // the nextcloud user
|
||||
volume: String, // temp dir for the test
|
||||
test_id: String, // name of the test (e.g nextsync_rand)
|
||||
exe_path: PathBuf, // absolute path of nextsync executable
|
||||
}
|
||||
|
||||
impl ClientTest {
|
||||
pub fn new(id: String) -> Self {
|
||||
|
||||
// create a directory in /tmp with the given id
|
||||
let mut vol = String::from("/tmp/");
|
||||
vol.push_str(&id);
|
||||
let _ = fs::create_dir(vol.clone());
|
||||
|
||||
// get nextsync path
|
||||
let mut exe_path = env::current_dir().unwrap();
|
||||
exe_path = exe_path.join("target/debug/nextsync");
|
||||
|
||||
// build the client
|
||||
ClientTest {
|
||||
user: String::from("admin"),
|
||||
volume: vol,
|
||||
test_id: id,
|
||||
exe_path
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(mut self) -> Self {
|
||||
self.run_cmd_ok("init");
|
||||
|
||||
// set remote url
|
||||
let url = String::from(format!("{}@nextcloud.local/{}", self.user, self.test_id));
|
||||
self.run_cmd_ok(&format!("remote add origin {}", url));
|
||||
|
||||
// set force_unsecure as debug server has not certificate
|
||||
self.run_cmd_ok("config set force_insecure true");
|
||||
|
||||
// set token for request
|
||||
self.run_cmd_ok(&format!("credential add {} {}", self.user, self.user));
|
||||
self
|
||||
|
||||
}
|
||||
|
||||
pub fn clean(self) -> Self {
|
||||
let _ = fs::remove_dir_all(&self.volume);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn run_cmd_ok(&mut self, args: &str) -> Output {
|
||||
let output = self.run_cmd(args);
|
||||
if !output.status.success() {
|
||||
println!("Failed to execute: '{}'", args);
|
||||
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
||||
}
|
||||
assert!(output.status.success());
|
||||
output
|
||||
}
|
||||
|
||||
pub fn run_cmd(&mut self, args: &str) -> Output {
|
||||
let output = Command::new(self.exe_path.to_str().unwrap())
|
||||
.current_dir(self.volume.clone())
|
||||
.args(args.split(" "))
|
||||
.output()
|
||||
.expect("Could not execute nextsync command");
|
||||
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("/");
|
||||
path.push_str(name);
|
||||
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(content.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
use std::process::Command;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::fs::{self, File, Permissions};
|
||||
use std::io::{BufReader, BufRead};
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(test)]
|
||||
pub struct ServerTest {
|
||||
user: String,
|
||||
volume: PathBuf,
|
||||
test_id: String
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl ServerTest {
|
||||
pub fn new(id: String) -> Self {
|
||||
let mut volume = env::current_dir().unwrap();
|
||||
volume = volume.join("tests/data/admin/files");
|
||||
|
||||
ServerTest {
|
||||
user: String::from("admin"),
|
||||
volume,
|
||||
test_id: id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(mut self) -> Self {
|
||||
self.add_dir(self.test_id.clone());
|
||||
self.volume = self.volume.join(self.test_id.clone());
|
||||
self.sync_test()
|
||||
}
|
||||
|
||||
pub fn clean(mut self) -> Self {
|
||||
self.remove_dir(self.test_id.clone());
|
||||
self.sync_root()
|
||||
}
|
||||
|
||||
pub fn add_dir(&mut self, path: String) -> &mut ServerTest {
|
||||
let mut full_path = self.volume.clone();
|
||||
full_path.push(path);
|
||||
|
||||
match fs::create_dir(&full_path) {
|
||||
Ok(_) => {
|
||||
// Set permissions to 777 to allow nextcloud to access it (workaround avoiding to
|
||||
// set group and owner to www-data)
|
||||
if let Err(e) = fs::set_permissions(&full_path, Permissions::from_mode(0o777)) {
|
||||
eprintln!("Error setting permissions: {}", e);
|
||||
}
|
||||
},
|
||||
Err(e) => eprintln!("Error creating directory: {}", e),
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn remove_dir(&mut self, path: String) -> &mut ServerTest {
|
||||
let mut full_path = self.volume.clone();
|
||||
full_path.push(path);
|
||||
|
||||
let _ = fs::remove_dir_all(&full_path);
|
||||
self
|
||||
}
|
||||
|
||||
fn sync_root(self) -> Self {
|
||||
self.sync("")
|
||||
}
|
||||
|
||||
fn sync_test(self) -> Self {
|
||||
let test_id = self.test_id.clone();
|
||||
self.sync(&test_id)
|
||||
}
|
||||
|
||||
fn sync(self, path: &str) -> Self {
|
||||
// perform the occ files:scan command inside the nextcloud docker container
|
||||
|
||||
let nextcloud_docker = "master-nextcloud-1";
|
||||
let mut args = String::from("exec -ti --user www-data");
|
||||
args.push_str(nextcloud_docker);
|
||||
args.push_str("/var/www/html/occ files:scan --path=/");
|
||||
args.push_str(&self.user);
|
||||
args.push_str("files/");
|
||||
args.push_str(path);
|
||||
|
||||
let _output = Command::new("docker")
|
||||
.args(args.split(" "))
|
||||
.output()
|
||||
.expect("Could not execute nextsync command");
|
||||
self
|
||||
}
|
||||
|
||||
pub fn has_file(&mut self, file: &str, content: &str) -> bool {
|
||||
let full_path = self.volume.clone().join(file);
|
||||
|
||||
if !full_path.exists() {
|
||||
eprintln!("File '{}' doesn't exists on the server", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
let f = File::open(full_path).unwrap();
|
||||
for line in BufReader::new(f).lines(){
|
||||
if let Ok(line) = line {
|
||||
if line != content {
|
||||
eprintln!("File '{}' is not equal, {} != {}", file, line, content);
|
||||
return false;
|
||||
}
|
||||
return line == content;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user