comparison rust/hg-core/src/operations/cat.rs @ 48237:027ebad952ac

rhg: internally, return a structured representation from hg cat The purpose of this change is to make it possible to support limited templating in `hg cat`, so we could print separators between files etc. The templating itself is not implemented yet, so this functionality is unused in `rhg cat`. However, in our fork of hg we're implementing a slightly different command `hg jscat` which makes use of this. So accepting this change will let us minimize the size of the patch we're maintaining on our side. Differential Revision: https://phab.mercurial-scm.org/D11679
author Arseniy Alekseyev <aalekseyev@janestreet.com>
date Fri, 15 Oct 2021 14:05:20 +0100
parents 1837663ac216
children 10c32e1b892a
comparison
equal deleted inserted replaced
48236:f8dc78716ad2 48237:027ebad952ac
8 use crate::repo::Repo; 8 use crate::repo::Repo;
9 use crate::revlog::revlog::RevlogError; 9 use crate::revlog::revlog::RevlogError;
10 use crate::revlog::Node; 10 use crate::revlog::Node;
11 11
12 use crate::utils::hg_path::HgPath; 12 use crate::utils::hg_path::HgPath;
13 use crate::utils::hg_path::HgPathBuf;
14 13
15 use itertools::put_back; 14 use itertools::put_back;
16 use itertools::PutBack; 15 use itertools::PutBack;
17 use std::cmp::Ordering; 16 use std::cmp::Ordering;
18 17
19 pub struct CatOutput { 18 pub struct CatOutput<'a> {
20 /// Whether any file in the manifest matched the paths given as CLI 19 /// Whether any file in the manifest matched the paths given as CLI
21 /// arguments 20 /// arguments
22 pub found_any: bool, 21 pub found_any: bool,
23 /// The contents of matching files, in manifest order 22 /// The contents of matching files, in manifest order
24 pub concatenated: Vec<u8>, 23 pub results: Vec<(&'a HgPath, Vec<u8>)>,
25 /// Which of the CLI arguments did not match any manifest file 24 /// Which of the CLI arguments did not match any manifest file
26 pub missing: Vec<HgPathBuf>, 25 pub missing: Vec<&'a HgPath>,
27 /// The node ID that the given revset was resolved to 26 /// The node ID that the given revset was resolved to
28 pub node: Node, 27 pub node: Node,
29 } 28 }
30 29
31 // Find an item in an iterator over a sorted collection. 30 // Find an item in an iterator over a sorted collection.
32 fn find_item<'a, 'b, 'c, D, I: Iterator<Item = (&'a HgPath, D)>>( 31 fn find_item<'a, 'b, 'c, D, I: Iterator<Item = (&'a HgPath, D)>>(
33 i: &mut PutBack<I>, 32 i: &mut PutBack<I>,
34 needle: &'b HgPath, 33 needle: &'b HgPath,
35 ) -> Option<I::Item> { 34 ) -> Option<D> {
36 loop { 35 loop {
37 match i.next() { 36 match i.next() {
38 None => return None, 37 None => return None,
39 Some(val) => match needle.as_bytes().cmp(val.0.as_bytes()) { 38 Some(val) => match needle.as_bytes().cmp(val.0.as_bytes()) {
40 Ordering::Less => { 39 Ordering::Less => {
41 i.put_back(val); 40 i.put_back(val);
42 return None; 41 return None;
43 } 42 }
44 Ordering::Greater => continue, 43 Ordering::Greater => continue,
45 Ordering::Equal => return Some(val), 44 Ordering::Equal => return Some(val.1),
46 }, 45 },
47 } 46 }
48 } 47 }
49 } 48 }
50 49
51 fn find_files_in_manifest< 50 fn find_files_in_manifest<
52 'a, 51 'manifest,
53 'b, 52 'query,
54 D, 53 Data,
55 I: Iterator<Item = (&'a HgPath, D)>, 54 Manifest: Iterator<Item = (&'manifest HgPath, Data)>,
56 J: Iterator<Item = &'b HgPath>, 55 Query: Iterator<Item = &'query HgPath>,
57 >( 56 >(
58 manifest: I, 57 manifest: Manifest,
59 files: J, 58 query: Query,
60 ) -> (Vec<(&'a HgPath, D)>, Vec<&'b HgPath>) { 59 ) -> (Vec<(&'query HgPath, Data)>, Vec<&'query HgPath>) {
61 let mut manifest = put_back(manifest); 60 let mut manifest = put_back(manifest);
62 let mut res = vec![]; 61 let mut res = vec![];
63 let mut missing = vec![]; 62 let mut missing = vec![];
64 63
65 for file in files { 64 for file in query {
66 match find_item(&mut manifest, file) { 65 match find_item(&mut manifest, file) {
67 None => missing.push(file), 66 None => missing.push(file),
68 Some(item) => res.push(item), 67 Some(item) => res.push((file, item)),
69 } 68 }
70 } 69 }
71 return (res, missing); 70 return (res, missing);
72 } 71 }
73 72
77 /// * `rev`: The revision to cat the files from. 76 /// * `rev`: The revision to cat the files from.
78 /// * `files`: The files to output. 77 /// * `files`: The files to output.
79 pub fn cat<'a>( 78 pub fn cat<'a>(
80 repo: &Repo, 79 repo: &Repo,
81 revset: &str, 80 revset: &str,
82 mut files: Vec<HgPathBuf>, 81 mut files: Vec<&'a HgPath>,
83 ) -> Result<CatOutput, RevlogError> { 82 ) -> Result<CatOutput<'a>, RevlogError> {
84 let rev = crate::revset::resolve_single(revset, repo)?; 83 let rev = crate::revset::resolve_single(revset, repo)?;
85 let manifest = repo.manifest_for_rev(rev)?; 84 let manifest = repo.manifest_for_rev(rev)?;
86 let node = *repo 85 let node = *repo
87 .changelog()? 86 .changelog()?
88 .node_from_rev(rev) 87 .node_from_rev(rev)
89 .expect("should succeed when repo.manifest did"); 88 .expect("should succeed when repo.manifest did");
90 let mut bytes: Vec<u8> = vec![]; 89 let mut results: Vec<(&'a HgPath, Vec<u8>)> = vec![];
91 let mut found_any = false; 90 let mut found_any = false;
92 91
93 files.sort_unstable(); 92 files.sort_unstable();
94 93
95 let (found, missing) = find_files_in_manifest( 94 let (found, missing) = find_files_in_manifest(
96 manifest.files_with_nodes(), 95 manifest.files_with_nodes(),
97 files.iter().map(|f| f.as_ref()), 96 files.into_iter().map(|f| f.as_ref()),
98 ); 97 );
99 98
100 for (manifest_file, node_bytes) in found { 99 for (file_path, node_bytes) in found {
101 found_any = true; 100 found_any = true;
102 let file_log = repo.filelog(manifest_file)?; 101 let file_log = repo.filelog(file_path)?;
103 let file_node = Node::from_hex_for_repo(node_bytes)?; 102 let file_node = Node::from_hex_for_repo(node_bytes)?;
104 bytes.extend(file_log.data_for_node(file_node)?.data()?); 103 results.push((
104 file_path,
105 file_log.data_for_node(file_node)?.into_data()?,
106 ));
105 } 107 }
106 108
107 let missing: Vec<HgPathBuf> =
108 missing.iter().map(|file| (*file).to_owned()).collect();
109 Ok(CatOutput { 109 Ok(CatOutput {
110 found_any, 110 found_any,
111 concatenated: bytes, 111 results,
112 missing, 112 missing,
113 node, 113 node,
114 }) 114 })
115 } 115 }