##// END OF EJS Templates
rhg: exit with relevant code for unsupported requirements...
Simon Sapin -
r46549:2ad2745e default
parent child Browse files
Show More
@@ -1,53 +1,72
1 use std::io;
1 use std::io;
2 use std::path::Path;
2 use std::path::Path;
3
3
4 #[derive(Debug)]
4 #[derive(Debug)]
5 pub enum RequirementsError {
5 pub enum RequirementsError {
6 // TODO: include a path?
6 // TODO: include a path?
7 Io(io::Error),
7 Io(io::Error),
8 /// The `requires` file is corrupted
8 /// The `requires` file is corrupted
9 Corrupted,
9 Corrupted,
10 /// The repository requires a feature that we donοΏ½t support
10 /// The repository requires a feature that we donοΏ½t support
11 Unsupported {
11 Unsupported {
12 feature: String,
12 feature: String,
13 },
13 },
14 }
14 }
15
15
16 fn parse(bytes: &[u8]) -> Result<Vec<String>, ()> {
16 fn parse(bytes: &[u8]) -> Result<Vec<String>, ()> {
17 // The Python code reading this file uses `str.splitlines`
17 // The Python code reading this file uses `str.splitlines`
18 // which looks for a number of line separators (even including a couple of
18 // which looks for a number of line separators (even including a couple of
19 // non-ASCII ones), but Python code writing it always uses `\n`.
19 // non-ASCII ones), but Python code writing it always uses `\n`.
20 let lines = bytes.split(|&byte| byte == b'\n');
20 let lines = bytes.split(|&byte| byte == b'\n');
21
21
22 lines
22 lines
23 .filter(|line| !line.is_empty())
23 .filter(|line| !line.is_empty())
24 .map(|line| {
24 .map(|line| {
25 // Python uses Unicode `str.isalnum` but feature names are all
25 // Python uses Unicode `str.isalnum` but feature names are all
26 // ASCII
26 // ASCII
27 if line[0].is_ascii_alphanumeric() {
27 if line[0].is_ascii_alphanumeric() {
28 Ok(String::from_utf8(line.into()).unwrap())
28 Ok(String::from_utf8(line.into()).unwrap())
29 } else {
29 } else {
30 Err(())
30 Err(())
31 }
31 }
32 })
32 })
33 .collect()
33 .collect()
34 }
34 }
35
35
36 pub fn load(repo_root: &Path) -> Result<Vec<String>, RequirementsError> {
36 pub fn load(repo_root: &Path) -> Result<Vec<String>, RequirementsError> {
37 match std::fs::read(repo_root.join(".hg").join("requires")) {
37 match std::fs::read(repo_root.join(".hg").join("requires")) {
38 Ok(bytes) => parse(&bytes).map_err(|()| RequirementsError::Corrupted),
38 Ok(bytes) => parse(&bytes).map_err(|()| RequirementsError::Corrupted),
39
39
40 // Treat a missing file the same as an empty file.
40 // Treat a missing file the same as an empty file.
41 // From `mercurial/localrepo.py`:
41 // From `mercurial/localrepo.py`:
42 // > requires file contains a newline-delimited list of
42 // > requires file contains a newline-delimited list of
43 // > features/capabilities the opener (us) must have in order to use
43 // > features/capabilities the opener (us) must have in order to use
44 // > the repository. This file was introduced in Mercurial 0.9.2,
44 // > the repository. This file was introduced in Mercurial 0.9.2,
45 // > which means very old repositories may not have one. We assume
45 // > which means very old repositories may not have one. We assume
46 // > a missing file translates to no requirements.
46 // > a missing file translates to no requirements.
47 Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
47 Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
48 Ok(Vec::new())
48 Ok(Vec::new())
49 }
49 }
50
50
51 Err(error) => Err(RequirementsError::Io(error))?,
51 Err(error) => Err(RequirementsError::Io(error))?,
52 }
52 }
53 }
53 }
54
55 pub fn check(repo_root: &Path) -> Result<(), RequirementsError> {
56 for feature in load(repo_root)? {
57 if !SUPPORTED.contains(&&*feature) {
58 return Err(RequirementsError::Unsupported { feature })
59 }
60 }
61 Ok(())
62 }
63
64 // TODO: set this to actually-supported features
65 const SUPPORTED: &[&str] = &[
66 "dotencode",
67 "fncache",
68 "generaldelta",
69 "revlogv1",
70 "sparserevlog",
71 "store",
72 ];
@@ -1,99 +1,101
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::FindRoot;
6 use hg::operations::{CatRev, CatRevError, CatRevErrorKind};
6 use hg::operations::{CatRev, CatRevError, CatRevErrorKind};
7 use hg::requirements;
7 use hg::utils::hg_path::HgPathBuf;
8 use hg::utils::hg_path::HgPathBuf;
8 use micro_timer::timed;
9 use micro_timer::timed;
9 use std::convert::TryFrom;
10 use std::convert::TryFrom;
10
11
11 pub const HELP_TEXT: &str = "
12 pub const HELP_TEXT: &str = "
12 Output the current or given revision of files
13 Output the current or given revision of files
13 ";
14 ";
14
15
15 pub struct CatCommand<'a> {
16 pub struct CatCommand<'a> {
16 rev: Option<&'a str>,
17 rev: Option<&'a str>,
17 files: Vec<&'a str>,
18 files: Vec<&'a str>,
18 }
19 }
19
20
20 impl<'a> CatCommand<'a> {
21 impl<'a> CatCommand<'a> {
21 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 {
22 Self { rev, files }
23 Self { rev, files }
23 }
24 }
24
25
25 fn display(&self, ui: &Ui, data: &[u8]) -> Result<(), CommandError> {
26 fn display(&self, ui: &Ui, data: &[u8]) -> Result<(), CommandError> {
26 ui.write_stdout(data)?;
27 ui.write_stdout(data)?;
27 Ok(())
28 Ok(())
28 }
29 }
29 }
30 }
30
31
31 impl<'a> Command for CatCommand<'a> {
32 impl<'a> Command for CatCommand<'a> {
32 #[timed]
33 #[timed]
33 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
34 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
34 let root = FindRoot::new().run()?;
35 let root = FindRoot::new().run()?;
36 requirements::check(&root)?;
35 let cwd = std::env::current_dir()
37 let cwd = std::env::current_dir()
36 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
38 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
37
39
38 let mut files = vec![];
40 let mut files = vec![];
39 for file in self.files.iter() {
41 for file in self.files.iter() {
40 let normalized = cwd.join(&file);
42 let normalized = cwd.join(&file);
41 let stripped = normalized
43 let stripped = normalized
42 .strip_prefix(&root)
44 .strip_prefix(&root)
43 .or(Err(CommandErrorKind::Abort(None)))?;
45 .or(Err(CommandErrorKind::Abort(None)))?;
44 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
46 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
45 .or(Err(CommandErrorKind::Abort(None)))?;
47 .or(Err(CommandErrorKind::Abort(None)))?;
46 files.push(hg_file);
48 files.push(hg_file);
47 }
49 }
48
50
49 match self.rev {
51 match self.rev {
50 Some(rev) => {
52 Some(rev) => {
51 let mut operation = CatRev::new(&root, rev, &files)
53 let mut operation = CatRev::new(&root, rev, &files)
52 .map_err(|e| map_rev_error(rev, e))?;
54 .map_err(|e| map_rev_error(rev, e))?;
53 let data =
55 let data =
54 operation.run().map_err(|e| map_rev_error(rev, e))?;
56 operation.run().map_err(|e| map_rev_error(rev, e))?;
55 self.display(ui, &data)
57 self.display(ui, &data)
56 }
58 }
57 None => Err(CommandErrorKind::Unimplemented.into()),
59 None => Err(CommandErrorKind::Unimplemented.into()),
58 }
60 }
59 }
61 }
60 }
62 }
61
63
62 /// Convert `CatRevErrorKind` to `CommandError`
64 /// Convert `CatRevErrorKind` to `CommandError`
63 fn map_rev_error(rev: &str, err: CatRevError) -> CommandError {
65 fn map_rev_error(rev: &str, err: CatRevError) -> CommandError {
64 CommandError {
66 CommandError {
65 kind: match err.kind {
67 kind: match err.kind {
66 CatRevErrorKind::IoError(err) => CommandErrorKind::Abort(Some(
68 CatRevErrorKind::IoError(err) => CommandErrorKind::Abort(Some(
67 utf8_to_local(&format!("abort: {}\n", err)).into(),
69 utf8_to_local(&format!("abort: {}\n", err)).into(),
68 )),
70 )),
69 CatRevErrorKind::InvalidRevision => CommandErrorKind::Abort(Some(
71 CatRevErrorKind::InvalidRevision => CommandErrorKind::Abort(Some(
70 utf8_to_local(&format!(
72 utf8_to_local(&format!(
71 "abort: invalid revision identifier{}\n",
73 "abort: invalid revision identifier{}\n",
72 rev
74 rev
73 ))
75 ))
74 .into(),
76 .into(),
75 )),
77 )),
76 CatRevErrorKind::UnsuportedRevlogVersion(version) => {
78 CatRevErrorKind::UnsuportedRevlogVersion(version) => {
77 CommandErrorKind::Abort(Some(
79 CommandErrorKind::Abort(Some(
78 utf8_to_local(&format!(
80 utf8_to_local(&format!(
79 "abort: unsupported revlog version {}\n",
81 "abort: unsupported revlog version {}\n",
80 version
82 version
81 ))
83 ))
82 .into(),
84 .into(),
83 ))
85 ))
84 }
86 }
85 CatRevErrorKind::CorruptedRevlog => CommandErrorKind::Abort(Some(
87 CatRevErrorKind::CorruptedRevlog => CommandErrorKind::Abort(Some(
86 "abort: corrupted revlog\n".into(),
88 "abort: corrupted revlog\n".into(),
87 )),
89 )),
88 CatRevErrorKind::UnknowRevlogDataFormat(format) => {
90 CatRevErrorKind::UnknowRevlogDataFormat(format) => {
89 CommandErrorKind::Abort(Some(
91 CommandErrorKind::Abort(Some(
90 utf8_to_local(&format!(
92 utf8_to_local(&format!(
91 "abort: unknow revlog dataformat {:?}\n",
93 "abort: unknow revlog dataformat {:?}\n",
92 format
94 format
93 ))
95 ))
94 .into(),
96 .into(),
95 ))
97 ))
96 }
98 }
97 },
99 },
98 }
100 }
99 }
101 }
@@ -1,136 +1,138
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::FindRoot;
6 use hg::operations::{
6 use hg::operations::{
7 ListDirstateTrackedFiles, ListDirstateTrackedFilesError,
7 ListDirstateTrackedFiles, ListDirstateTrackedFilesError,
8 ListDirstateTrackedFilesErrorKind,
8 ListDirstateTrackedFilesErrorKind,
9 };
9 };
10 use hg::operations::{
10 use hg::operations::{
11 ListRevTrackedFiles, ListRevTrackedFilesError,
11 ListRevTrackedFiles, ListRevTrackedFilesError,
12 ListRevTrackedFilesErrorKind,
12 ListRevTrackedFilesErrorKind,
13 };
13 };
14 use hg::requirements;
14 use hg::utils::files::{get_bytes_from_path, relativize_path};
15 use hg::utils::files::{get_bytes_from_path, relativize_path};
15 use hg::utils::hg_path::{HgPath, HgPathBuf};
16 use hg::utils::hg_path::{HgPath, HgPathBuf};
16 use std::path::PathBuf;
17 use std::path::PathBuf;
17
18
18 pub const HELP_TEXT: &str = "
19 pub const HELP_TEXT: &str = "
19 List tracked files.
20 List tracked files.
20
21
21 Returns 0 on success.
22 Returns 0 on success.
22 ";
23 ";
23
24
24 pub struct FilesCommand<'a> {
25 pub struct FilesCommand<'a> {
25 rev: Option<&'a str>,
26 rev: Option<&'a str>,
26 }
27 }
27
28
28 impl<'a> FilesCommand<'a> {
29 impl<'a> FilesCommand<'a> {
29 pub fn new(rev: Option<&'a str>) -> Self {
30 pub fn new(rev: Option<&'a str>) -> Self {
30 FilesCommand { rev }
31 FilesCommand { rev }
31 }
32 }
32
33
33 fn display_files(
34 fn display_files(
34 &self,
35 &self,
35 ui: &Ui,
36 ui: &Ui,
36 root: &PathBuf,
37 root: &PathBuf,
37 files: impl IntoIterator<Item = &'a HgPath>,
38 files: impl IntoIterator<Item = &'a HgPath>,
38 ) -> Result<(), CommandError> {
39 ) -> Result<(), CommandError> {
39 let cwd = std::env::current_dir()
40 let cwd = std::env::current_dir()
40 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
41 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
41 let rooted_cwd = cwd
42 let rooted_cwd = cwd
42 .strip_prefix(&root)
43 .strip_prefix(&root)
43 .expect("cwd was already checked within the repository");
44 .expect("cwd was already checked within the repository");
44 let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd));
45 let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd));
45
46
46 let mut stdout = ui.stdout_buffer();
47 let mut stdout = ui.stdout_buffer();
47
48
48 for file in files {
49 for file in files {
49 stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?;
50 stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?;
50 stdout.write_all(b"\n")?;
51 stdout.write_all(b"\n")?;
51 }
52 }
52 stdout.flush()?;
53 stdout.flush()?;
53 Ok(())
54 Ok(())
54 }
55 }
55 }
56 }
56
57
57 impl<'a> Command for FilesCommand<'a> {
58 impl<'a> Command for FilesCommand<'a> {
58 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
59 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
59 let root = FindRoot::new().run()?;
60 let root = FindRoot::new().run()?;
61 requirements::check(&root)?;
60 if let Some(rev) = self.rev {
62 if let Some(rev) = self.rev {
61 let mut operation = ListRevTrackedFiles::new(&root, rev)
63 let mut operation = ListRevTrackedFiles::new(&root, rev)
62 .map_err(|e| map_rev_error(rev, e))?;
64 .map_err(|e| map_rev_error(rev, e))?;
63 let files = operation.run().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)
66 self.display_files(ui, &root, files)
65 } else {
67 } else {
66 let mut operation = ListDirstateTrackedFiles::new(&root)
68 let mut operation = ListDirstateTrackedFiles::new(&root)
67 .map_err(map_dirstate_error)?;
69 .map_err(map_dirstate_error)?;
68 let files = operation.run().map_err(map_dirstate_error)?;
70 let files = operation.run().map_err(map_dirstate_error)?;
69 self.display_files(ui, &root, files)
71 self.display_files(ui, &root, files)
70 }
72 }
71 }
73 }
72 }
74 }
73
75
74 /// Convert `ListRevTrackedFilesErrorKind` to `CommandError`
76 /// Convert `ListRevTrackedFilesErrorKind` to `CommandError`
75 fn map_rev_error(rev: &str, err: ListRevTrackedFilesError) -> CommandError {
77 fn map_rev_error(rev: &str, err: ListRevTrackedFilesError) -> CommandError {
76 CommandError {
78 CommandError {
77 kind: match err.kind {
79 kind: match err.kind {
78 ListRevTrackedFilesErrorKind::IoError(err) => {
80 ListRevTrackedFilesErrorKind::IoError(err) => {
79 CommandErrorKind::Abort(Some(
81 CommandErrorKind::Abort(Some(
80 utf8_to_local(&format!("abort: {}\n", err)).into(),
82 utf8_to_local(&format!("abort: {}\n", err)).into(),
81 ))
83 ))
82 }
84 }
83 ListRevTrackedFilesErrorKind::InvalidRevision => {
85 ListRevTrackedFilesErrorKind::InvalidRevision => {
84 CommandErrorKind::Abort(Some(
86 CommandErrorKind::Abort(Some(
85 utf8_to_local(&format!(
87 utf8_to_local(&format!(
86 "abort: invalid revision identifier{}\n",
88 "abort: invalid revision identifier{}\n",
87 rev
89 rev
88 ))
90 ))
89 .into(),
91 .into(),
90 ))
92 ))
91 }
93 }
92 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version) => {
94 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version) => {
93 CommandErrorKind::Abort(Some(
95 CommandErrorKind::Abort(Some(
94 utf8_to_local(&format!(
96 utf8_to_local(&format!(
95 "abort: unsupported revlog version {}\n",
97 "abort: unsupported revlog version {}\n",
96 version
98 version
97 ))
99 ))
98 .into(),
100 .into(),
99 ))
101 ))
100 }
102 }
101 ListRevTrackedFilesErrorKind::CorruptedRevlog => {
103 ListRevTrackedFilesErrorKind::CorruptedRevlog => {
102 CommandErrorKind::Abort(Some(
104 CommandErrorKind::Abort(Some(
103 "abort: corrupted revlog\n".into(),
105 "abort: corrupted revlog\n".into(),
104 ))
106 ))
105 }
107 }
106 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format) => {
108 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format) => {
107 CommandErrorKind::Abort(Some(
109 CommandErrorKind::Abort(Some(
108 utf8_to_local(&format!(
110 utf8_to_local(&format!(
109 "abort: unknow revlog dataformat {:?}\n",
111 "abort: unknow revlog dataformat {:?}\n",
110 format
112 format
111 ))
113 ))
112 .into(),
114 .into(),
113 ))
115 ))
114 }
116 }
115 },
117 },
116 }
118 }
117 }
119 }
118
120
119 /// Convert `ListDirstateTrackedFilesError` to `CommandError`
121 /// Convert `ListDirstateTrackedFilesError` to `CommandError`
120 fn map_dirstate_error(err: ListDirstateTrackedFilesError) -> CommandError {
122 fn map_dirstate_error(err: ListDirstateTrackedFilesError) -> CommandError {
121 CommandError {
123 CommandError {
122 kind: match err.kind {
124 kind: match err.kind {
123 ListDirstateTrackedFilesErrorKind::IoError(err) => {
125 ListDirstateTrackedFilesErrorKind::IoError(err) => {
124 CommandErrorKind::Abort(Some(
126 CommandErrorKind::Abort(Some(
125 utf8_to_local(&format!("abort: {}\n", err)).into(),
127 utf8_to_local(&format!("abort: {}\n", err)).into(),
126 ))
128 ))
127 }
129 }
128 ListDirstateTrackedFilesErrorKind::ParseError(_) => {
130 ListDirstateTrackedFilesErrorKind::ParseError(_) => {
129 CommandErrorKind::Abort(Some(
131 CommandErrorKind::Abort(Some(
130 // TODO find a better error message
132 // TODO find a better error message
131 b"abort: parse error\n".to_vec(),
133 b"abort: parse error\n".to_vec(),
132 ))
134 ))
133 }
135 }
134 },
136 },
135 }
137 }
136 }
138 }
@@ -1,130 +1,133
1 use crate::exitcode;
1 use crate::exitcode;
2 use crate::ui::UiError;
2 use crate::ui::UiError;
3 use hg::operations::{FindRootError, FindRootErrorKind};
3 use hg::operations::{FindRootError, FindRootErrorKind};
4 use hg::requirements::RequirementsError;
4 use hg::requirements::RequirementsError;
5 use hg::utils::files::get_bytes_from_path;
5 use hg::utils::files::get_bytes_from_path;
6 use std::convert::From;
6 use std::convert::From;
7 use std::path::PathBuf;
7 use std::path::PathBuf;
8
8
9 /// The kind of command error
9 /// The kind of command error
10 #[derive(Debug)]
10 #[derive(Debug)]
11 pub enum CommandErrorKind {
11 pub enum CommandErrorKind {
12 /// The root of the repository cannot be found
12 /// The root of the repository cannot be found
13 RootNotFound(PathBuf),
13 RootNotFound(PathBuf),
14 /// The current directory cannot be found
14 /// The current directory cannot be found
15 CurrentDirNotFound(std::io::Error),
15 CurrentDirNotFound(std::io::Error),
16 /// `.hg/requires`
16 /// `.hg/requires`
17 RequirementsError(RequirementsError),
17 RequirementsError(RequirementsError),
18 /// The standard output stream cannot be written to
18 /// The standard output stream cannot be written to
19 StdoutError,
19 StdoutError,
20 /// The standard error stream cannot be written to
20 /// The standard error stream cannot be written to
21 StderrError,
21 StderrError,
22 /// The command aborted
22 /// The command aborted
23 Abort(Option<Vec<u8>>),
23 Abort(Option<Vec<u8>>),
24 /// A mercurial capability as not been implemented.
24 /// A mercurial capability as not been implemented.
25 Unimplemented,
25 Unimplemented,
26 }
26 }
27
27
28 impl CommandErrorKind {
28 impl CommandErrorKind {
29 pub fn get_exit_code(&self) -> exitcode::ExitCode {
29 pub fn get_exit_code(&self) -> exitcode::ExitCode {
30 match self {
30 match self {
31 CommandErrorKind::RootNotFound(_) => exitcode::ABORT,
31 CommandErrorKind::RootNotFound(_) => exitcode::ABORT,
32 CommandErrorKind::CurrentDirNotFound(_) => exitcode::ABORT,
32 CommandErrorKind::CurrentDirNotFound(_) => exitcode::ABORT,
33 CommandErrorKind::RequirementsError(
34 RequirementsError::Unsupported { .. },
35 ) => exitcode::UNIMPLEMENTED_COMMAND,
33 CommandErrorKind::RequirementsError(_) => exitcode::ABORT,
36 CommandErrorKind::RequirementsError(_) => exitcode::ABORT,
34 CommandErrorKind::StdoutError => exitcode::ABORT,
37 CommandErrorKind::StdoutError => exitcode::ABORT,
35 CommandErrorKind::StderrError => exitcode::ABORT,
38 CommandErrorKind::StderrError => exitcode::ABORT,
36 CommandErrorKind::Abort(_) => exitcode::ABORT,
39 CommandErrorKind::Abort(_) => exitcode::ABORT,
37 CommandErrorKind::Unimplemented => exitcode::UNIMPLEMENTED_COMMAND,
40 CommandErrorKind::Unimplemented => exitcode::UNIMPLEMENTED_COMMAND,
38 }
41 }
39 }
42 }
40
43
41 /// Return the message corresponding to the error kind if any
44 /// Return the message corresponding to the error kind if any
42 pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
45 pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
43 match self {
46 match self {
44 // TODO use formating macro
47 // TODO use formating macro
45 CommandErrorKind::RootNotFound(path) => {
48 CommandErrorKind::RootNotFound(path) => {
46 let bytes = get_bytes_from_path(path);
49 let bytes = get_bytes_from_path(path);
47 Some(
50 Some(
48 [
51 [
49 b"abort: no repository found in '",
52 b"abort: no repository found in '",
50 bytes.as_slice(),
53 bytes.as_slice(),
51 b"' (.hg not found)!\n",
54 b"' (.hg not found)!\n",
52 ]
55 ]
53 .concat(),
56 .concat(),
54 )
57 )
55 }
58 }
56 // TODO use formating macro
59 // TODO use formating macro
57 CommandErrorKind::CurrentDirNotFound(e) => Some(
60 CommandErrorKind::CurrentDirNotFound(e) => Some(
58 [
61 [
59 b"abort: error getting current working directory: ",
62 b"abort: error getting current working directory: ",
60 e.to_string().as_bytes(),
63 e.to_string().as_bytes(),
61 b"\n",
64 b"\n",
62 ]
65 ]
63 .concat(),
66 .concat(),
64 ),
67 ),
65 CommandErrorKind::RequirementsError(
68 CommandErrorKind::RequirementsError(
66 RequirementsError::Corrupted,
69 RequirementsError::Corrupted,
67 ) => Some(
70 ) => Some(
68 "abort: .hg/requires is corrupted\n".as_bytes().to_owned(),
71 "abort: .hg/requires is corrupted\n".as_bytes().to_owned(),
69 ),
72 ),
70 CommandErrorKind::Abort(message) => message.to_owned(),
73 CommandErrorKind::Abort(message) => message.to_owned(),
71 _ => None,
74 _ => None,
72 }
75 }
73 }
76 }
74 }
77 }
75
78
76 /// The error type for the Command trait
79 /// The error type for the Command trait
77 #[derive(Debug)]
80 #[derive(Debug)]
78 pub struct CommandError {
81 pub struct CommandError {
79 pub kind: CommandErrorKind,
82 pub kind: CommandErrorKind,
80 }
83 }
81
84
82 impl CommandError {
85 impl CommandError {
83 /// Exist the process with the corresponding exit code.
86 /// Exist the process with the corresponding exit code.
84 pub fn exit(&self) {
87 pub fn exit(&self) {
85 std::process::exit(self.kind.get_exit_code())
88 std::process::exit(self.kind.get_exit_code())
86 }
89 }
87
90
88 /// Return the message corresponding to the command error if any
91 /// Return the message corresponding to the command error if any
89 pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
92 pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
90 self.kind.get_error_message_bytes()
93 self.kind.get_error_message_bytes()
91 }
94 }
92 }
95 }
93
96
94 impl From<CommandErrorKind> for CommandError {
97 impl From<CommandErrorKind> for CommandError {
95 fn from(kind: CommandErrorKind) -> Self {
98 fn from(kind: CommandErrorKind) -> Self {
96 CommandError { kind }
99 CommandError { kind }
97 }
100 }
98 }
101 }
99
102
100 impl From<UiError> for CommandError {
103 impl From<UiError> for CommandError {
101 fn from(error: UiError) -> Self {
104 fn from(error: UiError) -> Self {
102 CommandError {
105 CommandError {
103 kind: match error {
106 kind: match error {
104 UiError::StdoutError(_) => CommandErrorKind::StdoutError,
107 UiError::StdoutError(_) => CommandErrorKind::StdoutError,
105 UiError::StderrError(_) => CommandErrorKind::StderrError,
108 UiError::StderrError(_) => CommandErrorKind::StderrError,
106 },
109 },
107 }
110 }
108 }
111 }
109 }
112 }
110
113
111 impl From<FindRootError> for CommandError {
114 impl From<FindRootError> for CommandError {
112 fn from(err: FindRootError) -> Self {
115 fn from(err: FindRootError) -> Self {
113 match err.kind {
116 match err.kind {
114 FindRootErrorKind::RootNotFound(path) => CommandError {
117 FindRootErrorKind::RootNotFound(path) => CommandError {
115 kind: CommandErrorKind::RootNotFound(path),
118 kind: CommandErrorKind::RootNotFound(path),
116 },
119 },
117 FindRootErrorKind::GetCurrentDirError(e) => CommandError {
120 FindRootErrorKind::GetCurrentDirError(e) => CommandError {
118 kind: CommandErrorKind::CurrentDirNotFound(e),
121 kind: CommandErrorKind::CurrentDirNotFound(e),
119 },
122 },
120 }
123 }
121 }
124 }
122 }
125 }
123
126
124 impl From<RequirementsError> for CommandError {
127 impl From<RequirementsError> for CommandError {
125 fn from(err: RequirementsError) -> Self {
128 fn from(err: RequirementsError) -> Self {
126 CommandError {
129 CommandError {
127 kind: CommandErrorKind::RequirementsError(err),
130 kind: CommandErrorKind::RequirementsError(err),
128 }
131 }
129 }
132 }
130 }
133 }
@@ -1,126 +1,142
1 #require rust
1 #require rust
2
2
3 Define an rhg function that will only run if rhg exists
3 Define an rhg function that will only run if rhg exists
4 $ rhg() {
4 $ rhg() {
5 > if [ -f "$RUNTESTDIR/../rust/target/debug/rhg" ]; then
5 > if [ -f "$RUNTESTDIR/../rust/target/debug/rhg" ]; then
6 > "$RUNTESTDIR/../rust/target/debug/rhg" "$@"
6 > "$RUNTESTDIR/../rust/target/debug/rhg" "$@"
7 > else
7 > else
8 > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg."
8 > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg."
9 > exit 80
9 > exit 80
10 > fi
10 > fi
11 > }
11 > }
12
12
13 Unimplemented command
13 Unimplemented command
14 $ rhg unimplemented-command
14 $ rhg unimplemented-command
15 error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
15 error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
16
16
17 USAGE:
17 USAGE:
18 rhg <SUBCOMMAND>
18 rhg <SUBCOMMAND>
19
19
20 For more information try --help
20 For more information try --help
21 [252]
21 [252]
22
22
23 Finding root
23 Finding root
24 $ rhg root
24 $ rhg root
25 abort: no repository found in '$TESTTMP' (.hg not found)!
25 abort: no repository found in '$TESTTMP' (.hg not found)!
26 [255]
26 [255]
27
27
28 $ hg init repository
28 $ hg init repository
29 $ cd repository
29 $ cd repository
30 $ rhg root
30 $ rhg root
31 $TESTTMP/repository
31 $TESTTMP/repository
32
32
33 Unwritable file descriptor
33 Unwritable file descriptor
34 $ rhg root > /dev/full
34 $ rhg root > /dev/full
35 abort: No space left on device (os error 28)
35 abort: No space left on device (os error 28)
36 [255]
36 [255]
37
37
38 Deleted repository
38 Deleted repository
39 $ rm -rf `pwd`
39 $ rm -rf `pwd`
40 $ rhg root
40 $ rhg root
41 abort: error getting current working directory: $ENOENT$
41 abort: error getting current working directory: $ENOENT$
42 [255]
42 [255]
43
43
44 Listing tracked files
44 Listing tracked files
45 $ cd $TESTTMP
45 $ cd $TESTTMP
46 $ hg init repository
46 $ hg init repository
47 $ cd repository
47 $ cd repository
48 $ for i in 1 2 3; do
48 $ for i in 1 2 3; do
49 > echo $i >> file$i
49 > echo $i >> file$i
50 > hg add file$i
50 > hg add file$i
51 > done
51 > done
52 > hg commit -m "commit $i" -q
52 > hg commit -m "commit $i" -q
53
53
54 Listing tracked files from root
54 Listing tracked files from root
55 $ rhg files
55 $ rhg files
56 file1
56 file1
57 file2
57 file2
58 file3
58 file3
59
59
60 Listing tracked files from subdirectory
60 Listing tracked files from subdirectory
61 $ mkdir -p path/to/directory
61 $ mkdir -p path/to/directory
62 $ cd path/to/directory
62 $ cd path/to/directory
63 $ rhg files
63 $ rhg files
64 ../../../file1
64 ../../../file1
65 ../../../file2
65 ../../../file2
66 ../../../file3
66 ../../../file3
67
67
68 Listing tracked files through broken pipe
68 Listing tracked files through broken pipe
69 $ rhg files | head -n 1
69 $ rhg files | head -n 1
70 ../../../file1
70 ../../../file1
71
71
72 Debuging data in inline index
72 Debuging data in inline index
73 $ cd $TESTTMP
73 $ cd $TESTTMP
74 $ rm -rf repository
74 $ rm -rf repository
75 $ hg init repository
75 $ hg init repository
76 $ cd repository
76 $ cd repository
77 $ for i in 1 2 3; do
77 $ for i in 1 2 3; do
78 > echo $i >> file$i
78 > echo $i >> file$i
79 > hg add file$i
79 > hg add file$i
80 > hg commit -m "commit $i" -q
80 > hg commit -m "commit $i" -q
81 > done
81 > done
82 $ rhg debugdata -c 2
82 $ rhg debugdata -c 2
83 e36fa63d37a576b27a69057598351db6ee5746bd
83 e36fa63d37a576b27a69057598351db6ee5746bd
84 test
84 test
85 0 0
85 0 0
86 file3
86 file3
87
87
88 commit 3 (no-eol)
88 commit 3 (no-eol)
89 $ rhg debugdata -m 2
89 $ rhg debugdata -m 2
90 file1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
90 file1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
91 file2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
91 file2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
92 file3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
92 file3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
93
93
94 Debuging with full node id
94 Debuging with full node id
95 $ rhg debugdata -c `hg log -r 0 -T '{node}'`
95 $ rhg debugdata -c `hg log -r 0 -T '{node}'`
96 c8e64718e1ca0312eeee0f59d37f8dc612793856
96 c8e64718e1ca0312eeee0f59d37f8dc612793856
97 test
97 test
98 0 0
98 0 0
99 file1
99 file1
100
100
101 commit 1 (no-eol)
101 commit 1 (no-eol)
102
102
103 Cat files
103 Cat files
104 $ cd $TESTTMP
104 $ cd $TESTTMP
105 $ rm -rf repository
105 $ rm -rf repository
106 $ hg init repository
106 $ hg init repository
107 $ cd repository
107 $ cd repository
108 $ echo "original content" > original
108 $ echo "original content" > original
109 $ hg add original
109 $ hg add original
110 $ hg commit -m "add original" original
110 $ hg commit -m "add original" original
111 $ rhg cat -r 0 original
111 $ rhg cat -r 0 original
112 original content
112 original content
113 Cat copied file should not display copy metadata
113 Cat copied file should not display copy metadata
114 $ hg copy original copy_of_original
114 $ hg copy original copy_of_original
115 $ hg commit -m "add copy of original"
115 $ hg commit -m "add copy of original"
116 $ rhg cat -r 1 copy_of_original
116 $ rhg cat -r 1 copy_of_original
117 original content
117 original content
118
118
119 Requirements
119 Requirements
120 $ rhg debugrequirements
120 $ rhg debugrequirements
121 dotencode
121 dotencode
122 fncache
122 fncache
123 generaldelta
123 generaldelta
124 revlogv1
124 revlogv1
125 sparserevlog
125 sparserevlog
126 store
126 store
127
128 $ echo indoor-pool >> .hg/requires
129 $ rhg files
130 [252]
131
132 $ rhg cat -r 1 copy_of_original
133 [252]
134
135 $ rhg debugrequirements
136 dotencode
137 fncache
138 generaldelta
139 revlogv1
140 sparserevlog
141 store
142 indoor-pool
General Comments 0
You need to be logged in to leave comments. Login now