hg-core: add a `ListRevTrackedFiles` operation
authorAntoine Cezar <antoine.cezar@octobus.net>
Fri, 18 Sep 2020 16:52:16 +0200
changeset 45536 639f33f22faf
parent 45535 72b7d58d6e35
child 45537 2f8227a12592
hg-core: add a `ListRevTrackedFiles` operation List files tracked at a given revision. Differential Revision: https://phab.mercurial-scm.org/D9014
rust/hg-core/src/operations/list_tracked_files.rs
rust/hg-core/src/operations/mod.rs
rust/hg-core/src/revlog/index.rs
rust/hg-core/src/revlog/revlog.rs
--- a/rust/hg-core/src/operations/list_tracked_files.rs	Wed Sep 09 12:12:11 2020 +0200
+++ b/rust/hg-core/src/operations/list_tracked_files.rs	Fri Sep 18 16:52:16 2020 +0200
@@ -6,14 +6,16 @@
 // GNU General Public License version 2 or any later version.
 
 use crate::dirstate::parsers::parse_dirstate;
+use crate::revlog::changelog::Changelog;
+use crate::revlog::manifest::{Manifest, ManifestEntry};
+use crate::revlog::revlog::RevlogError;
+use crate::revlog::Revision;
 use crate::utils::hg_path::HgPath;
 use crate::{DirstateParseError, EntryState};
 use rayon::prelude::*;
 use std::convert::From;
-use std::fmt;
 use std::fs;
-use std::ops::Deref;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
 
 /// Kind of error encountered by `ListDirstateTrackedFiles`
 #[derive(Debug)]
@@ -31,14 +33,6 @@
     pub kind: ListDirstateTrackedFilesErrorKind,
 }
 
-impl std::error::Error for ListDirstateTrackedFilesError {}
-
-impl fmt::Display for ListDirstateTrackedFilesError {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        unimplemented!()
-    }
-}
-
 impl From<ListDirstateTrackedFilesErrorKind>
     for ListDirstateTrackedFilesError
 {
@@ -84,3 +78,111 @@
         Ok(files)
     }
 }
+
+/// Kind of error encountered by `ListRevTrackedFiles`
+#[derive(Debug)]
+pub enum ListRevTrackedFilesErrorKind {
+    /// Error when reading a `revlog` file.
+    IoError(std::io::Error),
+    /// The revision has not been found.
+    InvalidRevision,
+    /// A `revlog` file is corrupted.
+    CorruptedRevlog,
+    /// The `revlog` format version is not supported.
+    UnsuportedRevlogVersion(u16),
+    /// The `revlog` data format is not supported.
+    UnknowRevlogDataFormat(u8),
+}
+
+/// A `ListRevTrackedFiles` error
+#[derive(Debug)]
+pub struct ListRevTrackedFilesError {
+    /// Kind of error encountered by `ListRevTrackedFiles`
+    pub kind: ListRevTrackedFilesErrorKind,
+}
+
+impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError {
+    fn from(kind: ListRevTrackedFilesErrorKind) -> Self {
+        ListRevTrackedFilesError { kind }
+    }
+}
+
+impl From<RevlogError> for ListRevTrackedFilesError {
+    fn from(err: RevlogError) -> Self {
+        match err {
+            RevlogError::IoError(err) => {
+                ListRevTrackedFilesErrorKind::IoError(err)
+            }
+            RevlogError::UnsuportedVersion(version) => {
+                ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version)
+            }
+            RevlogError::InvalidRevision => {
+                ListRevTrackedFilesErrorKind::InvalidRevision
+            }
+            RevlogError::Corrupted => {
+                ListRevTrackedFilesErrorKind::CorruptedRevlog
+            }
+            RevlogError::UnknowDataFormat(format) => {
+                ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format)
+            }
+        }
+        .into()
+    }
+}
+
+/// List files under Mercurial control at a given revision.
+pub struct ListRevTrackedFiles<'a> {
+    /// The revision to list the files from.
+    rev: &'a str,
+    /// The changelog file
+    changelog: Changelog,
+    /// The manifest file
+    manifest: Manifest,
+    /// The manifest entry corresponding to the revision.
+    ///
+    /// Used to hold the owner of the returned references.
+    manifest_entry: Option<ManifestEntry>,
+}
+
+impl<'a> ListRevTrackedFiles<'a> {
+    pub fn new(
+        root: &PathBuf,
+        rev: &'a str,
+    ) -> Result<Self, ListRevTrackedFilesError> {
+        let changelog = Changelog::open(&root)?;
+        let manifest = Manifest::open(&root)?;
+
+        Ok(Self {
+            rev,
+            changelog,
+            manifest,
+            manifest_entry: None,
+        })
+    }
+
+    pub fn run(
+        &mut self,
+    ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> {
+        let changelog_entry = match self.rev.parse::<Revision>() {
+            Ok(rev) => self.changelog.get_rev(rev)?,
+            _ => {
+                let changelog_node = hex::decode(&self.rev).map_err(|_| {
+                    ListRevTrackedFilesErrorKind::InvalidRevision
+                })?;
+                self.changelog.get_node(&changelog_node)?
+            }
+        };
+        let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
+            .map_err(|_| ListRevTrackedFilesErrorKind::CorruptedRevlog)?;
+
+        self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
+
+        if let Some(ref manifest_entry) = self.manifest_entry {
+            Ok(manifest_entry.files())
+        } else {
+            panic!(
+                "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it"
+            )
+        }
+    }
+}
--- a/rust/hg-core/src/operations/mod.rs	Wed Sep 09 12:12:11 2020 +0200
+++ b/rust/hg-core/src/operations/mod.rs	Fri Sep 18 16:52:16 2020 +0200
@@ -14,6 +14,10 @@
     ListDirstateTrackedFiles, ListDirstateTrackedFilesError,
     ListDirstateTrackedFilesErrorKind,
 };
+pub use list_tracked_files::{
+    ListRevTrackedFiles, ListRevTrackedFilesError,
+    ListRevTrackedFilesErrorKind,
+};
 
 // TODO add an `Operation` trait when GAT have landed (rust #44265):
 // there is no way to currently define a trait which can both return
--- a/rust/hg-core/src/revlog/index.rs	Wed Sep 09 12:12:11 2020 +0200
+++ b/rust/hg-core/src/revlog/index.rs	Fri Sep 18 16:52:16 2020 +0200
@@ -44,6 +44,20 @@
         }
     }
 
+    /// Return number of entries of the revlog index.
+    pub fn len(&self) -> usize {
+        if let Some(offsets) = &self.offsets {
+            offsets.len()
+        } else {
+            self.bytes.len() / INDEX_ENTRY_SIZE
+        }
+    }
+
+    /// Returns `true` if the `Index` has zero `entries`.
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
     /// Return the index entry corresponding to the given revision if it
     /// exists.
     pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
--- a/rust/hg-core/src/revlog/revlog.rs	Wed Sep 09 12:12:11 2020 +0200
+++ b/rust/hg-core/src/revlog/revlog.rs	Fri Sep 18 16:52:16 2020 +0200
@@ -188,7 +188,7 @@
     }
 
     /// Return the revlog index.
-    fn index(&self) -> Index {
+    pub fn index(&self) -> Index {
         let is_inline = self.data_bytes.is_none();
         Index::new(&self.index_bytes, is_inline)
     }