# HG changeset patch # User Antoine Cezar # Date 1591339689 -7200 # Node ID 5965efb609b6f8f2108b5b55ecd34e23b7fc093c # Parent a46e36b824612be16b38a1c1cd11160c293d115b hg-core: add FindRoot operation to find repository root path Differential Revision: https://phab.mercurial-scm.org/D8609 diff -r a46e36b82461 -r 5965efb609b6 rust/hg-core/src/operations/find_root.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hg-core/src/operations/find_root.rs Fri Jun 05 08:48:09 2020 +0200 @@ -0,0 +1,124 @@ +use super::Operation; +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), + } + } +} + +impl<'a> Operation for FindRoot<'a> { + type Error = FindRootError; + + 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.into()); + } + let mut ancestors = current_dir.ancestors(); + while let Some(parent) = ancestors.next() { + 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 */ diff -r a46e36b82461 -r 5965efb609b6 rust/hg-core/src/operations/mod.rs --- a/rust/hg-core/src/operations/mod.rs Fri Jun 05 08:46:35 2020 +0200 +++ b/rust/hg-core/src/operations/mod.rs Fri Jun 05 08:48:09 2020 +0200 @@ -1,3 +1,6 @@ +mod find_root; +pub use find_root::{FindRoot, FindRootError, FindRootErrorKind}; + /// An interface for high-level hg operations. /// /// A distinction is made between operation and commands.