diff rust/hg-core/src/requirements.rs @ 45939:a2eda1ff22aa

requirements: move loading to hg-core and add parsing No functional change, checking comes later. Differential Revision: https://phab.mercurial-scm.org/D9398
author Simon Sapin <simon-commits@exyr.org>
date Tue, 24 Nov 2020 17:49:16 +0100
parents
children 2ad2745e0be9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/requirements.rs	Tue Nov 24 17:49:16 2020 +0100
@@ -0,0 +1,53 @@
+use std::io;
+use std::path::Path;
+
+#[derive(Debug)]
+pub enum RequirementsError {
+    // TODO: include a path?
+    Io(io::Error),
+    /// The `requires` file is corrupted
+    Corrupted,
+    /// The repository requires a feature that we don’t support
+    Unsupported {
+        feature: String,
+    },
+}
+
+fn parse(bytes: &[u8]) -> Result<Vec<String>, ()> {
+    // The Python code reading this file uses `str.splitlines`
+    // which looks for a number of line separators (even including a couple of
+    // non-ASCII ones), but Python code writing it always uses `\n`.
+    let lines = bytes.split(|&byte| byte == b'\n');
+
+    lines
+        .filter(|line| !line.is_empty())
+        .map(|line| {
+            // Python uses Unicode `str.isalnum` but feature names are all
+            // ASCII
+            if line[0].is_ascii_alphanumeric() {
+                Ok(String::from_utf8(line.into()).unwrap())
+            } else {
+                Err(())
+            }
+        })
+        .collect()
+}
+
+pub fn load(repo_root: &Path) -> Result<Vec<String>, RequirementsError> {
+    match std::fs::read(repo_root.join(".hg").join("requires")) {
+        Ok(bytes) => parse(&bytes).map_err(|()| RequirementsError::Corrupted),
+
+        // Treat a missing file the same as an empty file.
+        // From `mercurial/localrepo.py`:
+        // > requires file contains a newline-delimited list of
+        // > features/capabilities the opener (us) must have in order to use
+        // > the repository. This file was introduced in Mercurial 0.9.2,
+        // > which means very old repositories may not have one. We assume
+        // > a missing file translates to no requirements.
+        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
+            Ok(Vec::new())
+        }
+
+        Err(error) => Err(RequirementsError::Io(error))?,
+    }
+}