Mercurial > public > mercurial-scm > hg
diff rust/hg-core/src/revlog/manifest.rs @ 48343:eb428010aad2
rhg: Also parse flags in the manifest parser
Differential Revision: https://phab.mercurial-scm.org/D11772
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Tue, 23 Nov 2021 19:39:51 +0100 |
parents | 10c32e1b892a |
children | e293ff808a05 |
line wrap: on
line diff
--- a/rust/hg-core/src/revlog/manifest.rs Tue Nov 23 18:27:42 2021 +0100 +++ b/rust/hg-core/src/revlog/manifest.rs Tue Nov 23 19:39:51 2021 +0100 @@ -4,6 +4,7 @@ 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 { @@ -55,50 +56,64 @@ } impl Manifest { - /// Return an iterator over the lines of the entry. - pub fn lines(&self) -> impl Iterator<Item = &[u8]> { + pub fn iter( + &self, + ) -> impl Iterator<Item = Result<ManifestEntry, HgError>> { 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 = Result<&HgPath, HgError>> { - self.lines().filter(|line| !line.is_empty()).map(|line| { - let pos = - line.iter().position(|x| x == &b'\0').ok_or_else(|| { + .map(|line| { + let (path, rest) = line.split_2(b'\0').ok_or_else(|| { HgError::corrupted("manifest line should contain \\0") })?; - Ok(HgPath::new(&line[..pos])) - }) - } - - /// Return an iterator over the files of the entry. - pub fn files_with_nodes( - &self, - ) -> impl Iterator<Item = Result<(&HgPath, &[u8]), HgError>> { - self.lines().filter(|line| !line.is_empty()).map(|line| { - let pos = - line.iter().position(|x| x == &b'\0').ok_or_else(|| { - HgError::corrupted("manifest line should contain \\0") - })?; - let hash_start = pos + 1; - let hash_end = hash_start + 40; - Ok((HgPath::new(&line[..pos]), &line[hash_start..hash_end])) - }) + 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<Node>, HgError> { + 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. - for entry in self.files_with_nodes() { - let (manifest_path, node) = entry?; - if manifest_path == path { - return Ok(Some(Node::from_hex_for_repo(node)?)); + + // 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) + } +}