blob: c3487565c23edc5ee6c8f47fa293289035493287 [file] [log] [blame] [edit]
use std::process::ExitCode;
use std::sync::LazyLock;
use std::{env, fs};
use regex::{Regex, RegexBuilder};
mod cache;
mod config;
mod directive;
mod error;
use cache::Cache;
use config::parse_config;
use directive::{Directive, DirectiveKind};
use error::CkError;
fn main() -> ExitCode {
let config = parse_config(env::args().collect());
let mut failed = Vec::new();
let mut cache = Cache::new(&config);
let Ok(directives) = get_directives(&config.template) else {
eprintln!("Jsondocck failed for {}", &config.template);
return ExitCode::FAILURE;
};
for directive in directives {
if let Err(message) = directive.check(&mut cache) {
failed.push(CkError { directive, message });
}
}
if failed.is_empty() {
ExitCode::SUCCESS
} else {
for i in failed {
eprintln!("{}:{}, directive failed", config.template, i.directive.lineno);
eprintln!("{}", i.message)
}
ExitCode::FAILURE
}
}
static LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
RegexBuilder::new(
r#"
^\s*
//@\s+
(?P<negated>!?)
(?P<directive>.+?)
(?:[\s:](?P<args>.*))?$
"#,
)
.ignore_whitespace(true)
.unicode(true)
.build()
.unwrap()
});
static DEPRECATED_LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
RegexBuilder::new(r"//\s+@").ignore_whitespace(true).unicode(true).build().unwrap()
});
fn print_err(msg: &str, lineno: usize) {
eprintln!("Invalid directive: {} on line {}", msg, lineno)
}
/// Get a list of directives from a file.
fn get_directives(template: &str) -> Result<Vec<Directive>, ()> {
let mut directives = Vec::new();
let mut errors = false;
let file = fs::read_to_string(template).unwrap();
for (lineno, line) in file.split('\n').enumerate() {
let lineno = lineno + 1;
if DEPRECATED_LINE_PATTERN.is_match(line) {
print_err("Deprecated directive syntax, replace `// @` with `//@ `", lineno);
errors = true;
continue;
}
let Some(cap) = LINE_PATTERN.captures(line) else {
continue;
};
let negated = &cap["negated"] == "!";
let args_str = cap.name("args").map(|m| m.as_str()).unwrap_or_default();
let Some(args) = shlex::split(args_str) else {
print_err(&format!("Invalid arguments to shlex::split: `{args_str}`",), lineno);
errors = true;
continue;
};
if let Some((kind, path)) = DirectiveKind::parse(&cap["directive"], negated, &args) {
directives.push(Directive { kind, lineno, path: path.to_owned() })
}
}
if !errors { Ok(directives) } else { Err(()) }
}