use std::fmt; use std::path::{Path, PathBuf}; /// Kind of error encoutered by FindRoot #[derive(Debug)] pub enum FindRootErrorKind { /// Root of the repository has not been found /// Contains the current directory used by FindRoot RootNotFound(PathBuf), /// The current directory does not exists or permissions are insufficient /// to get access to it GetCurrentDirError(std::io::Error), } /// A FindRoot error #[derive(Debug)] pub struct FindRootError { /// Kind of error encoutered by FindRoot pub kind: FindRootErrorKind, } impl std::error::Error for FindRootError {} impl fmt::Display for FindRootError { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } } /// Find the root of the repository /// by searching for a .hg directory in the current directory and its /// ancestors pub struct FindRoot<'a> { current_dir: Option<&'a Path>, } impl<'a> FindRoot<'a> { pub fn new() -> Self { Self { current_dir: None } } pub fn new_from_path(current_dir: &'a Path) -> Self { Self { current_dir: Some(current_dir), } } pub fn run(&self) -> Result { let current_dir = match self.current_dir { None => std::env::current_dir().or_else(|e| { Err(FindRootError { kind: FindRootErrorKind::GetCurrentDirError(e), }) })?, Some(path) => path.into(), }; if current_dir.join(".hg").exists() { return Ok(current_dir); } let ancestors = current_dir.ancestors(); for parent in ancestors { if parent.join(".hg").exists() { return Ok(parent.into()); } } Err(FindRootError { kind: FindRootErrorKind::RootNotFound(current_dir.to_path_buf()), }) } } #[cfg(test)] mod tests { use super::*; use std::fs; use tempfile; #[test] fn dot_hg_not_found() { let tmp_dir = tempfile::tempdir().unwrap(); let path = tmp_dir.path(); let err = FindRoot::new_from_path(&path).run().unwrap_err(); // TODO do something better assert!(match err { FindRootError { kind } => match kind { FindRootErrorKind::RootNotFound(p) => p == path.to_path_buf(), _ => false, }, }) } #[test] fn dot_hg_in_current_path() { let tmp_dir = tempfile::tempdir().unwrap(); let root = tmp_dir.path(); fs::create_dir_all(root.join(".hg")).unwrap(); let result = FindRoot::new_from_path(&root).run().unwrap(); assert_eq!(result, root) } #[test] fn dot_hg_in_parent() { let tmp_dir = tempfile::tempdir().unwrap(); let root = tmp_dir.path(); fs::create_dir_all(root.join(".hg")).unwrap(); let result = FindRoot::new_from_path(&root.join("some/nested/directory")) .run() .unwrap(); assert_eq!(result, root) } } /* tests */