use std::fmt::Display;
use std::process::ExitCode;
use loftspec::check::Placed;
use loftspec::codec::{ReadError, Site};
use loftspec::lint::Finding;
use loftspec::yaml::Location;
use serde_json::{Map, Value, json};
pub fn site(site: &Site) -> Map<String, Value> {
let mut fields = Map::new();
fields.insert("path".to_owned(), json!(site.path.to_string()));
match site.location {
Some(Location::At(position)) => {
fields.insert("line".to_owned(), json!(position.line));
fields.insert("column".to_owned(), json!(position.column));
}
Some(Location::Byte(offset)) => {
fields.insert("byte".to_owned(), json!(offset));
}
None => {}
}
fields
}
pub fn error(kind: &str, message: &str, sites: &[Site]) -> Value {
let mut fields = sites.first().map(site).unwrap_or_default();
fields.insert("kind".to_owned(), json!(kind));
fields.insert("message".to_owned(), json!(message));
fields.insert(
"sites".to_owned(),
Value::Array(sites.iter().map(|s| Value::Object(site(s))).collect()),
);
Value::Object(fields)
}
pub fn read_error(kind: &str, error: &ReadError) -> Value {
self::error(kind, &error.problem.to_string(), &error.sites)
}
pub fn read_errors(errors: &[ReadError]) -> Vec<Value> {
errors
.iter()
.map(|error| read_error("read", error))
.collect()
}
pub fn validation_errors(errors: &[Placed]) -> Vec<Value> {
errors
.iter()
.map(|placed| self::error("validation", &placed.error.to_string(), &placed.sites))
.collect()
}
pub fn finding(finding: &Finding) -> Value {
let mut fields = finding
.subjects
.first()
.and_then(|subject| subject.site.as_ref())
.map(site)
.unwrap_or_default();
fields.insert("rule".to_owned(), json!(finding.rule.as_str()));
fields.insert("severity".to_owned(), json!(finding.severity.as_str()));
fields.insert("message".to_owned(), json!(finding.message));
if let Some(term) = &finding.term {
fields.insert("term".to_owned(), json!(term));
}
if let Some(line) = finding.body_line {
fields.insert("bodyLine".to_owned(), json!(line));
}
let subjects = finding.subjects.iter().map(|subject| {
let mut fields = subject.site.as_ref().map(site).unwrap_or_default();
fields.insert("key".to_owned(), json!(subject.key.to_string()));
Value::Object(fields)
});
fields.insert("subjects".to_owned(), Value::Array(subjects.collect()));
Value::Object(fields)
}
pub fn result(schema: &str, outcome: &str, mut fields: Value, errors: Vec<Value>) -> Value {
fields["schema"] = json!(schema);
fields["outcome"] = json!(outcome);
fields["errors"] = Value::Array(errors);
fields
}
pub fn failure(schema: &str, errors: Vec<Value>) -> Value {
result(schema, "error", json!({}), errors)
}
pub fn print(document: &Value) {
println!(
"{}",
serde_json::to_string(document).expect("a JSON value serializes")
);
}
pub fn stop(schema: &str, errors: Vec<Value>, message: &impl Display, as_json: bool) -> ExitCode {
let lines = vec![message.to_string()];
crate::stop::Stop { errors, lines }.exit(schema, as_json)
}