repo.rs
140 lines
| 4.1 KiB
| application/rls-services+xml
|
RustLexer
Simon Sapin
|
r47172 | use crate::errors::{HgError, IoResultExt}; | ||
Simon Sapin
|
r46782 | use crate::requirements; | ||
Simon Sapin
|
r47190 | use crate::utils::files::get_path_from_bytes; | ||
Simon Sapin
|
r46782 | use memmap::{Mmap, MmapOptions}; | ||
Simon Sapin
|
r47190 | use std::collections::HashSet; | ||
Simon Sapin
|
r46782 | use std::path::{Path, PathBuf}; | ||
/// A repository on disk | ||||
pub struct Repo { | ||||
working_directory: PathBuf, | ||||
dot_hg: PathBuf, | ||||
store: PathBuf, | ||||
Simon Sapin
|
r47190 | requirements: HashSet<String>, | ||
Simon Sapin
|
r46782 | } | ||
Simon Sapin
|
r47175 | #[derive(Debug, derive_more::From)] | ||
pub enum RepoFindError { | ||||
NotFoundInCurrentDirectoryOrAncestors { | ||||
current_directory: PathBuf, | ||||
}, | ||||
#[from] | ||||
Other(HgError), | ||||
} | ||||
Simon Sapin
|
r46782 | /// Filesystem access abstraction for the contents of a given "base" diretory | ||
#[derive(Clone, Copy)] | ||||
pub(crate) struct Vfs<'a> { | ||||
base: &'a Path, | ||||
} | ||||
impl Repo { | ||||
Simon Sapin
|
r47175 | /// 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() { | ||||
Simon Sapin
|
r47190 | if ancestor.join(".hg").is_dir() { | ||
return Ok(Self::new_at_path(ancestor.to_owned())?); | ||||
Simon Sapin
|
r47175 | } | ||
Simon Sapin
|
r46782 | } | ||
Simon Sapin
|
r47175 | Err(RepoFindError::NotFoundInCurrentDirectoryOrAncestors { | ||
current_directory, | ||||
}) | ||||
Simon Sapin
|
r46782 | } | ||
Simon Sapin
|
r47190 | /// To be called after checking that `.hg` is a sub-directory | ||
fn new_at_path(working_directory: PathBuf) -> Result<Self, HgError> { | ||||
let dot_hg = working_directory.join(".hg"); | ||||
let hg_vfs = Vfs { base: &dot_hg }; | ||||
let reqs = requirements::load_if_exists(hg_vfs)?; | ||||
let relative = | ||||
reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT); | ||||
let shared = | ||||
reqs.contains(requirements::SHARED_REQUIREMENT) || relative; | ||||
let store_path; | ||||
if !shared { | ||||
store_path = dot_hg.join("store"); | ||||
} else { | ||||
let bytes = hg_vfs.read("sharedpath")?; | ||||
let mut shared_path = get_path_from_bytes(&bytes).to_owned(); | ||||
if relative { | ||||
shared_path = dot_hg.join(shared_path) | ||||
} | ||||
if !shared_path.is_dir() { | ||||
return Err(HgError::corrupted(format!( | ||||
".hg/sharedpath points to nonexistent directory {}", | ||||
shared_path.display() | ||||
))); | ||||
} | ||||
store_path = shared_path.join("store"); | ||||
} | ||||
let repo = Self { | ||||
requirements: reqs, | ||||
working_directory, | ||||
store: store_path, | ||||
dot_hg, | ||||
}; | ||||
requirements::check(&repo)?; | ||||
Ok(repo) | ||||
} | ||||
Simon Sapin
|
r46782 | pub fn working_directory_path(&self) -> &Path { | ||
&self.working_directory | ||||
} | ||||
Simon Sapin
|
r47190 | pub fn requirements(&self) -> &HashSet<String> { | ||
&self.requirements | ||||
} | ||||
Simon Sapin
|
r46782 | /// 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<'_> { | ||||
Simon Sapin
|
r47175 | pub(crate) fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf { | ||
self.base.join(relative_path) | ||||
} | ||||
Simon Sapin
|
r46782 | pub(crate) fn read( | ||
&self, | ||||
relative_path: impl AsRef<Path>, | ||||
Simon Sapin
|
r47172 | ) -> Result<Vec<u8>, HgError> { | ||
Simon Sapin
|
r47175 | let path = self.join(relative_path); | ||
Simon Sapin
|
r47172 | std::fs::read(&path).for_file(&path) | ||
Simon Sapin
|
r46782 | } | ||
pub(crate) fn mmap_open( | ||||
&self, | ||||
relative_path: impl AsRef<Path>, | ||||
Simon Sapin
|
r47172 | ) -> Result<Mmap, HgError> { | ||
let path = self.base.join(relative_path); | ||||
let file = std::fs::File::open(&path).for_file(&path)?; | ||||
Simon Sapin
|
r46782 | // TODO: what are the safety requirements here? | ||
Simon Sapin
|
r47172 | let mmap = unsafe { MmapOptions::new().map(&file) }.for_file(&path)?; | ||
Simon Sapin
|
r46782 | Ok(mmap) | ||
} | ||||
} | ||||