view rust/hg-core/src/requirements.rs @ 45973:ed0e1339e4a8

copies-rust: combine the iteration over remove and copies into one In the underlying data, the copies information and the removal information are interleaved. And in the consumer code, the consumption could be interleaved too. So, we make the processing closer to the underlying data by fusing the two iterators into one. Later, we will directly consume the underlying data and that logic to combine the two iterators will be unnecessary. Differential Revision: https://phab.mercurial-scm.org/D9304
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Mon, 05 Oct 2020 01:31:32 +0200
parents 40f79997e81f
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",
];