view rust/hg-core/src/repo.rs @ 46446:1dcd9c9975ed

rust: Fold find_root and check_requirements into Repo::find Differential Revision: https://phab.mercurial-scm.org/D9906
author Simon Sapin <simon.sapin@octobus.net>
date Thu, 28 Jan 2021 20:31:42 +0100
parents 43d63979a75e
children d03b0601e0eb
line wrap: on
line source

use crate::errors::{HgError, IoResultExt};
use crate::requirements;
use memmap::{Mmap, MmapOptions};
use std::path::{Path, PathBuf};

/// A repository on disk
pub struct Repo {
    working_directory: PathBuf,
    dot_hg: PathBuf,
    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> {
    base: &'a Path,
}

impl Repo {
    /// 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);
            }
        }
        Err(RepoFindError::NotFoundInCurrentDirectoryOrAncestors {
            current_directory,
        })
    }

    pub fn working_directory_path(&self) -> &Path {
        &self.working_directory
    }

    /// For accessing repository files (in `.hg`), except for the store
    /// (`.hg/store`).
    pub(crate) fn hg_vfs(&self) -> Vfs<'_> {
        Vfs { base: &self.dot_hg }
    }

    /// For accessing repository store files (in `.hg/store`)
    pub(crate) fn store_vfs(&self) -> Vfs<'_> {
        Vfs { base: &self.store }
    }

    /// For accessing the working copy

    // The undescore prefix silences the "never used" warning. Remove before
    // using.
    pub(crate) fn _working_directory_vfs(&self) -> Vfs<'_> {
        Vfs {
            base: &self.working_directory,
        }
    }
}

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.join(relative_path);
        std::fs::read(&path).for_file(&path)
    }

    pub(crate) fn mmap_open(
        &self,
        relative_path: impl AsRef<Path>,
    ) -> Result<Mmap, HgError> {
        let path = self.base.join(relative_path);
        let file = std::fs::File::open(&path).for_file(&path)?;
        // TODO: what are the safety requirements here?
        let mmap = unsafe { MmapOptions::new().map(&file) }.for_file(&path)?;
        Ok(mmap)
    }
}