##// END OF EJS Templates
rhg: Add lazy/cached dirstate data file ID parsing on Repo...
rhg: Add lazy/cached dirstate data file ID parsing on Repo The `dirstate_parents`, `dirstate_data_file_uuid`, and `dirstate_map` members of `Repo` can be access in any order and the `.hg/dirstate` file should only be opened once. Differential Revision: https://phab.mercurial-scm.org/D11838

File last commit:

r49166:eb428010 default
r49248:c7c23bb0 default
Show More
manifest.rs
119 lines | 3.6 KiB | application/rls-services+xml | RustLexer
use crate::errors::HgError;
use crate::repo::Repo;
use crate::revlog::revlog::{Revlog, RevlogError};
use crate::revlog::Revision;
use crate::revlog::{Node, NodePrefix};
use crate::utils::hg_path::HgPath;
use crate::utils::SliceExt;
/// A specialized `Revlog` to work with `manifest` data format.
pub struct Manifestlog {
/// The generic `revlog` format.
revlog: Revlog,
}
impl Manifestlog {
/// Open the `manifest` of a repository given by its root.
pub fn open(repo: &Repo) -> Result<Self, HgError> {
let revlog = Revlog::open(repo, "00manifest.i", None)?;
Ok(Self { revlog })
}
/// Return the `Manifest` for the given node ID.
///
/// Note: this is a node ID in the manifestlog, typically found through
/// `ChangelogEntry::manifest_node`. It is *not* the node ID of any
/// changeset.
///
/// See also `Repo::manifest_for_node`
pub fn data_for_node(
&self,
node: NodePrefix,
) -> Result<Manifest, RevlogError> {
let rev = self.revlog.rev_from_node(node)?;
self.data_for_rev(rev)
}
/// Return the `Manifest` of a given revision number.
///
/// Note: this is a revision number in the manifestlog, *not* of any
/// changeset.
///
/// See also `Repo::manifest_for_rev`
pub fn data_for_rev(
&self,
rev: Revision,
) -> Result<Manifest, RevlogError> {
let bytes = self.revlog.get_rev_data(rev)?;
Ok(Manifest { bytes })
}
}
/// `Manifestlog` entry which knows how to interpret the `manifest` data bytes.
#[derive(Debug)]
pub struct Manifest {
bytes: Vec<u8>,
}
impl Manifest {
pub fn iter(
&self,
) -> impl Iterator<Item = Result<ManifestEntry, HgError>> {
self.bytes
.split(|b| b == &b'\n')
.filter(|line| !line.is_empty())
.map(|line| {
let (path, rest) = line.split_2(b'\0').ok_or_else(|| {
HgError::corrupted("manifest line should contain \\0")
})?;
let path = HgPath::new(path);
let (hex_node_id, flags) = match rest.split_last() {
Some((&b'x', rest)) => (rest, Some(b'x')),
Some((&b'l', rest)) => (rest, Some(b'l')),
Some((&b't', rest)) => (rest, Some(b't')),
_ => (rest, None),
};
Ok(ManifestEntry {
path,
hex_node_id,
flags,
})
})
}
/// If the given path is in this manifest, return its filelog node ID
pub fn find_file(
&self,
path: &HgPath,
) -> Result<Option<ManifestEntry>, HgError> {
// TODO: use binary search instead of linear scan. This may involve
// building (and caching) an index of the byte indicex of each manifest
// line.
// TODO: use try_find when available (if still using linear scan)
// https://github.com/rust-lang/rust/issues/63178
for entry in self.iter() {
let entry = entry?;
if entry.path == path {
return Ok(Some(entry));
}
}
Ok(None)
}
}
/// `Manifestlog` entry which knows how to interpret the `manifest` data bytes.
#[derive(Debug)]
pub struct ManifestEntry<'manifest> {
pub path: &'manifest HgPath,
pub hex_node_id: &'manifest [u8],
/// `Some` values are b'x', b'l', or 't'
pub flags: Option<u8>,
}
impl ManifestEntry<'_> {
pub fn node_id(&self) -> Result<Node, HgError> {
Node::from_hex_for_repo(self.hex_node_id)
}
}