Mercurial > hg
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 } |