rust/hg-core/src/repo.rs
author Simon Sapin <simon.sapin@octobus.net>
Wed, 27 Jan 2021 14:45:25 +0100
changeset 46443 43d63979a75e
parent 46442 02d3bb972121
child 46446 1dcd9c9975ed
permissions -rw-r--r--
rust: use HgError in RevlogError and Vfs Differential Revision: https://phab.mercurial-scm.org/D9897

use crate::errors::{HgError, IoResultExt};
use crate::operations::{find_root, FindRootError};
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,
}

/// Filesystem access abstraction for the contents of a given "base" diretory
#[derive(Clone, Copy)]
pub(crate) struct Vfs<'a> {
    base: &'a Path,
}

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,
        }
    }

    pub fn find() -> Result<Self, FindRootError> {
        find_root().map(Self::for_path)
    }

    pub fn check_requirements(&self) -> Result<(), HgError> {
        requirements::check(self)
    }

    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 read(
        &self,
        relative_path: impl AsRef<Path>,
    ) -> Result<Vec<u8>, HgError> {
        let path = self.base.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)
    }
}