view rust/hg-core/src/operations/find_root.rs @ 46152:e166e8a035a7

copies-rust: use the entry API to overwrite deleted entry This is more efficient, more idiomatic and more compact. The main motivation for this change is to cleanup that area before start to do "overwrite" tracking. Such tracking will ultimately help to avoid costly is_ancestors call when merging changeset. Differential Revision: https://phab.mercurial-scm.org/D9494
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Wed, 02 Dec 2020 10:51:40 +0100
parents dca9cb99971c
children 3e2d539d0d1a
line wrap: on
line source

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 process’ current directory and its
/// ancestors
pub fn find_root() -> Result<PathBuf, FindRootError> {
    let current_dir = std::env::current_dir().map_err(|e| FindRootError {
        kind: FindRootErrorKind::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 {
        kind: FindRootErrorKind::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 { 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 = 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 */