##// END OF EJS Templates
requirements: move loading to hg-core and add parsing...
Simon Sapin -
r46536:a2eda1ff default
parent child Browse files
Show More
@@ -0,0 +1,53 b''
1 use std::io;
2 use std::path::Path;
3
4 #[derive(Debug)]
5 pub enum RequirementsError {
6 // TODO: include a path?
7 Io(io::Error),
8 /// The `requires` file is corrupted
9 Corrupted,
10 /// The repository requires a feature that we donοΏ½t support
11 Unsupported {
12 feature: String,
13 },
14 }
15
16 fn parse(bytes: &[u8]) -> Result<Vec<String>, ()> {
17 // The Python code reading this file uses `str.splitlines`
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`.
20 let lines = bytes.split(|&byte| byte == b'\n');
21
22 lines
23 .filter(|line| !line.is_empty())
24 .map(|line| {
25 // Python uses Unicode `str.isalnum` but feature names are all
26 // ASCII
27 if line[0].is_ascii_alphanumeric() {
28 Ok(String::from_utf8(line.into()).unwrap())
29 } else {
30 Err(())
31 }
32 })
33 .collect()
34 }
35
36 pub fn load(repo_root: &Path) -> Result<Vec<String>, RequirementsError> {
37 match std::fs::read(repo_root.join(".hg").join("requires")) {
38 Ok(bytes) => parse(&bytes).map_err(|()| RequirementsError::Corrupted),
39
40 // Treat a missing file the same as an empty file.
41 // From `mercurial/localrepo.py`:
42 // > requires file contains a newline-delimited list of
43 // > features/capabilities the opener (us) must have in order to use
44 // > the repository. This file was introduced in Mercurial 0.9.2,
45 // > which means very old repositories may not have one. We assume
46 // > a missing file translates to no requirements.
47 Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
48 Ok(Vec::new())
49 }
50
51 Err(error) => Err(RequirementsError::Io(error))?,
52 }
53 }
@@ -1,192 +1,193 b''
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
2 // and Mercurial contributors
2 // and Mercurial contributors
3 //
3 //
4 // This software may be used and distributed according to the terms of the
4 // This software may be used and distributed according to the terms of the
5 // GNU General Public License version 2 or any later version.
5 // GNU General Public License version 2 or any later version.
6 mod ancestors;
6 mod ancestors;
7 pub mod dagops;
7 pub mod dagops;
8 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
8 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
9 mod dirstate;
9 mod dirstate;
10 pub mod discovery;
10 pub mod discovery;
11 pub mod requirements;
11 pub mod testing; // unconditionally built, for use from integration tests
12 pub mod testing; // unconditionally built, for use from integration tests
12 pub use dirstate::{
13 pub use dirstate::{
13 dirs_multiset::{DirsMultiset, DirsMultisetIter},
14 dirs_multiset::{DirsMultiset, DirsMultisetIter},
14 dirstate_map::DirstateMap,
15 dirstate_map::DirstateMap,
15 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
16 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
16 status::{
17 status::{
17 status, BadMatch, BadType, DirstateStatus, StatusError, StatusOptions,
18 status, BadMatch, BadType, DirstateStatus, StatusError, StatusOptions,
18 },
19 },
19 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
20 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
20 StateMap, StateMapIter,
21 StateMap, StateMapIter,
21 };
22 };
22 mod filepatterns;
23 mod filepatterns;
23 pub mod matchers;
24 pub mod matchers;
24 pub mod revlog;
25 pub mod revlog;
25 pub use revlog::*;
26 pub use revlog::*;
26 pub mod operations;
27 pub mod operations;
27 pub mod utils;
28 pub mod utils;
28
29
29 // Remove this to see (potential) non-artificial compile failures. MacOS
30 // Remove this to see (potential) non-artificial compile failures. MacOS
30 // *should* compile, but fail to compile tests for example as of 2020-03-06
31 // *should* compile, but fail to compile tests for example as of 2020-03-06
31 #[cfg(not(target_os = "linux"))]
32 #[cfg(not(target_os = "linux"))]
32 compile_error!(
33 compile_error!(
33 "`hg-core` has only been tested on Linux and will most \
34 "`hg-core` has only been tested on Linux and will most \
34 likely not behave correctly on other platforms."
35 likely not behave correctly on other platforms."
35 );
36 );
36
37
37 use crate::utils::hg_path::{HgPathBuf, HgPathError};
38 use crate::utils::hg_path::{HgPathBuf, HgPathError};
38 pub use filepatterns::{
39 pub use filepatterns::{
39 parse_pattern_syntax, read_pattern_file, IgnorePattern,
40 parse_pattern_syntax, read_pattern_file, IgnorePattern,
40 PatternFileWarning, PatternSyntax,
41 PatternFileWarning, PatternSyntax,
41 };
42 };
42 use std::collections::HashMap;
43 use std::collections::HashMap;
43 use twox_hash::RandomXxHashBuilder64;
44 use twox_hash::RandomXxHashBuilder64;
44
45
45 /// This is a contract between the `micro-timer` crate and us, to expose
46 /// This is a contract between the `micro-timer` crate and us, to expose
46 /// the `log` crate as `crate::log`.
47 /// the `log` crate as `crate::log`.
47 use log;
48 use log;
48
49
49 pub type LineNumber = usize;
50 pub type LineNumber = usize;
50
51
51 /// Rust's default hasher is too slow because it tries to prevent collision
52 /// Rust's default hasher is too slow because it tries to prevent collision
52 /// attacks. We are not concerned about those: if an ill-minded person has
53 /// attacks. We are not concerned about those: if an ill-minded person has
53 /// write access to your repository, you have other issues.
54 /// write access to your repository, you have other issues.
54 pub type FastHashMap<K, V> = HashMap<K, V, RandomXxHashBuilder64>;
55 pub type FastHashMap<K, V> = HashMap<K, V, RandomXxHashBuilder64>;
55
56
56 #[derive(Clone, Debug, PartialEq)]
57 #[derive(Clone, Debug, PartialEq)]
57 pub enum DirstateParseError {
58 pub enum DirstateParseError {
58 TooLittleData,
59 TooLittleData,
59 Overflow,
60 Overflow,
60 // TODO refactor to use bytes instead of String
61 // TODO refactor to use bytes instead of String
61 CorruptedEntry(String),
62 CorruptedEntry(String),
62 Damaged,
63 Damaged,
63 }
64 }
64
65
65 impl From<std::io::Error> for DirstateParseError {
66 impl From<std::io::Error> for DirstateParseError {
66 fn from(e: std::io::Error) -> Self {
67 fn from(e: std::io::Error) -> Self {
67 DirstateParseError::CorruptedEntry(e.to_string())
68 DirstateParseError::CorruptedEntry(e.to_string())
68 }
69 }
69 }
70 }
70
71
71 impl ToString for DirstateParseError {
72 impl ToString for DirstateParseError {
72 fn to_string(&self) -> String {
73 fn to_string(&self) -> String {
73 use crate::DirstateParseError::*;
74 use crate::DirstateParseError::*;
74 match self {
75 match self {
75 TooLittleData => "Too little data for dirstate.".to_string(),
76 TooLittleData => "Too little data for dirstate.".to_string(),
76 Overflow => "Overflow in dirstate.".to_string(),
77 Overflow => "Overflow in dirstate.".to_string(),
77 CorruptedEntry(e) => format!("Corrupted entry: {:?}.", e),
78 CorruptedEntry(e) => format!("Corrupted entry: {:?}.", e),
78 Damaged => "Dirstate appears to be damaged.".to_string(),
79 Damaged => "Dirstate appears to be damaged.".to_string(),
79 }
80 }
80 }
81 }
81 }
82 }
82
83
83 #[derive(Debug, PartialEq)]
84 #[derive(Debug, PartialEq)]
84 pub enum DirstatePackError {
85 pub enum DirstatePackError {
85 CorruptedEntry(String),
86 CorruptedEntry(String),
86 CorruptedParent,
87 CorruptedParent,
87 BadSize(usize, usize),
88 BadSize(usize, usize),
88 }
89 }
89
90
90 impl From<std::io::Error> for DirstatePackError {
91 impl From<std::io::Error> for DirstatePackError {
91 fn from(e: std::io::Error) -> Self {
92 fn from(e: std::io::Error) -> Self {
92 DirstatePackError::CorruptedEntry(e.to_string())
93 DirstatePackError::CorruptedEntry(e.to_string())
93 }
94 }
94 }
95 }
95 #[derive(Debug, PartialEq)]
96 #[derive(Debug, PartialEq)]
96 pub enum DirstateMapError {
97 pub enum DirstateMapError {
97 PathNotFound(HgPathBuf),
98 PathNotFound(HgPathBuf),
98 EmptyPath,
99 EmptyPath,
99 InvalidPath(HgPathError),
100 InvalidPath(HgPathError),
100 }
101 }
101
102
102 impl ToString for DirstateMapError {
103 impl ToString for DirstateMapError {
103 fn to_string(&self) -> String {
104 fn to_string(&self) -> String {
104 match self {
105 match self {
105 DirstateMapError::PathNotFound(_) => {
106 DirstateMapError::PathNotFound(_) => {
106 "expected a value, found none".to_string()
107 "expected a value, found none".to_string()
107 }
108 }
108 DirstateMapError::EmptyPath => "Overflow in dirstate.".to_string(),
109 DirstateMapError::EmptyPath => "Overflow in dirstate.".to_string(),
109 DirstateMapError::InvalidPath(e) => e.to_string(),
110 DirstateMapError::InvalidPath(e) => e.to_string(),
110 }
111 }
111 }
112 }
112 }
113 }
113
114
114 #[derive(Debug)]
115 #[derive(Debug)]
115 pub enum DirstateError {
116 pub enum DirstateError {
116 Parse(DirstateParseError),
117 Parse(DirstateParseError),
117 Pack(DirstatePackError),
118 Pack(DirstatePackError),
118 Map(DirstateMapError),
119 Map(DirstateMapError),
119 IO(std::io::Error),
120 IO(std::io::Error),
120 }
121 }
121
122
122 impl From<DirstateParseError> for DirstateError {
123 impl From<DirstateParseError> for DirstateError {
123 fn from(e: DirstateParseError) -> Self {
124 fn from(e: DirstateParseError) -> Self {
124 DirstateError::Parse(e)
125 DirstateError::Parse(e)
125 }
126 }
126 }
127 }
127
128
128 impl From<DirstatePackError> for DirstateError {
129 impl From<DirstatePackError> for DirstateError {
129 fn from(e: DirstatePackError) -> Self {
130 fn from(e: DirstatePackError) -> Self {
130 DirstateError::Pack(e)
131 DirstateError::Pack(e)
131 }
132 }
132 }
133 }
133
134
134 #[derive(Debug)]
135 #[derive(Debug)]
135 pub enum PatternError {
136 pub enum PatternError {
136 Path(HgPathError),
137 Path(HgPathError),
137 UnsupportedSyntax(String),
138 UnsupportedSyntax(String),
138 UnsupportedSyntaxInFile(String, String, usize),
139 UnsupportedSyntaxInFile(String, String, usize),
139 TooLong(usize),
140 TooLong(usize),
140 IO(std::io::Error),
141 IO(std::io::Error),
141 /// Needed a pattern that can be turned into a regex but got one that
142 /// Needed a pattern that can be turned into a regex but got one that
142 /// can't. This should only happen through programmer error.
143 /// can't. This should only happen through programmer error.
143 NonRegexPattern(IgnorePattern),
144 NonRegexPattern(IgnorePattern),
144 }
145 }
145
146
146 impl ToString for PatternError {
147 impl ToString for PatternError {
147 fn to_string(&self) -> String {
148 fn to_string(&self) -> String {
148 match self {
149 match self {
149 PatternError::UnsupportedSyntax(syntax) => {
150 PatternError::UnsupportedSyntax(syntax) => {
150 format!("Unsupported syntax {}", syntax)
151 format!("Unsupported syntax {}", syntax)
151 }
152 }
152 PatternError::UnsupportedSyntaxInFile(syntax, file_path, line) => {
153 PatternError::UnsupportedSyntaxInFile(syntax, file_path, line) => {
153 format!(
154 format!(
154 "{}:{}: unsupported syntax {}",
155 "{}:{}: unsupported syntax {}",
155 file_path, line, syntax
156 file_path, line, syntax
156 )
157 )
157 }
158 }
158 PatternError::TooLong(size) => {
159 PatternError::TooLong(size) => {
159 format!("matcher pattern is too long ({} bytes)", size)
160 format!("matcher pattern is too long ({} bytes)", size)
160 }
161 }
161 PatternError::IO(e) => e.to_string(),
162 PatternError::IO(e) => e.to_string(),
162 PatternError::Path(e) => e.to_string(),
163 PatternError::Path(e) => e.to_string(),
163 PatternError::NonRegexPattern(pattern) => {
164 PatternError::NonRegexPattern(pattern) => {
164 format!("'{:?}' cannot be turned into a regex", pattern)
165 format!("'{:?}' cannot be turned into a regex", pattern)
165 }
166 }
166 }
167 }
167 }
168 }
168 }
169 }
169
170
170 impl From<DirstateMapError> for DirstateError {
171 impl From<DirstateMapError> for DirstateError {
171 fn from(e: DirstateMapError) -> Self {
172 fn from(e: DirstateMapError) -> Self {
172 DirstateError::Map(e)
173 DirstateError::Map(e)
173 }
174 }
174 }
175 }
175
176
176 impl From<std::io::Error> for DirstateError {
177 impl From<std::io::Error> for DirstateError {
177 fn from(e: std::io::Error) -> Self {
178 fn from(e: std::io::Error) -> Self {
178 DirstateError::IO(e)
179 DirstateError::IO(e)
179 }
180 }
180 }
181 }
181
182
182 impl From<std::io::Error> for PatternError {
183 impl From<std::io::Error> for PatternError {
183 fn from(e: std::io::Error) -> Self {
184 fn from(e: std::io::Error) -> Self {
184 PatternError::IO(e)
185 PatternError::IO(e)
185 }
186 }
186 }
187 }
187
188
188 impl From<HgPathError> for PatternError {
189 impl From<HgPathError> for PatternError {
189 fn from(e: HgPathError) -> Self {
190 fn from(e: HgPathError) -> Self {
190 PatternError::Path(e)
191 PatternError::Path(e)
191 }
192 }
192 }
193 }
@@ -1,40 +1,30 b''
1 use crate::commands::Command;
1 use crate::commands::Command;
2 use crate::error::{CommandError, CommandErrorKind};
2 use crate::error::CommandError;
3 use crate::ui::Ui;
3 use crate::ui::Ui;
4 use hg::operations::FindRoot;
4 use hg::operations::FindRoot;
5 use hg::requirements;
5
6
6 pub const HELP_TEXT: &str = "
7 pub const HELP_TEXT: &str = "
7 Print the current repo requirements.
8 Print the current repo requirements.
8 ";
9 ";
9
10
10 pub struct DebugRequirementsCommand {}
11 pub struct DebugRequirementsCommand {}
11
12
12 impl DebugRequirementsCommand {
13 impl DebugRequirementsCommand {
13 pub fn new() -> Self {
14 pub fn new() -> Self {
14 DebugRequirementsCommand {}
15 DebugRequirementsCommand {}
15 }
16 }
16 }
17 }
17
18
18 impl Command for DebugRequirementsCommand {
19 impl Command for DebugRequirementsCommand {
19 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
20 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
20 let root = FindRoot::new().run()?;
21 let root = FindRoot::new().run()?;
21 let requires = root.join(".hg").join("requires");
22 let mut output = String::new();
22 let requirements = match std::fs::read(requires) {
23 for req in requirements::load(&root)? {
23 Ok(bytes) => bytes,
24 output.push_str(&req);
24
25 output.push('\n');
25 // Treat a missing file the same as an empty file.
26 }
26 // From `mercurial/localrepo.py`:
27 ui.write_stdout(output.as_bytes())?;
27 // > requires file contains a newline-delimited list of
28 // > features/capabilities the opener (us) must have in order to use
29 // > the repository. This file was introduced in Mercurial 0.9.2,
30 // > which means very old repositories may not have one. We assume
31 // > a missing file translates to no requirements.
32 Err(error) if error.kind() == std::io::ErrorKind::NotFound => Vec::new(),
33
34 Err(error) => Err(CommandErrorKind::FileError(error))?,
35 };
36
37 ui.write_stdout(&requirements)?;
38 Ok(())
28 Ok(())
39 }
29 }
40 }
30 }
@@ -1,117 +1,130 b''
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::utils::files::get_bytes_from_path;
5 use hg::utils::files::get_bytes_from_path;
5 use std::convert::From;
6 use std::convert::From;
6 use std::path::PathBuf;
7 use std::path::PathBuf;
7
8
8 /// The kind of command error
9 /// The kind of command error
9 #[derive(Debug)]
10 #[derive(Debug)]
10 pub enum CommandErrorKind {
11 pub enum CommandErrorKind {
11 /// The root of the repository cannot be found
12 /// The root of the repository cannot be found
12 RootNotFound(PathBuf),
13 RootNotFound(PathBuf),
13 /// The current directory cannot be found
14 /// The current directory cannot be found
14 CurrentDirNotFound(std::io::Error),
15 CurrentDirNotFound(std::io::Error),
15 /// Error while reading or writing a file
16 /// `.hg/requires`
16 // TODO: add the file name/path?
17 RequirementsError(RequirementsError),
17 FileError(std::io::Error),
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::FileError(_) => exitcode::ABORT,
33 CommandErrorKind::RequirementsError(_) => exitcode::ABORT,
34 CommandErrorKind::StdoutError => exitcode::ABORT,
34 CommandErrorKind::StdoutError => exitcode::ABORT,
35 CommandErrorKind::StderrError => exitcode::ABORT,
35 CommandErrorKind::StderrError => exitcode::ABORT,
36 CommandErrorKind::Abort(_) => exitcode::ABORT,
36 CommandErrorKind::Abort(_) => exitcode::ABORT,
37 CommandErrorKind::Unimplemented => exitcode::UNIMPLEMENTED_COMMAND,
37 CommandErrorKind::Unimplemented => exitcode::UNIMPLEMENTED_COMMAND,
38 }
38 }
39 }
39 }
40
40
41 /// Return the message corresponding to the error kind if any
41 /// Return the message corresponding to the error kind if any
42 pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
42 pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
43 match self {
43 match self {
44 // TODO use formating macro
44 // TODO use formating macro
45 CommandErrorKind::RootNotFound(path) => {
45 CommandErrorKind::RootNotFound(path) => {
46 let bytes = get_bytes_from_path(path);
46 let bytes = get_bytes_from_path(path);
47 Some(
47 Some(
48 [
48 [
49 b"abort: no repository found in '",
49 b"abort: no repository found in '",
50 bytes.as_slice(),
50 bytes.as_slice(),
51 b"' (.hg not found)!\n",
51 b"' (.hg not found)!\n",
52 ]
52 ]
53 .concat(),
53 .concat(),
54 )
54 )
55 }
55 }
56 // TODO use formating macro
56 // TODO use formating macro
57 CommandErrorKind::CurrentDirNotFound(e) => Some(
57 CommandErrorKind::CurrentDirNotFound(e) => Some(
58 [
58 [
59 b"abort: error getting current working directory: ",
59 b"abort: error getting current working directory: ",
60 e.to_string().as_bytes(),
60 e.to_string().as_bytes(),
61 b"\n",
61 b"\n",
62 ]
62 ]
63 .concat(),
63 .concat(),
64 ),
64 ),
65 CommandErrorKind::RequirementsError(
66 RequirementsError::Corrupted,
67 ) => Some(
68 "abort: .hg/requires is corrupted\n".as_bytes().to_owned(),
69 ),
65 CommandErrorKind::Abort(message) => message.to_owned(),
70 CommandErrorKind::Abort(message) => message.to_owned(),
66 _ => None,
71 _ => None,
67 }
72 }
68 }
73 }
69 }
74 }
70
75
71 /// The error type for the Command trait
76 /// The error type for the Command trait
72 #[derive(Debug)]
77 #[derive(Debug)]
73 pub struct CommandError {
78 pub struct CommandError {
74 pub kind: CommandErrorKind,
79 pub kind: CommandErrorKind,
75 }
80 }
76
81
77 impl CommandError {
82 impl CommandError {
78 /// Exist the process with the corresponding exit code.
83 /// Exist the process with the corresponding exit code.
79 pub fn exit(&self) {
84 pub fn exit(&self) {
80 std::process::exit(self.kind.get_exit_code())
85 std::process::exit(self.kind.get_exit_code())
81 }
86 }
82
87
83 /// Return the message corresponding to the command error if any
88 /// Return the message corresponding to the command error if any
84 pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
89 pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> {
85 self.kind.get_error_message_bytes()
90 self.kind.get_error_message_bytes()
86 }
91 }
87 }
92 }
88
93
89 impl From<CommandErrorKind> for CommandError {
94 impl From<CommandErrorKind> for CommandError {
90 fn from(kind: CommandErrorKind) -> Self {
95 fn from(kind: CommandErrorKind) -> Self {
91 CommandError { kind }
96 CommandError { kind }
92 }
97 }
93 }
98 }
94
99
95 impl From<UiError> for CommandError {
100 impl From<UiError> for CommandError {
96 fn from(error: UiError) -> Self {
101 fn from(error: UiError) -> Self {
97 CommandError {
102 CommandError {
98 kind: match error {
103 kind: match error {
99 UiError::StdoutError(_) => CommandErrorKind::StdoutError,
104 UiError::StdoutError(_) => CommandErrorKind::StdoutError,
100 UiError::StderrError(_) => CommandErrorKind::StderrError,
105 UiError::StderrError(_) => CommandErrorKind::StderrError,
101 },
106 },
102 }
107 }
103 }
108 }
104 }
109 }
105
110
106 impl From<FindRootError> for CommandError {
111 impl From<FindRootError> for CommandError {
107 fn from(err: FindRootError) -> Self {
112 fn from(err: FindRootError) -> Self {
108 match err.kind {
113 match err.kind {
109 FindRootErrorKind::RootNotFound(path) => CommandError {
114 FindRootErrorKind::RootNotFound(path) => CommandError {
110 kind: CommandErrorKind::RootNotFound(path),
115 kind: CommandErrorKind::RootNotFound(path),
111 },
116 },
112 FindRootErrorKind::GetCurrentDirError(e) => CommandError {
117 FindRootErrorKind::GetCurrentDirError(e) => CommandError {
113 kind: CommandErrorKind::CurrentDirNotFound(e),
118 kind: CommandErrorKind::CurrentDirNotFound(e),
114 },
119 },
115 }
120 }
116 }
121 }
117 }
122 }
123
124 impl From<RequirementsError> for CommandError {
125 fn from(err: RequirementsError) -> Self {
126 CommandError {
127 kind: CommandErrorKind::RequirementsError(err),
128 }
129 }
130 }
General Comments 0
You need to be logged in to leave comments. Login now