Show More
@@ -1,72 +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 |
|
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() && line.is_ascii() { |
|
27 | if line[0].is_ascii_alphanumeric() && line.is_ascii() { | |
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 |
|
54 | |||
55 | pub fn check(repo_root: &Path) -> Result<(), RequirementsError> { |
|
55 | pub fn check(repo_root: &Path) -> Result<(), RequirementsError> { | |
56 | for feature in load(repo_root)? { |
|
56 | for feature in load(repo_root)? { | |
57 | if !SUPPORTED.contains(&&*feature) { |
|
57 | if !SUPPORTED.contains(&&*feature) { | |
58 | return Err(RequirementsError::Unsupported { feature }) |
|
58 | return Err(RequirementsError::Unsupported { feature }); | |
59 | } |
|
59 | } | |
60 | } |
|
60 | } | |
61 | Ok(()) |
|
61 | Ok(()) | |
62 | } |
|
62 | } | |
63 |
|
63 | |||
64 | // TODO: set this to actually-supported features |
|
64 | // TODO: set this to actually-supported features | |
65 | const SUPPORTED: &[&str] = &[ |
|
65 | const SUPPORTED: &[&str] = &[ | |
66 | "dotencode", |
|
66 | "dotencode", | |
67 | "fncache", |
|
67 | "fncache", | |
68 | "generaldelta", |
|
68 | "generaldelta", | |
69 | "revlogv1", |
|
69 | "revlogv1", | |
70 | "sparserevlog", |
|
70 | "sparserevlog", | |
71 | "store", |
|
71 | "store", | |
72 | ]; |
|
72 | ]; |
General Comments 0
You need to be logged in to leave comments.
Login now