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 | } |
@@ -8,6 +8,7 b' pub mod dagops;' | |||
|
8 | 8 | pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors}; |
|
9 | 9 | mod dirstate; |
|
10 | 10 | pub mod discovery; |
|
11 | pub mod requirements; | |
|
11 | 12 | pub mod testing; // unconditionally built, for use from integration tests |
|
12 | 13 | pub use dirstate::{ |
|
13 | 14 | dirs_multiset::{DirsMultiset, DirsMultisetIter}, |
@@ -1,7 +1,8 b'' | |||
|
1 | 1 | use crate::commands::Command; |
|
2 |
use crate::error:: |
|
|
2 | use crate::error::CommandError; | |
|
3 | 3 | use crate::ui::Ui; |
|
4 | 4 | use hg::operations::FindRoot; |
|
5 | use hg::requirements; | |
|
5 | 6 | |
|
6 | 7 | pub const HELP_TEXT: &str = " |
|
7 | 8 | Print the current repo requirements. |
@@ -18,23 +19,12 b' impl DebugRequirementsCommand {' | |||
|
18 | 19 | impl Command for DebugRequirementsCommand { |
|
19 | 20 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { |
|
20 | 21 | let root = FindRoot::new().run()?; |
|
21 | let requires = root.join(".hg").join("requires"); | |
|
22 | let requirements = match std::fs::read(requires) { | |
|
23 | Ok(bytes) => bytes, | |
|
24 | ||
|
25 | // Treat a missing file the same as an empty file. | |
|
26 | // From `mercurial/localrepo.py`: | |
|
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)?; | |
|
22 | let mut output = String::new(); | |
|
23 | for req in requirements::load(&root)? { | |
|
24 | output.push_str(&req); | |
|
25 | output.push('\n'); | |
|
26 | } | |
|
27 | ui.write_stdout(output.as_bytes())?; | |
|
38 | 28 | Ok(()) |
|
39 | 29 | } |
|
40 | 30 | } |
@@ -1,6 +1,7 b'' | |||
|
1 | 1 | use crate::exitcode; |
|
2 | 2 | use crate::ui::UiError; |
|
3 | 3 | use hg::operations::{FindRootError, FindRootErrorKind}; |
|
4 | use hg::requirements::RequirementsError; | |
|
4 | 5 | use hg::utils::files::get_bytes_from_path; |
|
5 | 6 | use std::convert::From; |
|
6 | 7 | use std::path::PathBuf; |
@@ -12,9 +13,8 b' pub enum CommandErrorKind {' | |||
|
12 | 13 | RootNotFound(PathBuf), |
|
13 | 14 | /// The current directory cannot be found |
|
14 | 15 | CurrentDirNotFound(std::io::Error), |
|
15 | /// Error while reading or writing a file | |
|
16 | // TODO: add the file name/path? | |
|
17 | FileError(std::io::Error), | |
|
16 | /// `.hg/requires` | |
|
17 | RequirementsError(RequirementsError), | |
|
18 | 18 | /// The standard output stream cannot be written to |
|
19 | 19 | StdoutError, |
|
20 | 20 | /// The standard error stream cannot be written to |
@@ -30,7 +30,7 b' impl CommandErrorKind {' | |||
|
30 | 30 | match self { |
|
31 | 31 | CommandErrorKind::RootNotFound(_) => exitcode::ABORT, |
|
32 | 32 | CommandErrorKind::CurrentDirNotFound(_) => exitcode::ABORT, |
|
33 |
CommandErrorKind:: |
|
|
33 | CommandErrorKind::RequirementsError(_) => exitcode::ABORT, | |
|
34 | 34 | CommandErrorKind::StdoutError => exitcode::ABORT, |
|
35 | 35 | CommandErrorKind::StderrError => exitcode::ABORT, |
|
36 | 36 | CommandErrorKind::Abort(_) => exitcode::ABORT, |
@@ -62,6 +62,11 b' impl CommandErrorKind {' | |||
|
62 | 62 | ] |
|
63 | 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 | 70 | CommandErrorKind::Abort(message) => message.to_owned(), |
|
66 | 71 | _ => None, |
|
67 | 72 | } |
@@ -115,3 +120,11 b' impl From<FindRootError> for CommandErro' | |||
|
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