hg-core: add `Manifest` a specialized `Revlog`
A facade to `Revlog` to provide a `manifest` specific interface.
Differential Revision: https://phab.mercurial-scm.org/D9011
--- a/rust/hg-core/src/revlog.rs Wed Sep 09 16:25:23 2020 +0200
+++ b/rust/hg-core/src/revlog.rs Wed Sep 09 14:50:58 2020 +0200
@@ -10,6 +10,7 @@
pub use node::{Node, NodeError, NodePrefix, NodePrefixRef};
pub mod changelog;
pub mod index;
+pub mod manifest;
pub mod patch;
pub mod revlog;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/revlog/manifest.rs Wed Sep 09 14:50:58 2020 +0200
@@ -0,0 +1,60 @@
+use crate::revlog::revlog::{Revlog, RevlogError};
+use crate::revlog::Revision;
+use crate::utils::hg_path::HgPath;
+use std::path::PathBuf;
+
+/// A specialized `Revlog` to work with `manifest` data format.
+pub struct Manifest {
+ /// The generic `revlog` format.
+ revlog: Revlog,
+}
+
+impl Manifest {
+ /// Open the `manifest` of a repository given by its root.
+ pub fn open(root: &PathBuf) -> Result<Self, RevlogError> {
+ let index_file = root.join(".hg/store/00manifest.i");
+ let revlog = Revlog::open(&index_file)?;
+ Ok(Self { revlog })
+ }
+
+ /// Return the `ManifestEntry` of a given node id.
+ pub fn get_node(&self, node: &[u8]) -> Result<ManifestEntry, RevlogError> {
+ let rev = self.revlog.get_node_rev(node)?;
+ self.get_rev(rev)
+ }
+
+ /// Return the `ManifestEntry` of a given node revision.
+ pub fn get_rev(
+ &self,
+ rev: Revision,
+ ) -> Result<ManifestEntry, RevlogError> {
+ let bytes = self.revlog.get_rev_data(rev)?;
+ Ok(ManifestEntry { bytes })
+ }
+}
+
+/// `Manifest` entry which knows how to interpret the `manifest` data bytes.
+#[derive(Debug)]
+pub struct ManifestEntry {
+ bytes: Vec<u8>,
+}
+
+impl ManifestEntry {
+ /// Return an iterator over the lines of the entry.
+ pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
+ self.bytes
+ .split(|b| b == &b'\n')
+ .filter(|line| !line.is_empty())
+ }
+
+ /// Return an iterator over the files of the entry.
+ pub fn files(&self) -> impl Iterator<Item = &HgPath> {
+ self.lines().filter(|line| !line.is_empty()).map(|line| {
+ let pos = line
+ .iter()
+ .position(|x| x == &b'\0')
+ .expect("manifest line should contain \\0");
+ HgPath::new(&line[..pos])
+ })
+ }
+}