##// END OF EJS Templates
rust: replace most "operation" structs with functions...
Simon Sapin -
r46751:dca9cb99 default
parent child Browse files
Show More
@@ -1,176 +1,139 b''
1 1 // list_tracked_files.rs
2 2 //
3 3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use std::convert::From;
9 9 use std::path::{Path, PathBuf};
10 10
11 11 use crate::revlog::changelog::Changelog;
12 use crate::revlog::manifest::{Manifest, ManifestEntry};
12 use crate::revlog::manifest::Manifest;
13 13 use crate::revlog::path_encode::path_encode;
14 14 use crate::revlog::revlog::Revlog;
15 15 use crate::revlog::revlog::RevlogError;
16 16 use crate::revlog::Node;
17 17 use crate::revlog::NodePrefix;
18 18 use crate::revlog::Revision;
19 19 use crate::utils::files::get_path_from_bytes;
20 20 use crate::utils::hg_path::{HgPath, HgPathBuf};
21 21
22 22 const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n'];
23 23
24 24 /// Kind of error encountered by `CatRev`
25 25 #[derive(Debug)]
26 26 pub enum CatRevErrorKind {
27 27 /// Error when reading a `revlog` file.
28 28 IoError(std::io::Error),
29 29 /// The revision has not been found.
30 30 InvalidRevision,
31 31 /// Found more than one revision whose ID match the requested prefix
32 32 AmbiguousPrefix,
33 33 /// A `revlog` file is corrupted.
34 34 CorruptedRevlog,
35 35 /// The `revlog` format version is not supported.
36 36 UnsuportedRevlogVersion(u16),
37 37 /// The `revlog` data format is not supported.
38 38 UnknowRevlogDataFormat(u8),
39 39 }
40 40
41 41 /// A `CatRev` error
42 42 #[derive(Debug)]
43 43 pub struct CatRevError {
44 44 /// Kind of error encountered by `CatRev`
45 45 pub kind: CatRevErrorKind,
46 46 }
47 47
48 48 impl From<CatRevErrorKind> for CatRevError {
49 49 fn from(kind: CatRevErrorKind) -> Self {
50 50 CatRevError { kind }
51 51 }
52 52 }
53 53
54 54 impl From<RevlogError> for CatRevError {
55 55 fn from(err: RevlogError) -> Self {
56 56 match err {
57 57 RevlogError::IoError(err) => CatRevErrorKind::IoError(err),
58 58 RevlogError::UnsuportedVersion(version) => {
59 59 CatRevErrorKind::UnsuportedRevlogVersion(version)
60 60 }
61 61 RevlogError::InvalidRevision => CatRevErrorKind::InvalidRevision,
62 62 RevlogError::AmbiguousPrefix => CatRevErrorKind::AmbiguousPrefix,
63 63 RevlogError::Corrupted => CatRevErrorKind::CorruptedRevlog,
64 64 RevlogError::UnknowDataFormat(format) => {
65 65 CatRevErrorKind::UnknowRevlogDataFormat(format)
66 66 }
67 67 }
68 68 .into()
69 69 }
70 70 }
71 71
72 72 /// List files under Mercurial control at a given revision.
73 pub struct CatRev<'a> {
74 root: &'a Path,
75 /// The revision to cat the files from.
76 rev: &'a str,
77 /// The files to output.
78 files: &'a [HgPathBuf],
79 /// The changelog file
80 changelog: Changelog,
81 /// The manifest file
82 manifest: Manifest,
83 /// The manifest entry corresponding to the revision.
84 ///
85 /// Used to hold the owner of the returned references.
86 manifest_entry: Option<ManifestEntry>,
87 }
73 ///
74 /// * `root`: Repository root
75 /// * `rev`: The revision to cat the files from.
76 /// * `files`: The files to output.
77 pub fn cat(
78 root: &Path,
79 rev: &str,
80 files: &[HgPathBuf],
81 ) -> Result<Vec<u8>, CatRevError> {
82 let changelog = Changelog::open(&root)?;
83 let manifest = Manifest::open(&root)?;
84
85 let changelog_entry = match rev.parse::<Revision>() {
86 Ok(rev) => changelog.get_rev(rev)?,
87 _ => {
88 let changelog_node = NodePrefix::from_hex(&rev)
89 .map_err(|_| CatRevErrorKind::InvalidRevision)?;
90 changelog.get_node(changelog_node.borrow())?
91 }
92 };
93 let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
94 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
95
96 let manifest_entry = manifest.get_node((&manifest_node).into())?;
97 let mut bytes = vec![];
88 98
89 impl<'a> CatRev<'a> {
90 pub fn new(
91 root: &'a Path,
92 rev: &'a str,
93 files: &'a [HgPathBuf],
94 ) -> Result<Self, CatRevError> {
95 let changelog = Changelog::open(&root)?;
96 let manifest = Manifest::open(&root)?;
97 let manifest_entry = None;
99 for (manifest_file, node_bytes) in manifest_entry.files_with_nodes() {
100 for cat_file in files.iter() {
101 if cat_file.as_bytes() == manifest_file.as_bytes() {
102 let index_path = store_path(root, manifest_file, b".i");
103 let data_path = store_path(root, manifest_file, b".d");
98 104
99 Ok(Self {
100 root,
101 rev,
102 files,
103 changelog,
104 manifest,
105 manifest_entry,
106 })
105 let file_log = Revlog::open(&index_path, Some(&data_path))?;
106 let file_node = Node::from_hex(node_bytes)
107 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
108 let file_rev = file_log.get_node_rev((&file_node).into())?;
109 let data = file_log.get_rev_data(file_rev)?;
110 if data.starts_with(&METADATA_DELIMITER) {
111 let end_delimiter_position = data
112 [METADATA_DELIMITER.len()..]
113 .windows(METADATA_DELIMITER.len())
114 .position(|bytes| bytes == METADATA_DELIMITER);
115 if let Some(position) = end_delimiter_position {
116 let offset = METADATA_DELIMITER.len() * 2;
117 bytes.extend(data[position + offset..].iter());
118 }
119 } else {
120 bytes.extend(data);
121 }
122 }
123 }
107 124 }
108 125
109 pub fn run(&mut self) -> Result<Vec<u8>, CatRevError> {
110 let changelog_entry = match self.rev.parse::<Revision>() {
111 Ok(rev) => self.changelog.get_rev(rev)?,
112 _ => {
113 let changelog_node = NodePrefix::from_hex(&self.rev)
114 .map_err(|_| CatRevErrorKind::InvalidRevision)?;
115 self.changelog.get_node(changelog_node.borrow())?
116 }
117 };
118 let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
119 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
120
121 self.manifest_entry =
122 Some(self.manifest.get_node((&manifest_node).into())?);
123 if let Some(ref manifest_entry) = self.manifest_entry {
124 let mut bytes = vec![];
125
126 for (manifest_file, node_bytes) in
127 manifest_entry.files_with_nodes()
128 {
129 for cat_file in self.files.iter() {
130 if cat_file.as_bytes() == manifest_file.as_bytes() {
131 let index_path =
132 store_path(self.root, manifest_file, b".i");
133 let data_path =
134 store_path(self.root, manifest_file, b".d");
135
136 let file_log =
137 Revlog::open(&index_path, Some(&data_path))?;
138 let file_node = Node::from_hex(node_bytes)
139 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
140 let file_rev =
141 file_log.get_node_rev((&file_node).into())?;
142 let data = file_log.get_rev_data(file_rev)?;
143 if data.starts_with(&METADATA_DELIMITER) {
144 let end_delimiter_position = data
145 [METADATA_DELIMITER.len()..]
146 .windows(METADATA_DELIMITER.len())
147 .position(|bytes| bytes == METADATA_DELIMITER);
148 if let Some(position) = end_delimiter_position {
149 let offset = METADATA_DELIMITER.len() * 2;
150 bytes.extend(data[position + offset..].iter());
151 }
152 } else {
153 bytes.extend(data);
154 }
155 }
156 }
157 }
158
159 Ok(bytes)
160 } else {
161 unreachable!("manifest_entry should have been stored");
162 }
163 }
126 Ok(bytes)
164 127 }
165 128
166 129 fn store_path(root: &Path, hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
167 130 let encoded_bytes =
168 131 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
169 132 [
170 133 root,
171 134 &Path::new(".hg/store/"),
172 135 get_path_from_bytes(&encoded_bytes),
173 136 ]
174 137 .iter()
175 138 .collect()
176 139 }
@@ -1,120 +1,103 b''
1 1 // debugdata.rs
2 2 //
3 3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 use super::find_root;
8 use std::path::Path;
9
9 10 use crate::revlog::revlog::{Revlog, RevlogError};
10 11 use crate::revlog::NodePrefix;
11 12 use crate::revlog::Revision;
12 13
13 14 /// Kind of data to debug
14 15 #[derive(Debug, Copy, Clone)]
15 16 pub enum DebugDataKind {
16 17 Changelog,
17 18 Manifest,
18 19 }
19 20
20 21 /// Kind of error encountered by DebugData
21 22 #[derive(Debug)]
22 23 pub enum DebugDataErrorKind {
23 FindRootError(find_root::FindRootError),
24 24 /// Error when reading a `revlog` file.
25 25 IoError(std::io::Error),
26 26 /// The revision has not been found.
27 27 InvalidRevision,
28 28 /// Found more than one revision whose ID match the requested prefix
29 29 AmbiguousPrefix,
30 30 /// A `revlog` file is corrupted.
31 31 CorruptedRevlog,
32 32 /// The `revlog` format version is not supported.
33 33 UnsuportedRevlogVersion(u16),
34 34 /// The `revlog` data format is not supported.
35 35 UnknowRevlogDataFormat(u8),
36 36 }
37 37
38 38 /// A DebugData error
39 39 #[derive(Debug)]
40 40 pub struct DebugDataError {
41 41 /// Kind of error encountered by DebugData
42 42 pub kind: DebugDataErrorKind,
43 43 }
44 44
45 45 impl From<DebugDataErrorKind> for DebugDataError {
46 46 fn from(kind: DebugDataErrorKind) -> Self {
47 47 DebugDataError { kind }
48 48 }
49 49 }
50 50
51 impl From<find_root::FindRootError> for DebugDataError {
52 fn from(err: find_root::FindRootError) -> Self {
53 let kind = DebugDataErrorKind::FindRootError(err);
54 DebugDataError { kind }
55 }
56 }
57
58 51 impl From<std::io::Error> for DebugDataError {
59 52 fn from(err: std::io::Error) -> Self {
60 53 let kind = DebugDataErrorKind::IoError(err);
61 54 DebugDataError { kind }
62 55 }
63 56 }
64 57
65 58 impl From<RevlogError> for DebugDataError {
66 59 fn from(err: RevlogError) -> Self {
67 60 match err {
68 61 RevlogError::IoError(err) => DebugDataErrorKind::IoError(err),
69 62 RevlogError::UnsuportedVersion(version) => {
70 63 DebugDataErrorKind::UnsuportedRevlogVersion(version)
71 64 }
72 65 RevlogError::InvalidRevision => {
73 66 DebugDataErrorKind::InvalidRevision
74 67 }
75 68 RevlogError::AmbiguousPrefix => {
76 69 DebugDataErrorKind::AmbiguousPrefix
77 70 }
78 71 RevlogError::Corrupted => DebugDataErrorKind::CorruptedRevlog,
79 72 RevlogError::UnknowDataFormat(format) => {
80 73 DebugDataErrorKind::UnknowRevlogDataFormat(format)
81 74 }
82 75 }
83 76 .into()
84 77 }
85 78 }
86 79
87 80 /// Dump the contents data of a revision.
88 pub struct DebugData<'a> {
89 /// Revision or hash of the revision.
90 rev: &'a str,
91 /// Kind of data to debug.
81 pub fn debug_data(
82 root: &Path,
83 rev: &str,
92 84 kind: DebugDataKind,
93 }
94
95 impl<'a> DebugData<'a> {
96 pub fn new(rev: &'a str, kind: DebugDataKind) -> Self {
97 DebugData { rev, kind }
98 }
85 ) -> Result<Vec<u8>, DebugDataError> {
86 let index_file = match kind {
87 DebugDataKind::Changelog => root.join(".hg/store/00changelog.i"),
88 DebugDataKind::Manifest => root.join(".hg/store/00manifest.i"),
89 };
90 let revlog = Revlog::open(&index_file, None)?;
99 91
100 pub fn run(&mut self) -> Result<Vec<u8>, DebugDataError> {
101 let root = find_root::FindRoot::new().run()?;
102 let index_file = match self.kind {
103 DebugDataKind::Changelog => root.join(".hg/store/00changelog.i"),
104 DebugDataKind::Manifest => root.join(".hg/store/00manifest.i"),
105 };
106 let revlog = Revlog::open(&index_file, None)?;
92 let data = match rev.parse::<Revision>() {
93 Ok(rev) => revlog.get_rev_data(rev)?,
94 _ => {
95 let node = NodePrefix::from_hex(&rev)
96 .map_err(|_| DebugDataErrorKind::InvalidRevision)?;
97 let rev = revlog.get_node_rev(node.borrow())?;
98 revlog.get_rev_data(rev)?
99 }
100 };
107 101
108 let data = match self.rev.parse::<Revision>() {
109 Ok(rev) => revlog.get_rev_data(rev)?,
110 _ => {
111 let node = NodePrefix::from_hex(&self.rev)
112 .map_err(|_| DebugDataErrorKind::InvalidRevision)?;
113 let rev = revlog.get_node_rev(node.borrow())?;
114 revlog.get_rev_data(rev)?
115 }
116 };
117
118 Ok(data)
119 }
102 Ok(data)
120 103 }
@@ -1,119 +1,100 b''
1 1 use std::fmt;
2 2 use std::path::{Path, PathBuf};
3 3
4 4 /// Kind of error encoutered by FindRoot
5 5 #[derive(Debug)]
6 6 pub enum FindRootErrorKind {
7 7 /// Root of the repository has not been found
8 8 /// Contains the current directory used by FindRoot
9 9 RootNotFound(PathBuf),
10 10 /// The current directory does not exists or permissions are insufficient
11 11 /// to get access to it
12 12 GetCurrentDirError(std::io::Error),
13 13 }
14 14
15 15 /// A FindRoot error
16 16 #[derive(Debug)]
17 17 pub struct FindRootError {
18 18 /// Kind of error encoutered by FindRoot
19 19 pub kind: FindRootErrorKind,
20 20 }
21 21
22 22 impl std::error::Error for FindRootError {}
23 23
24 24 impl fmt::Display for FindRootError {
25 25 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 26 unimplemented!()
27 27 }
28 28 }
29 29
30 30 /// Find the root of the repository
31 /// by searching for a .hg directory in the current directory and its
31 /// by searching for a .hg directory in the process’ current directory and its
32 32 /// ancestors
33 pub struct FindRoot<'a> {
34 current_dir: Option<&'a Path>,
33 pub fn find_root() -> Result<PathBuf, FindRootError> {
34 let current_dir = std::env::current_dir().map_err(|e| FindRootError {
35 kind: FindRootErrorKind::GetCurrentDirError(e),
36 })?;
37 Ok(find_root_from_path(&current_dir)?.into())
35 38 }
36 39
37 impl<'a> FindRoot<'a> {
38 pub fn new() -> Self {
39 Self { current_dir: None }
40 /// Find the root of the repository
41 /// by searching for a .hg directory in the given directory and its ancestors
42 pub fn find_root_from_path(start: &Path) -> Result<&Path, FindRootError> {
43 if start.join(".hg").exists() {
44 return Ok(start);
40 45 }
41
42 pub fn new_from_path(current_dir: &'a Path) -> Self {
43 Self {
44 current_dir: Some(current_dir),
46 for ancestor in start.ancestors() {
47 if ancestor.join(".hg").exists() {
48 return Ok(ancestor);
45 49 }
46 50 }
47
48 pub fn run(&self) -> Result<PathBuf, FindRootError> {
49 let current_dir = match self.current_dir {
50 None => std::env::current_dir().or_else(|e| {
51 Err(FindRootError {
52 kind: FindRootErrorKind::GetCurrentDirError(e),
53 })
54 })?,
55 Some(path) => path.into(),
56 };
57
58 if current_dir.join(".hg").exists() {
59 return Ok(current_dir);
60 }
61 let ancestors = current_dir.ancestors();
62 for parent in ancestors {
63 if parent.join(".hg").exists() {
64 return Ok(parent.into());
65 }
66 }
67 Err(FindRootError {
68 kind: FindRootErrorKind::RootNotFound(current_dir.to_path_buf()),
69 })
70 }
51 Err(FindRootError {
52 kind: FindRootErrorKind::RootNotFound(start.into()),
53 })
71 54 }
72 55
73 56 #[cfg(test)]
74 57 mod tests {
75 58 use super::*;
76 59 use std::fs;
77 60 use tempfile;
78 61
79 62 #[test]
80 63 fn dot_hg_not_found() {
81 64 let tmp_dir = tempfile::tempdir().unwrap();
82 65 let path = tmp_dir.path();
83 66
84 let err = FindRoot::new_from_path(&path).run().unwrap_err();
67 let err = find_root_from_path(&path).unwrap_err();
85 68
86 69 // TODO do something better
87 70 assert!(match err {
88 71 FindRootError { kind } => match kind {
89 72 FindRootErrorKind::RootNotFound(p) => p == path.to_path_buf(),
90 73 _ => false,
91 74 },
92 75 })
93 76 }
94 77
95 78 #[test]
96 79 fn dot_hg_in_current_path() {
97 80 let tmp_dir = tempfile::tempdir().unwrap();
98 81 let root = tmp_dir.path();
99 82 fs::create_dir_all(root.join(".hg")).unwrap();
100 83
101 let result = FindRoot::new_from_path(&root).run().unwrap();
84 let result = find_root_from_path(&root).unwrap();
102 85
103 86 assert_eq!(result, root)
104 87 }
105 88
106 89 #[test]
107 90 fn dot_hg_in_parent() {
108 91 let tmp_dir = tempfile::tempdir().unwrap();
109 92 let root = tmp_dir.path();
110 93 fs::create_dir_all(root.join(".hg")).unwrap();
111 94
112 let result =
113 FindRoot::new_from_path(&root.join("some/nested/directory"))
114 .run()
115 .unwrap();
95 let directory = root.join("some/nested/directory");
96 let result = find_root_from_path(&directory).unwrap();
116 97
117 98 assert_eq!(result, root)
118 99 }
119 100 } /* tests */
@@ -1,194 +1,167 b''
1 1 // list_tracked_files.rs
2 2 //
3 3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use crate::dirstate::parsers::parse_dirstate;
9 9 use crate::revlog::changelog::Changelog;
10 10 use crate::revlog::manifest::{Manifest, ManifestEntry};
11 11 use crate::revlog::node::{Node, NodePrefix};
12 12 use crate::revlog::revlog::RevlogError;
13 13 use crate::revlog::Revision;
14 14 use crate::utils::hg_path::HgPath;
15 15 use crate::{DirstateParseError, EntryState};
16 16 use rayon::prelude::*;
17 17 use std::convert::From;
18 18 use std::fs;
19 19 use std::path::Path;
20 20
21 21 /// Kind of error encountered by `ListDirstateTrackedFiles`
22 22 #[derive(Debug)]
23 23 pub enum ListDirstateTrackedFilesErrorKind {
24 24 /// Error when reading the `dirstate` file
25 25 IoError(std::io::Error),
26 26 /// Error when parsing the `dirstate` file
27 27 ParseError(DirstateParseError),
28 28 }
29 29
30 30 /// A `ListDirstateTrackedFiles` error
31 31 #[derive(Debug)]
32 32 pub struct ListDirstateTrackedFilesError {
33 33 /// Kind of error encountered by `ListDirstateTrackedFiles`
34 34 pub kind: ListDirstateTrackedFilesErrorKind,
35 35 }
36 36
37 37 impl From<ListDirstateTrackedFilesErrorKind>
38 38 for ListDirstateTrackedFilesError
39 39 {
40 40 fn from(kind: ListDirstateTrackedFilesErrorKind) -> Self {
41 41 ListDirstateTrackedFilesError { kind }
42 42 }
43 43 }
44 44
45 45 impl From<std::io::Error> for ListDirstateTrackedFilesError {
46 46 fn from(err: std::io::Error) -> Self {
47 47 let kind = ListDirstateTrackedFilesErrorKind::IoError(err);
48 48 ListDirstateTrackedFilesError { kind }
49 49 }
50 50 }
51 51
52 52 /// List files under Mercurial control in the working directory
53 53 /// by reading the dirstate
54 pub struct ListDirstateTrackedFiles {
54 pub struct Dirstate {
55 55 /// The `dirstate` content.
56 56 content: Vec<u8>,
57 57 }
58 58
59 impl ListDirstateTrackedFiles {
59 impl Dirstate {
60 60 pub fn new(root: &Path) -> Result<Self, ListDirstateTrackedFilesError> {
61 61 let dirstate = root.join(".hg/dirstate");
62 62 let content = fs::read(&dirstate)?;
63 63 Ok(Self { content })
64 64 }
65 65
66 pub fn run(
67 &mut self,
66 pub fn tracked_files(
67 &self,
68 68 ) -> Result<Vec<&HgPath>, ListDirstateTrackedFilesError> {
69 69 let (_, entries, _) = parse_dirstate(&self.content)
70 70 .map_err(ListDirstateTrackedFilesErrorKind::ParseError)?;
71 71 let mut files: Vec<&HgPath> = entries
72 72 .into_iter()
73 73 .filter_map(|(path, entry)| match entry.state {
74 74 EntryState::Removed => None,
75 75 _ => Some(path),
76 76 })
77 77 .collect();
78 78 files.par_sort_unstable();
79 79 Ok(files)
80 80 }
81 81 }
82 82
83 83 /// Kind of error encountered by `ListRevTrackedFiles`
84 84 #[derive(Debug)]
85 85 pub enum ListRevTrackedFilesErrorKind {
86 86 /// Error when reading a `revlog` file.
87 87 IoError(std::io::Error),
88 88 /// The revision has not been found.
89 89 InvalidRevision,
90 90 /// Found more than one revision whose ID match the requested prefix
91 91 AmbiguousPrefix,
92 92 /// A `revlog` file is corrupted.
93 93 CorruptedRevlog,
94 94 /// The `revlog` format version is not supported.
95 95 UnsuportedRevlogVersion(u16),
96 96 /// The `revlog` data format is not supported.
97 97 UnknowRevlogDataFormat(u8),
98 98 }
99 99
100 100 /// A `ListRevTrackedFiles` error
101 101 #[derive(Debug)]
102 102 pub struct ListRevTrackedFilesError {
103 103 /// Kind of error encountered by `ListRevTrackedFiles`
104 104 pub kind: ListRevTrackedFilesErrorKind,
105 105 }
106 106
107 107 impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError {
108 108 fn from(kind: ListRevTrackedFilesErrorKind) -> Self {
109 109 ListRevTrackedFilesError { kind }
110 110 }
111 111 }
112 112
113 113 impl From<RevlogError> for ListRevTrackedFilesError {
114 114 fn from(err: RevlogError) -> Self {
115 115 match err {
116 116 RevlogError::IoError(err) => {
117 117 ListRevTrackedFilesErrorKind::IoError(err)
118 118 }
119 119 RevlogError::UnsuportedVersion(version) => {
120 120 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version)
121 121 }
122 122 RevlogError::InvalidRevision => {
123 123 ListRevTrackedFilesErrorKind::InvalidRevision
124 124 }
125 125 RevlogError::AmbiguousPrefix => {
126 126 ListRevTrackedFilesErrorKind::AmbiguousPrefix
127 127 }
128 128 RevlogError::Corrupted => {
129 129 ListRevTrackedFilesErrorKind::CorruptedRevlog
130 130 }
131 131 RevlogError::UnknowDataFormat(format) => {
132 132 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format)
133 133 }
134 134 }
135 135 .into()
136 136 }
137 137 }
138 138
139 139 /// List files under Mercurial control at a given revision.
140 pub struct ListRevTrackedFiles<'a> {
141 /// The revision to list the files from.
142 rev: &'a str,
143 /// The changelog file
144 changelog: Changelog,
145 /// The manifest file
146 manifest: Manifest,
147 /// The manifest entry corresponding to the revision.
148 ///
149 /// Used to hold the owner of the returned references.
150 manifest_entry: Option<ManifestEntry>,
140 pub fn list_rev_tracked_files(
141 root: &Path,
142 rev: &str,
143 ) -> Result<FilesForRev, ListRevTrackedFilesError> {
144 let changelog = Changelog::open(root)?;
145 let manifest = Manifest::open(root)?;
146
147 let changelog_entry = match rev.parse::<Revision>() {
148 Ok(rev) => changelog.get_rev(rev)?,
149 _ => {
150 let changelog_node = NodePrefix::from_hex(&rev)
151 .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?;
152 changelog.get_node(changelog_node.borrow())?
153 }
154 };
155 let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
156 .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?;
157 let manifest_entry = manifest.get_node((&manifest_node).into())?;
158 Ok(FilesForRev(manifest_entry))
151 159 }
152 160
153 impl<'a> ListRevTrackedFiles<'a> {
154 pub fn new(
155 root: &Path,
156 rev: &'a str,
157 ) -> Result<Self, ListRevTrackedFilesError> {
158 let changelog = Changelog::open(root)?;
159 let manifest = Manifest::open(root)?;
160
161 Ok(Self {
162 rev,
163 changelog,
164 manifest,
165 manifest_entry: None,
166 })
167 }
161 pub struct FilesForRev(ManifestEntry);
168 162
169 pub fn run(
170 &mut self,
171 ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> {
172 let changelog_entry = match self.rev.parse::<Revision>() {
173 Ok(rev) => self.changelog.get_rev(rev)?,
174 _ => {
175 let changelog_node = NodePrefix::from_hex(&self.rev)
176 .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?;
177 self.changelog.get_node(changelog_node.borrow())?
178 }
179 };
180 let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
181 .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?;
182
183 self.manifest_entry =
184 Some(self.manifest.get_node((&manifest_node).into())?);
185
186 if let Some(ref manifest_entry) = self.manifest_entry {
187 Ok(manifest_entry.files())
188 } else {
189 panic!(
190 "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it"
191 )
192 }
163 impl FilesForRev {
164 pub fn iter(&self) -> impl Iterator<Item = &HgPath> {
165 self.0.files()
193 166 }
194 167 }
@@ -1,28 +1,23 b''
1 1 //! A distinction is made between operations and commands.
2 2 //! An operation is what can be done whereas a command is what is exposed by
3 3 //! the cli. A single command can use several operations to achieve its goal.
4 4
5 5 mod cat;
6 6 mod debugdata;
7 7 mod dirstate_status;
8 8 mod find_root;
9 9 mod list_tracked_files;
10 pub use cat::{CatRev, CatRevError, CatRevErrorKind};
10 pub use cat::{cat, CatRevError, CatRevErrorKind};
11 11 pub use debugdata::{
12 DebugData, DebugDataError, DebugDataErrorKind, DebugDataKind,
12 debug_data, DebugDataError, DebugDataErrorKind, DebugDataKind,
13 13 };
14 pub use find_root::{FindRoot, FindRootError, FindRootErrorKind};
15 pub use list_tracked_files::{
16 ListDirstateTrackedFiles, ListDirstateTrackedFilesError,
17 ListDirstateTrackedFilesErrorKind,
14 pub use find_root::{
15 find_root, find_root_from_path, FindRootError, FindRootErrorKind,
18 16 };
19 17 pub use list_tracked_files::{
20 ListRevTrackedFiles, ListRevTrackedFilesError,
18 list_rev_tracked_files, FilesForRev, ListRevTrackedFilesError,
21 19 ListRevTrackedFilesErrorKind,
22 20 };
23
24 // TODO add an `Operation` trait when GAT have landed (rust #44265):
25 // there is no way to currently define a trait which can both return
26 // references to `self` and to passed data, which is what we would need.
27 // Generic Associated Types may fix this and allow us to have a unified
28 // interface.
21 pub use list_tracked_files::{
22 Dirstate, ListDirstateTrackedFilesError, ListDirstateTrackedFilesErrorKind,
23 };
@@ -1,108 +1,106 b''
1 1 use crate::commands::Command;
2 2 use crate::error::{CommandError, CommandErrorKind};
3 3 use crate::ui::utf8_to_local;
4 4 use crate::ui::Ui;
5 use hg::operations::FindRoot;
6 use hg::operations::{CatRev, CatRevError, CatRevErrorKind};
5 use hg::operations::find_root;
6 use hg::operations::{cat, CatRevError, CatRevErrorKind};
7 7 use hg::requirements;
8 8 use hg::utils::hg_path::HgPathBuf;
9 9 use micro_timer::timed;
10 10 use std::convert::TryFrom;
11 11
12 12 pub const HELP_TEXT: &str = "
13 13 Output the current or given revision of files
14 14 ";
15 15
16 16 pub struct CatCommand<'a> {
17 17 rev: Option<&'a str>,
18 18 files: Vec<&'a str>,
19 19 }
20 20
21 21 impl<'a> CatCommand<'a> {
22 22 pub fn new(rev: Option<&'a str>, files: Vec<&'a str>) -> Self {
23 23 Self { rev, files }
24 24 }
25 25
26 26 fn display(&self, ui: &Ui, data: &[u8]) -> Result<(), CommandError> {
27 27 ui.write_stdout(data)?;
28 28 Ok(())
29 29 }
30 30 }
31 31
32 32 impl<'a> Command for CatCommand<'a> {
33 33 #[timed]
34 34 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
35 let root = FindRoot::new().run()?;
35 let root = find_root()?;
36 36 requirements::check(&root)?;
37 37 let cwd = std::env::current_dir()
38 38 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
39 39
40 40 let mut files = vec![];
41 41 for file in self.files.iter() {
42 42 let normalized = cwd.join(&file);
43 43 let stripped = normalized
44 44 .strip_prefix(&root)
45 45 .or(Err(CommandErrorKind::Abort(None)))?;
46 46 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
47 47 .or(Err(CommandErrorKind::Abort(None)))?;
48 48 files.push(hg_file);
49 49 }
50 50
51 51 match self.rev {
52 52 Some(rev) => {
53 let mut operation = CatRev::new(&root, rev, &files)
53 let data = cat(&root, rev, &files)
54 54 .map_err(|e| map_rev_error(rev, e))?;
55 let data =
56 operation.run().map_err(|e| map_rev_error(rev, e))?;
57 55 self.display(ui, &data)
58 56 }
59 57 None => Err(CommandErrorKind::Unimplemented.into()),
60 58 }
61 59 }
62 60 }
63 61
64 62 /// Convert `CatRevErrorKind` to `CommandError`
65 63 fn map_rev_error(rev: &str, err: CatRevError) -> CommandError {
66 64 CommandError {
67 65 kind: match err.kind {
68 66 CatRevErrorKind::IoError(err) => CommandErrorKind::Abort(Some(
69 67 utf8_to_local(&format!("abort: {}\n", err)).into(),
70 68 )),
71 69 CatRevErrorKind::InvalidRevision => CommandErrorKind::Abort(Some(
72 70 utf8_to_local(&format!(
73 71 "abort: invalid revision identifier {}\n",
74 72 rev
75 73 ))
76 74 .into(),
77 75 )),
78 76 CatRevErrorKind::AmbiguousPrefix => CommandErrorKind::Abort(Some(
79 77 utf8_to_local(&format!(
80 78 "abort: ambiguous revision identifier {}\n",
81 79 rev
82 80 ))
83 81 .into(),
84 82 )),
85 83 CatRevErrorKind::UnsuportedRevlogVersion(version) => {
86 84 CommandErrorKind::Abort(Some(
87 85 utf8_to_local(&format!(
88 86 "abort: unsupported revlog version {}\n",
89 87 version
90 88 ))
91 89 .into(),
92 90 ))
93 91 }
94 92 CatRevErrorKind::CorruptedRevlog => CommandErrorKind::Abort(Some(
95 93 "abort: corrupted revlog\n".into(),
96 94 )),
97 95 CatRevErrorKind::UnknowRevlogDataFormat(format) => {
98 96 CommandErrorKind::Abort(Some(
99 97 utf8_to_local(&format!(
100 98 "abort: unknow revlog dataformat {:?}\n",
101 99 format
102 100 ))
103 101 .into(),
104 102 ))
105 103 }
106 104 },
107 105 }
108 106 }
@@ -1,91 +1,91 b''
1 1 use crate::commands::Command;
2 2 use crate::error::{CommandError, CommandErrorKind};
3 3 use crate::ui::utf8_to_local;
4 4 use crate::ui::Ui;
5 use hg::operations::find_root;
5 6 use hg::operations::{
6 DebugData, DebugDataError, DebugDataErrorKind, DebugDataKind,
7 debug_data, DebugDataError, DebugDataErrorKind, DebugDataKind,
7 8 };
8 9 use micro_timer::timed;
9 10
10 11 pub const HELP_TEXT: &str = "
11 12 Dump the contents of a data file revision
12 13 ";
13 14
14 15 pub struct DebugDataCommand<'a> {
15 16 rev: &'a str,
16 17 kind: DebugDataKind,
17 18 }
18 19
19 20 impl<'a> DebugDataCommand<'a> {
20 21 pub fn new(rev: &'a str, kind: DebugDataKind) -> Self {
21 22 DebugDataCommand { rev, kind }
22 23 }
23 24 }
24 25
25 26 impl<'a> Command for DebugDataCommand<'a> {
26 27 #[timed]
27 28 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
28 let mut operation = DebugData::new(self.rev, self.kind);
29 let data =
30 operation.run().map_err(|e| to_command_error(self.rev, e))?;
29 let root = find_root()?;
30 let data = debug_data(&root, self.rev, self.kind)
31 .map_err(|e| to_command_error(self.rev, e))?;
31 32
32 33 let mut stdout = ui.stdout_buffer();
33 34 stdout.write_all(&data)?;
34 35 stdout.flush()?;
35 36
36 37 Ok(())
37 38 }
38 39 }
39 40
40 41 /// Convert operation errors to command errors
41 42 fn to_command_error(rev: &str, err: DebugDataError) -> CommandError {
42 43 match err.kind {
43 DebugDataErrorKind::FindRootError(err) => CommandError::from(err),
44 44 DebugDataErrorKind::IoError(err) => CommandError {
45 45 kind: CommandErrorKind::Abort(Some(
46 46 utf8_to_local(&format!("abort: {}\n", err)).into(),
47 47 )),
48 48 },
49 49 DebugDataErrorKind::InvalidRevision => CommandError {
50 50 kind: CommandErrorKind::Abort(Some(
51 51 utf8_to_local(&format!(
52 52 "abort: invalid revision identifier{}\n",
53 53 rev
54 54 ))
55 55 .into(),
56 56 )),
57 57 },
58 58 DebugDataErrorKind::AmbiguousPrefix => CommandError {
59 59 kind: CommandErrorKind::Abort(Some(
60 60 utf8_to_local(&format!(
61 61 "abort: ambiguous revision identifier{}\n",
62 62 rev
63 63 ))
64 64 .into(),
65 65 )),
66 66 },
67 67 DebugDataErrorKind::UnsuportedRevlogVersion(version) => CommandError {
68 68 kind: CommandErrorKind::Abort(Some(
69 69 utf8_to_local(&format!(
70 70 "abort: unsupported revlog version {}\n",
71 71 version
72 72 ))
73 73 .into(),
74 74 )),
75 75 },
76 76 DebugDataErrorKind::CorruptedRevlog => CommandError {
77 77 kind: CommandErrorKind::Abort(Some(
78 78 "abort: corrupted revlog\n".into(),
79 79 )),
80 80 },
81 81 DebugDataErrorKind::UnknowRevlogDataFormat(format) => CommandError {
82 82 kind: CommandErrorKind::Abort(Some(
83 83 utf8_to_local(&format!(
84 84 "abort: unknow revlog dataformat {:?}\n",
85 85 format
86 86 ))
87 87 .into(),
88 88 )),
89 89 },
90 90 }
91 91 }
@@ -1,30 +1,30 b''
1 1 use crate::commands::Command;
2 2 use crate::error::CommandError;
3 3 use crate::ui::Ui;
4 use hg::operations::FindRoot;
4 use hg::operations::find_root;
5 5 use hg::requirements;
6 6
7 7 pub const HELP_TEXT: &str = "
8 8 Print the current repo requirements.
9 9 ";
10 10
11 11 pub struct DebugRequirementsCommand {}
12 12
13 13 impl DebugRequirementsCommand {
14 14 pub fn new() -> Self {
15 15 DebugRequirementsCommand {}
16 16 }
17 17 }
18 18
19 19 impl Command for DebugRequirementsCommand {
20 20 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
21 let root = FindRoot::new().run()?;
21 let root = find_root()?;
22 22 let mut output = String::new();
23 23 for req in requirements::load(&root)? {
24 24 output.push_str(&req);
25 25 output.push('\n');
26 26 }
27 27 ui.write_stdout(output.as_bytes())?;
28 28 Ok(())
29 29 }
30 30 }
@@ -1,147 +1,144 b''
1 1 use crate::commands::Command;
2 2 use crate::error::{CommandError, CommandErrorKind};
3 3 use crate::ui::utf8_to_local;
4 4 use crate::ui::Ui;
5 use hg::operations::FindRoot;
5 use hg::operations::find_root;
6 6 use hg::operations::{
7 ListDirstateTrackedFiles, ListDirstateTrackedFilesError,
8 ListDirstateTrackedFilesErrorKind,
7 list_rev_tracked_files, ListRevTrackedFilesError,
8 ListRevTrackedFilesErrorKind,
9 9 };
10 10 use hg::operations::{
11 ListRevTrackedFiles, ListRevTrackedFilesError,
12 ListRevTrackedFilesErrorKind,
11 Dirstate, ListDirstateTrackedFilesError, ListDirstateTrackedFilesErrorKind,
13 12 };
14 13 use hg::requirements;
15 14 use hg::utils::files::{get_bytes_from_path, relativize_path};
16 15 use hg::utils::hg_path::{HgPath, HgPathBuf};
17 16 use std::path::Path;
18 17
19 18 pub const HELP_TEXT: &str = "
20 19 List tracked files.
21 20
22 21 Returns 0 on success.
23 22 ";
24 23
25 24 pub struct FilesCommand<'a> {
26 25 rev: Option<&'a str>,
27 26 }
28 27
29 28 impl<'a> FilesCommand<'a> {
30 29 pub fn new(rev: Option<&'a str>) -> Self {
31 30 FilesCommand { rev }
32 31 }
33 32
34 33 fn display_files(
35 34 &self,
36 35 ui: &Ui,
37 36 root: &Path,
38 37 files: impl IntoIterator<Item = &'a HgPath>,
39 38 ) -> Result<(), CommandError> {
40 39 let cwd = std::env::current_dir()
41 40 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
42 41 let rooted_cwd = cwd
43 42 .strip_prefix(root)
44 43 .expect("cwd was already checked within the repository");
45 44 let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd));
46 45
47 46 let mut stdout = ui.stdout_buffer();
48 47
49 48 for file in files {
50 49 stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?;
51 50 stdout.write_all(b"\n")?;
52 51 }
53 52 stdout.flush()?;
54 53 Ok(())
55 54 }
56 55 }
57 56
58 57 impl<'a> Command for FilesCommand<'a> {
59 58 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
60 let root = FindRoot::new().run()?;
59 let root = find_root()?;
61 60 requirements::check(&root)?;
62 61 if let Some(rev) = self.rev {
63 let mut operation = ListRevTrackedFiles::new(&root, rev)
62 let files = list_rev_tracked_files(&root, rev)
64 63 .map_err(|e| map_rev_error(rev, e))?;
65 let files = operation.run().map_err(|e| map_rev_error(rev, e))?;
66 self.display_files(ui, &root, files)
64 self.display_files(ui, &root, files.iter())
67 65 } else {
68 let mut operation = ListDirstateTrackedFiles::new(&root)
69 .map_err(map_dirstate_error)?;
70 let files = operation.run().map_err(map_dirstate_error)?;
66 let distate = Dirstate::new(&root).map_err(map_dirstate_error)?;
67 let files = distate.tracked_files().map_err(map_dirstate_error)?;
71 68 self.display_files(ui, &root, files)
72 69 }
73 70 }
74 71 }
75 72
76 73 /// Convert `ListRevTrackedFilesErrorKind` to `CommandError`
77 74 fn map_rev_error(rev: &str, err: ListRevTrackedFilesError) -> CommandError {
78 75 CommandError {
79 76 kind: match err.kind {
80 77 ListRevTrackedFilesErrorKind::IoError(err) => {
81 78 CommandErrorKind::Abort(Some(
82 79 utf8_to_local(&format!("abort: {}\n", err)).into(),
83 80 ))
84 81 }
85 82 ListRevTrackedFilesErrorKind::InvalidRevision => {
86 83 CommandErrorKind::Abort(Some(
87 84 utf8_to_local(&format!(
88 85 "abort: invalid revision identifier {}\n",
89 86 rev
90 87 ))
91 88 .into(),
92 89 ))
93 90 }
94 91 ListRevTrackedFilesErrorKind::AmbiguousPrefix => {
95 92 CommandErrorKind::Abort(Some(
96 93 utf8_to_local(&format!(
97 94 "abort: ambiguous revision identifier {}\n",
98 95 rev
99 96 ))
100 97 .into(),
101 98 ))
102 99 }
103 100 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version) => {
104 101 CommandErrorKind::Abort(Some(
105 102 utf8_to_local(&format!(
106 103 "abort: unsupported revlog version {}\n",
107 104 version
108 105 ))
109 106 .into(),
110 107 ))
111 108 }
112 109 ListRevTrackedFilesErrorKind::CorruptedRevlog => {
113 110 CommandErrorKind::Abort(Some(
114 111 "abort: corrupted revlog\n".into(),
115 112 ))
116 113 }
117 114 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format) => {
118 115 CommandErrorKind::Abort(Some(
119 116 utf8_to_local(&format!(
120 117 "abort: unknow revlog dataformat {:?}\n",
121 118 format
122 119 ))
123 120 .into(),
124 121 ))
125 122 }
126 123 },
127 124 }
128 125 }
129 126
130 127 /// Convert `ListDirstateTrackedFilesError` to `CommandError`
131 128 fn map_dirstate_error(err: ListDirstateTrackedFilesError) -> CommandError {
132 129 CommandError {
133 130 kind: match err.kind {
134 131 ListDirstateTrackedFilesErrorKind::IoError(err) => {
135 132 CommandErrorKind::Abort(Some(
136 133 utf8_to_local(&format!("abort: {}\n", err)).into(),
137 134 ))
138 135 }
139 136 ListDirstateTrackedFilesErrorKind::ParseError(_) => {
140 137 CommandErrorKind::Abort(Some(
141 138 // TODO find a better error message
142 139 b"abort: parse error\n".to_vec(),
143 140 ))
144 141 }
145 142 },
146 143 }
147 144 }
@@ -1,32 +1,32 b''
1 1 use crate::commands::Command;
2 2 use crate::error::CommandError;
3 3 use crate::ui::Ui;
4 4 use format_bytes::format_bytes;
5 use hg::operations::FindRoot;
5 use hg::operations::find_root;
6 6 use hg::utils::files::get_bytes_from_path;
7 7
8 8 pub const HELP_TEXT: &str = "
9 9 Print the root directory of the current repository.
10 10
11 11 Returns 0 on success.
12 12 ";
13 13
14 14 pub struct RootCommand {}
15 15
16 16 impl RootCommand {
17 17 pub fn new() -> Self {
18 18 RootCommand {}
19 19 }
20 20 }
21 21
22 22 impl Command for RootCommand {
23 23 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
24 let path_buf = FindRoot::new().run()?;
24 let path_buf = find_root()?;
25 25
26 26 let bytes = get_bytes_from_path(path_buf);
27 27
28 28 ui.write_stdout(&format_bytes!(b"{}\n", bytes.as_slice()))?;
29 29
30 30 Ok(())
31 31 }
32 32 }
General Comments 0
You need to be logged in to leave comments. Login now