From 8f636b4bf75da40487fd12697a908b170a6da556 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Wed, 11 Sep 2024 23:36:29 +0200 Subject: [PATCH] feat(status): relative path of obj --- src/commands/status.rs | 12 +++---- src/store/object.rs | 30 ++++++++++++++++-- src/store/structs.rs | 2 +- src/utils/path.rs | 9 ++++++ tests/common/client.rs | 7 +++++ tests/status_test.rs | 71 +++++++++++++++++++++++++++++++++--------- 6 files changed, 107 insertions(+), 24 deletions(-) diff --git a/src/commands/status.rs b/src/commands/status.rs index f165042..68aaeac 100644 --- a/src/commands/status.rs +++ b/src/commands/status.rs @@ -106,7 +106,7 @@ fn setup_staged(obj_statuses: Arc, indexer: &Indexer) -> ObjStaged pub fn exec(args: StatusArgs, config: Config) { let status = get_obj_changes(&args, &config); - print_status(&status); + print_status(&status, config.get_root_unsafe()); } pub fn get_obj_changes(args: &StatusArgs, config: &Config) -> ObjStaged { @@ -156,7 +156,7 @@ pub fn get_obj_changes(args: &StatusArgs, config: &Config) -> ObjStaged { } /// -fn print_status(objs: &ObjStaged) { +fn print_status(objs: &ObjStaged, root_path: &PathBuf) { if objs.staged.len() == 0 && objs.not_staged.len() == 0 { println!("Nothing to push, working tree clean"); return; @@ -168,7 +168,7 @@ fn print_status(objs: &ObjStaged) { // (use "git restore --staged ..." to unstage) // by alphabetical order for obj in objs.staged.iter() { - print_object(&obj, |status: &str| status.green()); + print_object(&obj, root_path, |status: &str| status.green()); } } // modified @@ -178,7 +178,7 @@ fn print_status(objs: &ObjStaged) { println!("Changes not staged for push:"); println!(" (Use \"nextsync add ...\" to update what will be pushed)"); for obj in objs.not_staged.iter() { - print_object(&obj, |status: &str| status.red()); + print_object(&obj, root_path, |status: &str| status.red()); } // println!("Untracked files:"); @@ -187,7 +187,7 @@ fn print_status(objs: &ObjStaged) { // } } -fn print_object(obj: &Obj, color: impl Fn(&str) -> ColoredString) { +fn print_object(obj: &Obj, root_path: &PathBuf, color: impl Fn(&str) -> ColoredString) { println!( " {}{}", match obj.get_status() { @@ -201,7 +201,7 @@ fn print_object(obj: &Obj, color: impl Fn(&str) -> ColoredString) { ObjStatus::Deleted => color("deleted: "), _ => "unknown".red(), }, - color(&obj.cpy_path().to_string()) + color(&path::to_string(&obj.get_env_relative_path(root_path))) ); } diff --git a/src/store/object.rs b/src/store/object.rs index 1f58653..b3ac240 100644 --- a/src/store/object.rs +++ b/src/store/object.rs @@ -1,5 +1,6 @@ -use crate::store::{structs::ObjPath, nsobject::NsObject}; +use crate::store::{nsobject::NsObject, structs::ObjPath}; use crate::utils::path; +use std::env; use std::fs; use std::path::PathBuf; use std::sync::OnceLock; @@ -26,7 +27,6 @@ impl From for u8 { } } - impl TryFrom for ObjType { type Error = String; @@ -96,6 +96,32 @@ impl Obj { &self.obj_path } + /// Return the path of the current object relatively to the path the + /// command was executed from. + /// + /// * `repo_root`: the absolute repo's root path + pub fn get_env_relative_path(&self, repo_root: &PathBuf) -> PathBuf { + let binding = env::current_dir().unwrap(); + + if let Ok(root_diff) = binding.strip_prefix(repo_root) { + match self.obj_path.strip_prefix(&root_diff) { + Ok(path) => path.to_path_buf(), + Err(_) => { + // if cannot strip prefix then we need to go up to the root + let mut res_path = PathBuf::new(); + for _ in 0..path::get_level(&root_diff.to_path_buf()) { + res_path.push(".."); + } + res_path.push(&*self.obj_path); + res_path + } + } + } else { + // if cannot strip prefix then we are at the repo's root + self.obj_path.to_path_buf() + } + } + pub fn cpy_path(&self) -> String { path::to_string(&self.obj_path) } diff --git a/src/store/structs.rs b/src/store/structs.rs index e9fb84e..6371de0 100644 --- a/src/store/structs.rs +++ b/src/store/structs.rs @@ -13,7 +13,7 @@ static REPO_ROOT: OnceLock = OnceLock::new(); pub fn init(repo_root: &Option) { if tests::is_var_setup() { - REPO_ROOT_DEV.lock().unwrap()[0] = repo_root.clone().unwrap(); + REPO_ROOT_DEV.lock().unwrap()[0] = repo_root.clone().expect("Tried to initialize REPO_ROOT_DEV without a proper `repo_root`"); return; } diff --git a/src/utils/path.rs b/src/utils/path.rs index c05f53a..06ef1e1 100644 --- a/src/utils/path.rs +++ b/src/utils/path.rs @@ -16,6 +16,15 @@ pub fn to_string(path: &PathBuf) -> String { path.to_str().unwrap().to_string() } +pub fn get_level(path: &PathBuf) -> i32 { + let mut level = 0; + let mut path = path.clone(); + while path.pop() { + level += 1; + } + level +} + /// Improve the path to try remove and solve .. token. /// Taken from https://stackoverflow.com/questions/68231306/stdfscanonicalize-for-files-that-dont-exist /// diff --git a/tests/common/client.rs b/tests/common/client.rs index 793928c..694fe66 100644 --- a/tests/common/client.rs +++ b/tests/common/client.rs @@ -66,6 +66,13 @@ impl ClientTest { Config::from(Some(&self.volume)) } + pub fn new_config(&self, path: &str) -> Config { + let mut full_path = self.volume.clone(); + full_path.push_str("/"); + full_path.push_str(path); + Config::from(Some(&full_path)) + } + pub fn ok(self) -> io::Result<()> { fs::remove_dir_all(&self.volume)?; Ok(()) diff --git a/tests/status_test.rs b/tests/status_test.rs index 7a29360..ede0d3d 100644 --- a/tests/status_test.rs +++ b/tests/status_test.rs @@ -15,19 +15,35 @@ fn status_expected(config: &Config, staged: Vec<&str>, not_staged: Vec<&str>) { assert_eq!(res.not_staged.len(), not_staged.len()); for obj in staged { - assert!(res - .staged - .iter() - .position(|e| { e.get_obj_path() == &PathBuf::from(obj) }) - .is_some()); + assert!( + res.staged + .iter() + .position(|e| { + e.get_env_relative_path(config.get_root_unsafe()) == PathBuf::from(obj) + }) + .is_some(), + "{:?}", + res.staged + .iter() + .map(|e| { e.get_env_relative_path(config.get_root_unsafe()) }) + .collect::>() + ); } for obj in not_staged { - assert!(res - .not_staged - .iter() - .position(|e| { e.get_obj_path() == &PathBuf::from(obj) }) - .is_some()); + assert!( + res.not_staged + .iter() + .position(|e| { + e.get_env_relative_path(config.get_root_unsafe()) == PathBuf::from(obj) + }) + .is_some(), + "{:?}", + res.not_staged + .iter() + .map(|e| { e.get_env_relative_path(config.get_root_unsafe()) }) + .collect::>() + ); } } @@ -55,13 +71,16 @@ fn all_folder() -> io::Result<()> { status_expected(&client.get_config(), vec![], vec!["foo", "dir"]); client.exec_ok("add dir"); - status_expected(&client.get_config(), vec!["dir/foo", "dir/bar"], vec!["foo"]); + status_expected( + &client.get_config(), + vec!["dir/foo", "dir/bar"], + vec!["foo"], + ); client.ok() } #[test] -#[ignore] fn all_folder_current() -> io::Result<()> { let mut client = ClientTest::new("status__all_folder_current").init(); @@ -73,9 +92,31 @@ fn all_folder_current() -> io::Result<()> { client.exec_ok("add dir"); status_expected( - &Config::from(Some(&String::from("./dir"))), - vec!["foor", "bar"], - vec!["../foo"], + &client.new_config("dir"), + vec!["dir/foo", "dir/bar"], + vec![], + ); + + client.ok() +} + +#[test] +#[ignore] +fn relative_path() -> io::Result<()> { + let mut client = ClientTest::new("status__all_folder_current").init(); + + client.add_dir("dir")?; + client.add_file("dir/foo", "foo")?; + client.add_file("dir/bar", "bar")?; + client.add_file("foor", "foor")?; + status_expected(&client.get_config(), vec![], vec!["foor", "dir"]); + + client.exec_ok("add dir"); + // client.set_execution_path("dir"); + status_expected( + &client.new_config("dir"), + vec!["foo", "bar"], + vec!["../foor"], ); client.ok()