rust/hg-core/src/operations/cat.rs
changeset 46744 b1f2c2b336ec
parent 46443 43d63979a75e
child 47957 d44740725b95
--- a/rust/hg-core/src/operations/cat.rs	Mon Mar 08 19:07:29 2021 +0100
+++ b/rust/hg-core/src/operations/cat.rs	Wed Mar 03 16:40:03 2021 +0100
@@ -17,30 +17,49 @@
 use crate::utils::files::get_path_from_bytes;
 use crate::utils::hg_path::{HgPath, HgPathBuf};
 
+pub struct CatOutput {
+    /// Whether any file in the manifest matched the paths given as CLI
+    /// arguments
+    pub found_any: bool,
+    /// The contents of matching files, in manifest order
+    pub concatenated: Vec<u8>,
+    /// Which of the CLI arguments did not match any manifest file
+    pub missing: Vec<HgPathBuf>,
+    /// The node ID that the given revset was resolved to
+    pub node: Node,
+}
+
 const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n'];
 
-/// List files under Mercurial control at a given revision.
+/// Output the given revision of files
 ///
 /// * `root`: Repository root
 /// * `rev`: The revision to cat the files from.
 /// * `files`: The files to output.
-pub fn cat(
+pub fn cat<'a>(
     repo: &Repo,
     revset: &str,
-    files: &[HgPathBuf],
-) -> Result<Vec<u8>, RevlogError> {
+    files: &'a [HgPathBuf],
+) -> Result<CatOutput, RevlogError> {
     let rev = crate::revset::resolve_single(revset, repo)?;
     let changelog = Changelog::open(repo)?;
     let manifest = Manifest::open(repo)?;
     let changelog_entry = changelog.get_rev(rev)?;
+    let node = *changelog
+        .node_from_rev(rev)
+        .expect("should succeed when changelog.get_rev did");
     let manifest_node =
         Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?;
     let manifest_entry = manifest.get_node(manifest_node.into())?;
     let mut bytes = vec![];
+    let mut matched = vec![false; files.len()];
+    let mut found_any = false;
 
     for (manifest_file, node_bytes) in manifest_entry.files_with_nodes() {
-        for cat_file in files.iter() {
+        for (cat_file, is_matched) in files.iter().zip(&mut matched) {
             if cat_file.as_bytes() == manifest_file.as_bytes() {
+                *is_matched = true;
+                found_any = true;
                 let index_path = store_path(manifest_file, b".i");
                 let data_path = store_path(manifest_file, b".d");
 
@@ -65,7 +84,18 @@
         }
     }
 
-    Ok(bytes)
+    let missing: Vec<_> = files
+        .iter()
+        .zip(&matched)
+        .filter(|pair| !*pair.1)
+        .map(|pair| pair.0.clone())
+        .collect();
+    Ok(CatOutput {
+        found_any,
+        concatenated: bytes,
+        missing,
+        node,
+    })
 }
 
 fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf {