Compare commits

...

4 Commits

Author SHA1 Message Date
grimhilt
f56dcd30b8 cleaning all warnings 2023-07-02 19:04:45 +02:00
grimhilt
ddf2169950 use PathBuf in obj instead of &Path and get lastmodified when pushing changes 2023-07-02 18:50:33 +02:00
grimhilt
5ccce48381 update date to all parent 2023-07-02 18:49:56 +02:00
grimhilt
4ec389b6cc improve documentation 2023-07-02 15:51:51 +02:00
16 changed files with 210 additions and 62 deletions

19
Cargo.lock generated
View File

@ -113,7 +113,7 @@ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
"strsim", "strsim",
"textwrap", "textwrap 0.11.0",
"unicode-width", "unicode-width",
"vec_map", "vec_map",
] ]
@ -588,6 +588,7 @@ dependencies = [
"regex", "regex",
"reqwest", "reqwest",
"rust-crypto", "rust-crypto",
"textwrap 0.13.4",
"tokio", "tokio",
"xml-rs", "xml-rs",
] ]
@ -976,6 +977,12 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "smawk"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.4.9" version = "0.4.9"
@ -1025,6 +1032,16 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "textwrap"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd05616119e612a8041ef58f2b578906cc2531a6069047ae092cfb86a325d835"
dependencies = [
"smawk",
"unicode-width",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.45" version = "0.1.45"

View File

@ -16,6 +16,7 @@ xml-rs = "0.8.0"
regex = "1.8.3" regex = "1.8.3"
lazy_static = "1.4.0" lazy_static = "1.4.0"
glob = "0.3.1" glob = "0.3.1"
textwrap = "0.13"
chrono = "0.4.26" chrono = "0.4.26"
[profile.release] [profile.release]

View File

@ -104,7 +104,7 @@ pub fn clone(remote: Values<'_>) {
// add tree // add tree
let path_folder = p.strip_prefix(ref_path.clone()).unwrap(); let path_folder = p.strip_prefix(ref_path.clone()).unwrap();
let lastmodified = folder.lastmodified.unwrap().timestamp_millis(); let lastmodified = folder.lastmodified.unwrap().timestamp_millis();
if let Err(err) = tree::add(&path_folder, &lastmodified.to_string()) { if let Err(err) = tree::add(path_folder.to_path_buf(), &lastmodified.to_string()) {
eprintln!("err: saving ref of {} ({})", path_folder.display(), err); eprintln!("err: saving ref of {} ({})", path_folder.display(), err);
} }
} }
@ -134,7 +134,7 @@ fn download_files(ref_p: PathBuf, files: Vec<ObjProps>, api_props: &ApiProps) {
match res { match res {
Ok(()) => { Ok(()) => {
let relative_p = Path::new(&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::add(relative_p, &lastmodified.to_string()) { if let Err(err) = blob::add(relative_p, &lastmodified.to_string()) {
eprintln!("err: saving ref of {} ({})", relative_s.clone(), err); eprintln!("err: saving ref of {} ({})", relative_s.clone(), err);

View File

@ -9,7 +9,8 @@ pub mod rm_dir;
pub mod deleted; pub mod deleted;
pub fn push() { pub fn push() {
let remote = match config::get("remote") { // todo
let _remote = match config::get("remote") {
Some(r) => r, Some(r) => r,
None => { None => {
eprintln!("fatal: no remote set in configuration"); eprintln!("fatal: no remote set in configuration");
@ -27,20 +28,35 @@ pub fn push() {
for obj in staged_objs { for obj in staged_objs {
if obj.otype == String::from("tree") { if obj.otype == String::from("tree") {
dbg!(("folder", obj.clone()));
let push_factory = PushFactory.new_dir(obj.clone()); let push_factory = PushFactory.new_dir(obj.clone());
let res = match push_factory.can_push(&mut whitelist) { let res = push_factory.can_push(&mut whitelist);
PushState::Valid => push_factory.push(), match res {
PushState::Valid => {
match push_factory.push() {
Ok(()) => (),
Err(err) => {
eprintln!("err: pushing {}: {}", obj.name, err);
}
}
},
PushState::Done => (), PushState::Done => (),
PushState::Conflict => (), PushState::Conflict => {
println!("CONFLICT: {}", obj.clone().name);
},
_ => todo!(), _ => todo!(),
}; };
} else { } else {
dbg!(("file", obj.clone()));
let push_factory = PushFactory.new(obj.clone()); let push_factory = PushFactory.new(obj.clone());
match push_factory.can_push(&mut whitelist) { match push_factory.can_push(&mut whitelist) {
PushState::Valid => push_factory.push(), PushState::Valid => {
match push_factory.push() {
Ok(()) => (),
Err(err) => {
eprintln!("err: pushing {}: {}", obj.name, err);
}
}
},
PushState::Done => (), PushState::Done => (),
PushState::Conflict => { PushState::Conflict => {
// download file // download file

View File

@ -1,6 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::req_props::ReqProps;
use crate::services::delete_path::DeletePath; use crate::services::delete_path::DeletePath;
use crate::store::index; use crate::store::index;
use crate::store::object::blob; use crate::store::object::blob;
@ -22,7 +22,7 @@ impl PushChange for Deleted {
} }
} }
fn push(&self) { fn push(&self) -> io::Result<()> {
let obj = &self.obj; let obj = &self.obj;
let res = DeletePath::new() let res = DeletePath::new()
.set_url(obj.path.to_str().unwrap()) .set_url(obj.path.to_str().unwrap())
@ -41,9 +41,13 @@ impl PushChange for Deleted {
} }
// update tree // update tree
blob::rm(&obj.path.clone()); // todo date
blob::rm(obj.path.clone())?;
// remove index // remove index
index::rm_line(obj.path.to_str().unwrap()); index::rm_line(obj.path.to_str().unwrap())?;
Ok(())
} }
fn conflict(&self) { fn conflict(&self) {

View File

@ -1,5 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::req_props::ReqProps;
use crate::services::upload_file::UploadFile; use crate::services::upload_file::UploadFile;
use crate::store::index; use crate::store::index;
use crate::store::object::blob; use crate::store::object::blob;
@ -21,7 +23,7 @@ impl PushChange for New {
} }
} }
fn push(&self) { fn push(&self) -> io::Result<()> {
let obj = &self.obj; let obj = &self.obj;
let res = UploadFile::new() let res = UploadFile::new()
.set_url(obj.path.to_str().unwrap()) .set_url(obj.path.to_str().unwrap())
@ -40,11 +42,38 @@ impl PushChange for New {
_ => (), _ => (),
} }
// update tree // get lastmodified props to update it
blob::add(&obj.path.clone(), "todo_date"); let props = ReqProps::new()
.set_url(obj.path.to_str().unwrap())
.getlastmodified()
.send_req_single();
let prop = match props {
Ok(o) => o,
Err(ApiError::IncorrectRequest(err)) => {
eprintln!("fatal: {}", err.status());
std::process::exit(1);
},
Err(ApiError::EmptyError(_)) => {
eprintln!("Failed to get body");
std::process::exit(1);
}
Err(ApiError::RequestError(err)) => {
eprintln!("fatal: {}", err);
std::process::exit(1);
},
Err(ApiError::Unexpected(_)) => todo!()
};
let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
// update blob
blob::add(obj.path.clone(), &lastmodified.to_string())?;
// remove index // remove index
index::rm_line(obj.path.to_str().unwrap()); index::rm_line(obj.path.to_str().unwrap())?;
Ok(())
} }
// download file with .distant at the end // download file with .distant at the end

View File

@ -1,4 +1,5 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::req_props::ReqProps; use crate::services::req_props::ReqProps;
use crate::services::create_folder::CreateFolder; use crate::services::create_folder::CreateFolder;
@ -28,7 +29,7 @@ impl PushChange for NewDir {
} }
} }
fn push(&self) { fn push(&self) -> io::Result<()> {
let obj = &self.obj; let obj = &self.obj;
let res = CreateFolder::new() let res = CreateFolder::new()
.set_url(obj.path.to_str().unwrap()) .set_url(obj.path.to_str().unwrap())
@ -46,11 +47,39 @@ impl PushChange for NewDir {
_ => (), _ => (),
} }
// get lastmodified props to update it
let props = ReqProps::new()
.set_url(obj.path.to_str().unwrap())
.getlastmodified()
.send_req_single();
let prop = match props {
Ok(o) => o,
Err(ApiError::IncorrectRequest(err)) => {
eprintln!("fatal: {}", err.status());
std::process::exit(1);
},
Err(ApiError::EmptyError(_)) => {
eprintln!("Failed to get body");
std::process::exit(1);
}
Err(ApiError::RequestError(err)) => {
eprintln!("fatal: {}", err);
std::process::exit(1);
},
Err(ApiError::Unexpected(_)) => todo!()
};
let lastmodified = prop.lastmodified.unwrap().timestamp_millis();
// update tree // update tree
tree::add(&obj.path.clone(), "todo_date"); tree::add(obj.path.clone(), &lastmodified.to_string())?;
// remove index // remove index
index::rm_line(obj.path.to_str().unwrap()); index::rm_line(obj.path.to_str().unwrap())?;
Ok(())
} }
fn conflict(&self) {} fn conflict(&self) {}

View File

@ -1,8 +1,9 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io;
use crate::commands::status::{State, LocalObj}; use crate::commands::status::{State, LocalObj};
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::store::object; use crate::store::object;
use crate::services::req_props::{ObjProps, ReqProps}; use crate::services::req_props::ReqProps;
use crate::commands::push::new::New; use crate::commands::push::new::New;
use crate::commands::push::new_dir::NewDir; use crate::commands::push::new_dir::NewDir;
use crate::commands::push::rm_dir::RmDir; use crate::commands::push::rm_dir::RmDir;
@ -26,18 +27,9 @@ pub enum PushFlowState {
pub trait PushChange { pub trait PushChange {
fn can_push(&self, whitelist: &mut Option<PathBuf>) -> PushState; fn can_push(&self, whitelist: &mut Option<PathBuf>) -> PushState;
fn push(&self); fn push(&self) -> io::Result<()>;
fn conflict(&self); fn conflict(&self);
fn try_push(&self, whitelist: &mut Option<PathBuf>) {
match self.can_push(whitelist) {
PushState::Valid => self.push(),
PushState::Conflict => self.conflict(),
PushState::Done => (),
PushState::Error => (),
}
}
fn is_whitelisted(&self, obj: &LocalObj, path: Option<PathBuf>) -> bool { fn is_whitelisted(&self, obj: &LocalObj, path: Option<PathBuf>) -> bool {
match path { match path {
Some(p) => obj.path.starts_with(p), Some(p) => obj.path.starts_with(p),

View File

@ -1,4 +1,5 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::io;
use crate::services::api::ApiError; use crate::services::api::ApiError;
use crate::services::delete_path::DeletePath; use crate::services::delete_path::DeletePath;
use crate::store::index; use crate::store::index;
@ -27,7 +28,7 @@ impl PushChange for RmDir {
} }
} }
fn push(&self) { fn push(&self) -> io::Result<()> {
let obj = &self.obj; let obj = &self.obj;
let res = DeletePath::new() let res = DeletePath::new()
.set_url(obj.path.to_str().unwrap()) .set_url(obj.path.to_str().unwrap())
@ -46,9 +47,13 @@ impl PushChange for RmDir {
} }
// update tree // update tree
tree::rm(&obj.path.clone()); // todo update date
tree::rm(obj.path.clone())?;
// remove index // remove index
index::rm_line(obj.path.to_str().unwrap()); index::rm_line(obj.path.to_str().unwrap())?;
Ok(())
} }
fn conflict(&self) {} fn conflict(&self) {}

View File

@ -1,7 +1,7 @@
use std::fs::File; use std::fs::File;
use std::path::PathBuf; use std::path::PathBuf;
use std::io::{self, Lines, BufReader}; use std::io::{self, Lines, BufReader};
use std::collections::{HashSet, HashMap}; use std::collections::HashMap;
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::sha1::Sha1; use crypto::sha1::Sha1;
use colored::Colorize; use colored::Colorize;
@ -31,7 +31,7 @@ pub enum State {
pub fn status() { pub fn status() {
let (mut new_objs_hashes, mut del_objs_hashes) = get_diff(); let (mut new_objs_hashes, mut del_objs_hashes) = get_diff();
// get copy, modified // get copy, modified
let mut staged_objs = get_staged(&mut new_objs_hashes, &mut del_objs_hashes); let staged_objs = get_staged(&mut new_objs_hashes, &mut del_objs_hashes);
let mut objs: Vec<LocalObj> = del_objs_hashes.iter().map(|x| { let mut objs: Vec<LocalObj> = del_objs_hashes.iter().map(|x| {
x.1.clone() x.1.clone()
@ -41,8 +41,6 @@ pub fn status() {
objs.push(elt.clone()); objs.push(elt.clone());
} }
dbg!(objs.clone());
dbg!(staged_objs.clone());
print_status(staged_objs, objs); print_status(staged_objs, objs);
} }
@ -57,7 +55,7 @@ pub struct LocalObj {
pub fn get_all_staged() -> Vec<LocalObj> { pub fn get_all_staged() -> Vec<LocalObj> {
let (mut new_objs_hashes, mut del_objs_hashes) = get_diff(); let (mut new_objs_hashes, mut del_objs_hashes) = get_diff();
// get copy, modified // get copy, modified
let mut staged_objs = get_staged(&mut new_objs_hashes, &mut del_objs_hashes); let staged_objs = get_staged(&mut new_objs_hashes, &mut del_objs_hashes);
staged_objs.clone() staged_objs.clone()
// todo opti getting staged and then finding differences ? // todo opti getting staged and then finding differences ?
@ -92,11 +90,12 @@ fn get_staged(new_objs_h: &mut HashMap<String, LocalObj>, del_objs_h: &mut HashM
del_objs_h.remove(&hash); del_objs_h.remove(&hash);
}else { }else {
let mut t_path = ref_p.clone(); let mut t_path = ref_p.clone();
t_path.push(PathBuf::from(obj.clone())); let relative_p = PathBuf::from(obj.clone());
t_path.push(relative_p.clone());
staged_objs.push(LocalObj { staged_objs.push(LocalObj {
otype: get_otype(t_path.clone()), otype: get_otype(t_path.clone()),
name: obj.to_string(), name: obj.to_string(),
path: t_path.clone(), path: relative_p.clone(),
state: { state: {
if t_path.exists() { if t_path.exists() {
State::New State::New

View File

@ -1,4 +1,5 @@
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, SubCommand};
use textwrap::{fill, Options};
use crate::commands::add::AddArgs; use crate::commands::add::AddArgs;
mod commands; mod commands;
@ -20,6 +21,10 @@ fn main() {
.required(true) .required(true)
.takes_value(true) .takes_value(true)
.value_name("REMOTE") .value_name("REMOTE")
.help(&fill(
"The repository to clone from. See the NEXTSYNC URLS section below for more information on specifying repositories.",
Options::new(80).width,
))
) )
.arg( .arg(
Arg::with_name("directory") Arg::with_name("directory")
@ -28,6 +33,7 @@ fn main() {
.value_name("DIRECTORY") .value_name("DIRECTORY")
) )
.about("Clone a repository into a new directory") .about("Clone a repository into a new directory")
.after_help("NEXTSYNC URLS\nThe following syntaxes may be used:\n\t- user@host.xz/path/to/repo\n\t- http[s]://host.xz/apps/files/?dir=/path/to/repo&fileid=111111\n\t- [http[s]://]host.xz/remote.php/dav/files/user/path/to/repo\n")
) )
.subcommand( .subcommand(
SubCommand::with_name("init") SubCommand::with_name("init")

View File

@ -73,7 +73,7 @@ impl ReqProps {
self self
} }
pub fn getcontentlenght(&mut self) -> &mut ReqProps { pub fn _getcontentlenght(&mut self) -> &mut ReqProps {
self.xml_balises.push(String::from("getcontentlength")); self.xml_balises.push(String::from("getcontentlength"));
self.xml_payload.push_str(r#"<d:getcontentlength/>"#); self.xml_payload.push_str(r#"<d:getcontentlength/>"#);
self self

View File

@ -1,8 +1,9 @@
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::fs::{OpenOptions, self}; use std::fs::{self, OpenOptions};
use crypto::sha1::Sha1; use crypto::sha1::Sha1;
use crypto::digest::Digest; use crypto::digest::Digest;
use std::io::{Seek, SeekFrom, Read};
use crate::utils::{read, path}; use crate::utils::{read, path};
pub mod tree; pub mod tree;
@ -13,7 +14,7 @@ pub mod blob;
/// # Examples /// # Examples
/// Input: /foo/bar /// Input: /foo/bar
/// Result: ("tree hash(/foo/bar) bar", hash(/foo/bar), bar) /// Result: ("tree hash(/foo/bar) bar", hash(/foo/bar), bar)
pub fn parse_path(path: &Path, is_blob: bool) -> (String, String, String) { pub fn parse_path(path: PathBuf, is_blob: bool) -> (String, String, String) {
let file_name = path.file_name().unwrap().to_str().unwrap(); let file_name = path.file_name().unwrap().to_str().unwrap();
let mut hasher = Sha1::new(); let mut hasher = Sha1::new();
@ -86,6 +87,48 @@ fn add_node(path: &Path, node: &str) -> io::Result<()> {
Ok(()) Ok(())
} }
fn update_dates(mut path: PathBuf, date: &str) -> io::Result<()> {
let mut obj_p = path::objects();
while path.pop() {
let (dir, res) = hash_obj(path.to_str().unwrap());
obj_p.push(dir);
obj_p.push(res);
update_date(obj_p.clone(), date.clone())?;
obj_p.pop();
obj_p.pop();
}
Ok(())
}
pub fn update_date(path: PathBuf, date: &str) -> io::Result<()> {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open(path.clone())?;
let mut buffer = [0; 1];
file.seek(SeekFrom::Start(0))?;
// Seek and read until a space is found
loop {
let bytes_read = file.read(&mut buffer)?;
if bytes_read == 0 {
// Reached the end of the file without finding a space
break;
}
if buffer[0] == b' ' {
break;
}
}
file.write_all(&date.as_bytes())?;
Ok(())
}
fn create_obj(name: String, content: &str) -> io::Result<()> { fn create_obj(name: String, content: &str) -> io::Result<()> {
let mut root = path::objects(); let mut root = path::objects();

View File

@ -1,11 +1,11 @@
use std::io::{self}; use std::io;
use std::path::Path; use std::path::PathBuf;
use std::fs::{self}; use std::fs;
use crate::utils::path; use crate::utils::path;
use crate::store::head; use crate::store::head;
use crate::store::object::{parse_path, add_node, create_obj, rm_node}; use crate::store::object::{update_dates, parse_path, add_node, create_obj, rm_node};
pub fn add(path: &Path, date: &str) -> io::Result<()> { pub fn add(path: PathBuf, date: &str) -> io::Result<()> {
let (line, hash, name) = parse_path(path.clone(), true); let (line, hash, name) = parse_path(path.clone(), true);
// add blob reference to parent // add blob reference to parent
if path.iter().count() == 1 { if path.iter().count() == 1 {
@ -21,10 +21,13 @@ pub fn add(path: &Path, date: &str) -> io::Result<()> {
// create blob object // create blob object
create_obj(hash, &content)?; create_obj(hash, &content)?;
// update date for all parent
update_dates(path, date)?;
Ok(()) Ok(())
} }
pub fn rm(path: &Path) -> io::Result<()> { pub fn rm(path: PathBuf) -> io::Result<()> {
let (line, hash, _) = parse_path(path.clone(), true); let (line, hash, _) = parse_path(path.clone(), true);
// remove blob reference to parent // remove blob reference to parent

View File

@ -1,11 +1,11 @@
use std::fs::File; use std::fs::File;
use std::io::{self}; use std::io;
use std::path::Path; use std::path::PathBuf;
use crate::utils::{read, path}; use crate::utils::{read, path};
use crate::store::head; use crate::store::head;
use crate::store::object::{self, parse_path, hash_obj, add_node, create_obj}; use crate::store::object::{self, update_dates, parse_path, hash_obj, add_node, create_obj};
pub fn add(path: &Path, date: &str) -> io::Result<()> { pub fn add(path: PathBuf, date: &str) -> io::Result<()> {
let (line, hash, name) = parse_path(path.clone(), false); let (line, hash, name) = parse_path(path.clone(), false);
// add tree reference to parent // add tree reference to parent
@ -21,23 +21,26 @@ pub fn add(path: &Path, date: &str) -> io::Result<()> {
content.push_str(date); content.push_str(date);
create_obj(hash, &content)?; create_obj(hash, &content)?;
// update date for all parent
update_dates(path, date)?;
Ok(()) Ok(())
} }
pub fn rm(path: &Path) -> io::Result<()> { pub fn rm(path: PathBuf) -> io::Result<()> {
let (_, lines) = read(path.to_path_buf().to_str().unwrap().to_string()).unwrap(); let (_, lines) = read(path.to_path_buf().to_str().unwrap().to_string()).unwrap();
for line in lines { for line in lines {
let (ftype, hash, _) = parse_line(line.unwrap()); let (ftype, hash, _) = parse_line(line.unwrap());
if ftype == String::from("blob") { if ftype == String::from("blob") {
object::rm(&hash); object::rm(&hash)?;
} else { } else {
rm_hash(hash); rm_hash(hash)?;
} }
} }
Ok(()) Ok(())
} }
fn rm_hash(hash: String) { fn rm_hash(hash: String) -> io::Result<()> {
let mut obj_p = path::objects(); let mut obj_p = path::objects();
let (dir, res) = hash.split_at(2); let (dir, res) = hash.split_at(2);
obj_p.push(dir); obj_p.push(dir);
@ -49,9 +52,9 @@ fn rm_hash(hash: String) {
for line in reader { for line in reader {
let (ftype, hash, _) = parse_line(line.unwrap()); let (ftype, hash, _) = parse_line(line.unwrap());
if ftype == String::from("blob") { if ftype == String::from("blob") {
object::rm(&hash); object::rm(&hash)?;
} else { } else {
rm_hash(hash); rm_hash(hash)?;
} }
} }
}, },
@ -59,6 +62,7 @@ fn rm_hash(hash: String) {
eprintln!("error reading tree: {}", err); eprintln!("error reading tree: {}", err);
}, },
} }
Ok(())
} }
pub fn read(tree: String) -> Option<(String, io::Lines<io::BufReader<File>>)> { pub fn read(tree: String) -> Option<(String, io::Lines<io::BufReader<File>>)> {