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