diff --git a/rust/hg-core/src/operations/list_tracked_files.rs b/rust/hg-core/src/operations/list_tracked_files.rs --- a/rust/hg-core/src/operations/list_tracked_files.rs +++ b/rust/hg-core/src/operations/list_tracked_files.rs @@ -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 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 for ListRevTrackedFilesError { + fn from(kind: ListRevTrackedFilesErrorKind) -> Self { + ListRevTrackedFilesError { kind } + } +} + +impl From 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, +} + +impl<'a> ListRevTrackedFiles<'a> { + pub fn new( + root: &PathBuf, + rev: &'a str, + ) -> Result { + let changelog = Changelog::open(&root)?; + let manifest = Manifest::open(&root)?; + + Ok(Self { + rev, + changelog, + manifest, + manifest_entry: None, + }) + } + + pub fn run( + &mut self, + ) -> Result, ListRevTrackedFilesError> { + let changelog_entry = match self.rev.parse::() { + 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" + ) + } + } +} diff --git a/rust/hg-core/src/operations/mod.rs b/rust/hg-core/src/operations/mod.rs --- a/rust/hg-core/src/operations/mod.rs +++ b/rust/hg-core/src/operations/mod.rs @@ -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 diff --git a/rust/hg-core/src/revlog/index.rs b/rust/hg-core/src/revlog/index.rs --- a/rust/hg-core/src/revlog/index.rs +++ b/rust/hg-core/src/revlog/index.rs @@ -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 { diff --git a/rust/hg-core/src/revlog/revlog.rs b/rust/hg-core/src/revlog/revlog.rs --- a/rust/hg-core/src/revlog/revlog.rs +++ b/rust/hg-core/src/revlog/revlog.rs @@ -85,6 +85,11 @@ self.index().len() } + /// Returns `true` if the `Revlog` has zero `entries`. + pub fn is_empty(&self) -> bool { + self.index().is_empty() + } + /// Return the full data associated to a node. #[timed] pub fn get_node_rev(&self, node: &[u8]) -> Result { @@ -182,7 +187,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) }