use std::path::Path;
use std::process::ExitCode;
use loftspec::check::{Checked, check};
use loftspec::lint::Severity;
use loftspec_store::FileStore;
use serde_json::{Value, json};
use crate::json;
const SCHEMA: &str = "loft-check/1";
pub fn run(root: &Path, fail_on: Option<Severity>, as_json: bool) -> ExitCode {
let files = match FileStore::open(root).and_then(|store| store.read_files()) {
Ok(files) => files,
Err(error) => {
if as_json {
json::print(&json::failure(SCHEMA, vec![json::store_error(&error)]));
}
eprintln!("{error}");
return ExitCode::FAILURE;
}
};
let checked = check(&files);
let failed_on = fail_on.is_some_and(|threshold| {
checked
.findings()
.iter()
.any(|finding| finding.severity >= threshold)
});
let failed = checked.has_errors() || failed_on;
if as_json {
json::print(&document(
&checked,
if failed { "failed" } else { "passed" },
));
} else {
print(&checked);
}
if failed {
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
}
}
fn print(checked: &Checked) {
match checked {
Checked::Unread(errors) => {
for error in errors {
println!("{error}");
}
}
Checked::Read {
invalid,
config,
findings,
} => {
for error in invalid {
println!("{error}");
}
for error in config {
println!("{error}");
}
for finding in findings {
println!("{finding}");
}
}
}
}
fn document(checked: &Checked, outcome: &str) -> Value {
let (errors, findings): (Vec<Value>, Vec<Value>) = match checked {
Checked::Unread(errors) => (json::read_errors(errors), Vec::new()),
Checked::Read {
invalid,
config,
findings,
} => (
json::validation_errors(invalid)
.into_iter()
.chain(config.iter().map(|e| json::read_error("lint-config", e)))
.collect(),
findings.iter().map(json::finding).collect(),
),
};
json::result(SCHEMA, outcome, json!({ "findings": findings }), errors)
}