rust: Fold find_root and check_requirements into Repo::find
authorSimon Sapin <simon.sapin@octobus.net>
Thu, 28 Jan 2021 20:31:42 +0100
changeset 46446 1dcd9c9975ed
parent 46445 ca3f73cc3cf4
child 46447 0cb1b02228a6
rust: Fold find_root and check_requirements into Repo::find Differential Revision: https://phab.mercurial-scm.org/D9906
rust/hg-core/src/config/config.rs
rust/hg-core/src/operations/find_root.rs
rust/hg-core/src/operations/mod.rs
rust/hg-core/src/repo.rs
rust/rhg/src/commands/cat.rs
rust/rhg/src/commands/files.rs
rust/rhg/src/error.rs
tests/test-rhg.t
--- a/rust/hg-core/src/config/config.rs	Thu Jan 28 19:13:55 2021 +0100
+++ b/rust/hg-core/src/config/config.rs	Thu Jan 28 20:31:42 2021 +0100
@@ -11,7 +11,7 @@
 use crate::config::layer::{ConfigError, ConfigLayer, ConfigValue};
 use std::path::PathBuf;
 
-use crate::operations::find_root;
+use crate::repo::Repo;
 use crate::utils::files::read_whole_file;
 
 /// Holds the config values for the current repository
@@ -76,10 +76,9 @@
 
     /// Loads the local config. In a future version, this will also load the
     /// `$HOME/.hgrc` and more to mirror the Python implementation.
-    pub fn load() -> Result<Self, ConfigError> {
-        let root = find_root().unwrap();
+    pub fn load_for_repo(repo: &Repo) -> Result<Self, ConfigError> {
         Ok(Self::load_from_explicit_sources(vec![
-            ConfigSource::AbsPath(root.join(".hg/hgrc")),
+            ConfigSource::AbsPath(repo.hg_vfs().join("hgrc")),
         ])?)
     }
 
--- a/rust/hg-core/src/operations/find_root.rs	Thu Jan 28 19:13:55 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-use std::path::{Path, PathBuf};
-
-/// Error type for `find_root`
-#[derive(Debug)]
-pub enum FindRootError {
-    /// 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),
-}
-
-/// Find the root of the repository
-/// by searching for a .hg directory in the process’ current directory and its
-/// ancestors
-pub fn find_root() -> Result<PathBuf, FindRootError> {
-    let current_dir = std::env::current_dir()
-        .map_err(|e| FindRootError::GetCurrentDirError(e))?;
-    Ok(find_root_from_path(&current_dir)?.into())
-}
-
-/// Find the root of the repository
-/// by searching for a .hg directory in the given directory and its ancestors
-pub fn find_root_from_path(start: &Path) -> Result<&Path, FindRootError> {
-    if start.join(".hg").exists() {
-        return Ok(start);
-    }
-    for ancestor in start.ancestors() {
-        if ancestor.join(".hg").exists() {
-            return Ok(ancestor);
-        }
-    }
-    Err(FindRootError::RootNotFound(start.into()))
-}
-
-#[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 = find_root_from_path(&path).unwrap_err();
-
-        // TODO do something better
-        assert!(match err {
-            FindRootError::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 = find_root_from_path(&root).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 directory = root.join("some/nested/directory");
-        let result = find_root_from_path(&directory).unwrap();
-
-        assert_eq!(result, root)
-    }
-} /* tests */
--- a/rust/hg-core/src/operations/mod.rs	Thu Jan 28 19:13:55 2021 +0100
+++ b/rust/hg-core/src/operations/mod.rs	Thu Jan 28 20:31:42 2021 +0100
@@ -5,10 +5,8 @@
 mod cat;
 mod debugdata;
 mod dirstate_status;
-mod find_root;
 mod list_tracked_files;
 pub use cat::cat;
 pub use debugdata::{debug_data, DebugDataKind};
-pub use find_root::{find_root, find_root_from_path, FindRootError};
 pub use list_tracked_files::Dirstate;
 pub use list_tracked_files::{list_rev_tracked_files, FilesForRev};
--- a/rust/hg-core/src/repo.rs	Thu Jan 28 19:13:55 2021 +0100
+++ b/rust/hg-core/src/repo.rs	Thu Jan 28 20:31:42 2021 +0100
@@ -1,5 +1,4 @@
 use crate::errors::{HgError, IoResultExt};
-use crate::operations::{find_root, FindRootError};
 use crate::requirements;
 use memmap::{Mmap, MmapOptions};
 use std::path::{Path, PathBuf};
@@ -11,6 +10,15 @@
     store: PathBuf,
 }
 
+#[derive(Debug, derive_more::From)]
+pub enum RepoFindError {
+    NotFoundInCurrentDirectoryOrAncestors {
+        current_directory: PathBuf,
+    },
+    #[from]
+    Other(HgError),
+}
+
 /// Filesystem access abstraction for the contents of a given "base" diretory
 #[derive(Clone, Copy)]
 pub(crate) struct Vfs<'a> {
@@ -18,24 +26,26 @@
 }
 
 impl Repo {
-    /// Returns `None` if the given path doesn’t look like a repository
-    /// (doesn’t contain a `.hg` sub-directory).
-    pub fn for_path(root: impl Into<PathBuf>) -> Self {
-        let working_directory = root.into();
-        let dot_hg = working_directory.join(".hg");
-        Self {
-            store: dot_hg.join("store"),
-            dot_hg,
-            working_directory,
+    /// Search the current directory and its ancestores for a repository:
+    /// a working directory that contains a `.hg` sub-directory.
+    pub fn find() -> Result<Self, RepoFindError> {
+        let current_directory = crate::utils::current_dir()?;
+        // ancestors() is inclusive: it first yields `current_directory` as-is.
+        for ancestor in current_directory.ancestors() {
+            let dot_hg = ancestor.join(".hg");
+            if dot_hg.is_dir() {
+                let repo = Self {
+                    store: dot_hg.join("store"),
+                    dot_hg,
+                    working_directory: ancestor.to_owned(),
+                };
+                requirements::check(&repo)?;
+                return Ok(repo);
+            }
         }
-    }
-
-    pub fn find() -> Result<Self, FindRootError> {
-        find_root().map(Self::for_path)
-    }
-
-    pub fn check_requirements(&self) -> Result<(), HgError> {
-        requirements::check(self)
+        Err(RepoFindError::NotFoundInCurrentDirectoryOrAncestors {
+            current_directory,
+        })
     }
 
     pub fn working_directory_path(&self) -> &Path {
@@ -65,11 +75,15 @@
 }
 
 impl Vfs<'_> {
+    pub(crate) fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
+        self.base.join(relative_path)
+    }
+
     pub(crate) fn read(
         &self,
         relative_path: impl AsRef<Path>,
     ) -> Result<Vec<u8>, HgError> {
-        let path = self.base.join(relative_path);
+        let path = self.join(relative_path);
         std::fs::read(&path).for_file(&path)
     }
 
--- a/rust/rhg/src/commands/cat.rs	Thu Jan 28 19:13:55 2021 +0100
+++ b/rust/rhg/src/commands/cat.rs	Thu Jan 28 20:31:42 2021 +0100
@@ -31,7 +31,6 @@
     #[timed]
     fn run(&self, ui: &Ui) -> Result<(), CommandError> {
         let repo = Repo::find()?;
-        repo.check_requirements()?;
         let cwd = hg::utils::current_dir()?;
 
         let mut files = vec![];
--- a/rust/rhg/src/commands/files.rs	Thu Jan 28 19:13:55 2021 +0100
+++ b/rust/rhg/src/commands/files.rs	Thu Jan 28 20:31:42 2021 +0100
@@ -48,7 +48,6 @@
 impl<'a> Command for FilesCommand<'a> {
     fn run(&self, ui: &Ui) -> Result<(), CommandError> {
         let repo = Repo::find()?;
-        repo.check_requirements()?;
         if let Some(rev) = self.rev {
             let files =
                 list_rev_tracked_files(&repo, rev).map_err(|e| (e, rev))?;
--- a/rust/rhg/src/error.rs	Thu Jan 28 19:13:55 2021 +0100
+++ b/rust/rhg/src/error.rs	Thu Jan 28 20:31:42 2021 +0100
@@ -1,8 +1,10 @@
 use crate::ui::utf8_to_local;
 use crate::ui::UiError;
-use hg::errors::{HgError, IoErrorContext};
-use hg::operations::FindRootError;
+use format_bytes::format_bytes;
+use hg::errors::HgError;
+use hg::repo::RepoFindError;
 use hg::revlog::revlog::RevlogError;
+use hg::utils::files::get_bytes_from_path;
 use std::convert::From;
 
 /// The kind of command error
@@ -48,18 +50,18 @@
     }
 }
 
-impl From<FindRootError> for CommandError {
-    fn from(err: FindRootError) -> Self {
-        match err {
-            FindRootError::RootNotFound(path) => CommandError::abort(format!(
-                "no repository found in '{}' (.hg not found)!",
-                path.display()
-            )),
-            FindRootError::GetCurrentDirError(error) => HgError::IoError {
-                error,
-                context: IoErrorContext::CurrentDir,
-            }
-            .into(),
+impl From<RepoFindError> for CommandError {
+    fn from(error: RepoFindError) -> Self {
+        match error {
+            RepoFindError::NotFoundInCurrentDirectoryOrAncestors {
+                current_directory,
+            } => CommandError::Abort {
+                message: format_bytes!(
+                    b"no repository found in '{}' (.hg not found)!",
+                    get_bytes_from_path(current_directory)
+                ),
+            },
+            RepoFindError::Other(error) => error.into(),
         }
     }
 }
--- a/tests/test-rhg.t	Thu Jan 28 19:13:55 2021 +0100
+++ b/tests/test-rhg.t	Thu Jan 28 20:31:42 2021 +0100
@@ -153,13 +153,7 @@
   [252]
 
   $ rhg debugrequirements
-  dotencode
-  fncache
-  generaldelta
-  revlogv1
-  sparserevlog
-  store
-  indoor-pool
+  [252]
 
   $ echo -e '\xFF' >> .hg/requires
   $ rhg debugrequirements