use std::path::{Path, PathBuf};
#[derive(Debug)]
pub(crate) struct Exemption {
pub(crate) op: String,
pub(crate) why: String,
}
const CITE: &str = "bl-";
pub(crate) fn path() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("parity.toml")
}
pub(crate) fn read() -> Vec<Exemption> {
let text = std::fs::read_to_string(path()).expect("parity.toml");
parse(&text).unwrap_or_else(|why| panic!("parity.toml: {why}"))
}
pub(crate) fn parse(text: &str) -> Result<Vec<Exemption>, String> {
let mut rows: Vec<Exemption> = Vec::new();
for (n, line) in text.lines().enumerate() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let at = n + 1;
let (op, rest) = line
.split_once('=')
.ok_or_else(|| format!("line {at} is not `op = \"reason\"`"))?;
let op = op.trim();
if op.is_empty() || op.split_whitespace().count() != 1 {
return Err(format!("line {at} names no single op"));
}
let quoted = rest.trim();
let why = quoted
.strip_prefix('"')
.and_then(|rest| rest.strip_suffix('"'))
.ok_or_else(|| format!("line {at}: the reason for {op:?} is not a quoted string"))?;
if !why.contains(CITE) {
return Err(format!(
"line {at}: the reason for {op:?} cites no ball — a reason with no citation \
cannot be checked for still being true"
));
}
if rows.iter().any(|row| row.op == op) {
return Err(format!("line {at}: {op:?} is recorded twice"));
}
rows.push(Exemption {
op: op.to_owned(),
why: why.to_owned(),
});
}
Ok(rows)
}