rust/hg-core/src/revlog/manifest.rs
changeset 48343 eb428010aad2
parent 48342 10c32e1b892a
child 48495 e293ff808a05
--- 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)
+    }
+}