##// 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 }
@@ -8,6 +8,7 b' 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},
@@ -1,7 +1,8 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.
@@ -18,23 +19,12 b' impl DebugRequirementsCommand {'
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,6 +1,7 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;
@@ -12,9 +13,8 b' pub enum CommandErrorKind {'
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
@@ -30,7 +30,7 b' impl CommandErrorKind {'
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,
@@ -62,6 +62,11 b' impl CommandErrorKind {'
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 }
@@ -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