Commit c8919aea authored by Simon Sapin's avatar Simon Sapin
Browse files

requirements: move loading to hg-core and add parsing

No functional change, checking comes later.
parent e3e7f64d10b6
......@@ -8,6 +8,7 @@ pub mod dagops;
pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
mod dirstate;
pub mod discovery;
pub mod requirements;
pub mod testing; // unconditionally built, for use from integration tests
pub use dirstate::{
dirs_multiset::{DirsMultiset, DirsMultisetIter},
......
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))?,
}
}
use crate::commands::Command;
use crate::error::{CommandError, CommandErrorKind};
use crate::error::CommandError;
use crate::ui::Ui;
use hg::operations::FindRoot;
use hg::requirements;
pub const HELP_TEXT: &str = "
Print the current repo requirements.
......@@ -18,23 +19,12 @@ impl DebugRequirementsCommand {
impl Command for DebugRequirementsCommand {
fn run(&self, ui: &Ui) -> Result<(), CommandError> {
let root = FindRoot::new().run()?;
let requires = root.join(".hg").join("requires");
let requirements = match std::fs::read(requires) {
Ok(bytes) => bytes,
// 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 => Vec::new(),
Err(error) => Err(CommandErrorKind::FileError(error))?,
};
ui.write_stdout(&requirements)?;
let mut output = String::new();
for req in requirements::load(&root)? {
output.push_str(&req);
output.push('\n');
}
ui.write_stdout(output.as_bytes())?;
Ok(())
}
}
use crate::exitcode;
use crate::ui::UiError;
use hg::operations::{FindRootError, FindRootErrorKind};
use hg::requirements::RequirementsError;
use hg::utils::files::get_bytes_from_path;
use std::convert::From;
use std::path::PathBuf;
......@@ -12,9 +13,8 @@ pub enum CommandErrorKind {
RootNotFound(PathBuf),
/// The current directory cannot be found
CurrentDirNotFound(std::io::Error),
/// Error while reading or writing a file
// TODO: add the file name/path?
FileError(std::io::Error),
/// `.hg/requires`
RequirementsError(RequirementsError),
/// The standard output stream cannot be written to
StdoutError,
/// The standard error stream cannot be written to
......@@ -30,7 +30,7 @@ impl CommandErrorKind {
match self {
CommandErrorKind::RootNotFound(_) => exitcode::ABORT,
CommandErrorKind::CurrentDirNotFound(_) => exitcode::ABORT,
CommandErrorKind::FileError(_) => exitcode::ABORT,
CommandErrorKind::RequirementsError(_) => exitcode::ABORT,
CommandErrorKind::StdoutError => exitcode::ABORT,
CommandErrorKind::StderrError => exitcode::ABORT,
CommandErrorKind::Abort(_) => exitcode::ABORT,
......@@ -62,6 +62,11 @@ impl CommandErrorKind {
]
.concat(),
),
CommandErrorKind::RequirementsError(
RequirementsError::Corrupted,
) => Some(
"abort: .hg/requires is corrupted\n".as_bytes().to_owned(),
),
CommandErrorKind::Abort(message) => message.to_owned(),
_ => None,
}
......@@ -115,3 +120,11 @@ impl From<FindRootError> for CommandError {
}
}
}
impl From<RequirementsError> for CommandError {
fn from(err: RequirementsError) -> Self {
CommandError {
kind: CommandErrorKind::RequirementsError(err),
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment