view rust/hg-core/src/requirements.rs @ 45956:40f79997e81f

rust: fix non-utf8 char in requirements.rs Apparently Phabricator detect `rust/hg-core/src/requirements.rs` file as non utf8 ‽, and mark it as binary. During application it ended up being non-utf8 and this made Rust (and as a result heptapod) very angry:: error: couldn't read hg-core/src/requirements.rs: stream did not contain valid UTF-8 --> hg-core/src/lib.rs:11:9 | 11 | pub mod requirements; | ^^^^^^^^^^^^ error: aborting due to previous error error: could not compile `hg-core`. The after "fixing", the file content has no character matching the following regexp: [^0-9-a-zA-Z /(|).,{}!\[\]:"&=>?_*-;<`'#] So we should be fine, unless Phabricator does something funny again. Differential Revision: https://phab.mercurial-scm.org/D9444
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Sat, 28 Nov 2020 14:29:50 +0100
parents f5d62f4d5327
children 9eb07ab3f2d4
line wrap: on
line source

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() && line.is_ascii() {
                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))?,
    }
}

pub fn check(repo_root: &Path) -> Result<(), RequirementsError> {
    for feature in load(repo_root)? {
        if !SUPPORTED.contains(&&*feature) {
            return Err(RequirementsError::Unsupported { feature });
        }
    }
    Ok(())
}

// TODO: set this to actually-supported features
const SUPPORTED: &[&str] = &[
    "dotencode",
    "fncache",
    "generaldelta",
    "revlogv1",
    "sparserevlog",
    "store",
];