Mercurial > public > mercurial-scm > hg
diff rust/hg-core/src/revlog/filelog.rs @ 47961:4d2a5ca060e3
rust: Add a Filelog struct that wraps Revlog
Some filelog-specific logic is moved from code `rhg cat` into this struct
where it can better be reused.
Additionally, a missing end delimiter for metadata causes an error
to be returned instead of being silently ignored.
Differential Revision: https://phab.mercurial-scm.org/D11408
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Mon, 13 Sep 2021 15:42:39 +0200 |
parents | |
children | 001d747c2baf |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hg-core/src/revlog/filelog.rs Mon Sep 13 15:42:39 2021 +0200 @@ -0,0 +1,79 @@ +use crate::errors::HgError; +use crate::repo::Repo; +use crate::revlog::path_encode::path_encode; +use crate::revlog::revlog::{Revlog, RevlogError}; +use crate::revlog::NodePrefix; +use crate::revlog::Revision; +use crate::utils::files::get_path_from_bytes; +use crate::utils::hg_path::HgPath; +use crate::utils::SliceExt; +use std::borrow::Cow; +use std::path::PathBuf; + +/// A specialized `Revlog` to work with file data logs. +pub struct Filelog { + /// The generic `revlog` format. + revlog: Revlog, +} + +impl Filelog { + pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, RevlogError> { + let index_path = store_path(file_path, b".i"); + let data_path = store_path(file_path, b".d"); + let revlog = Revlog::open(repo, index_path, Some(&data_path))?; + Ok(Self { revlog }) + } + + /// The given node ID is that of the file as found in a manifest, not of a + /// changeset. + pub fn get_node( + &self, + file_node: impl Into<NodePrefix>, + ) -> Result<FilelogEntry, RevlogError> { + let file_rev = self.revlog.get_node_rev(file_node.into())?; + self.get_rev(file_rev) + } + + /// The given revision is that of the file as found in a manifest, not of a + /// changeset. + pub fn get_rev( + &self, + file_rev: Revision, + ) -> Result<FilelogEntry, RevlogError> { + let data = self.revlog.get_rev_data(file_rev)?; + Ok(FilelogEntry(data.into())) + } +} + +fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { + let encoded_bytes = + path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); + get_path_from_bytes(&encoded_bytes).into() +} + +pub struct FilelogEntry<'filelog>(Cow<'filelog, [u8]>); + +impl<'filelog> FilelogEntry<'filelog> { + /// Split into metadata and data + pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> { + const DELIMITER: &[u8; 2] = &[b'\x01', b'\n']; + + if let Some(rest) = self.0.drop_prefix(DELIMITER) { + if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) { + Ok((Some(metadata), data)) + } else { + Err(HgError::corrupted( + "Missing metadata end delimiter in filelog entry", + )) + } + } else { + Ok((None, &self.0)) + } + } + + /// Returns the file contents at this revision, stripped of any metadata + pub fn data(&self) -> Result<&[u8], HgError> { + let (_metadata, data) = self.split()?; + Ok(data) + } +}