#![allow(
dead_code,
reason = "each test crate that includes this file uses only some of it"
)]
use jsonvet::{Config, Dialect, Rule, Severity, check};
pub(crate) fn recount(text: &str, offset: usize, dialect: Dialect) -> (usize, usize) {
let json5 = dialect == Dialect::Json5;
let (mut line, mut column) = (1, 1);
for (i, c) in text.char_indices().take_while(|&(i, _)| i < offset) {
match c {
'\r' if text[i + 1..].starts_with('\n') => column += 1,
'\n' | '\r' => (line, column) = (line + 1, 1),
'\u{2028}' | '\u{2029}' if json5 => (line, column) = (line + 1, 1),
_ => column += 1,
}
}
(line, column)
}
pub(crate) fn strict() -> Config {
Config::new(Dialect::Json5)
.rule(Rule::InvalidNumber, Severity::Error)
.max_depth(8)
}
pub(crate) fn report_violation(text: &str, config: &Config) -> Result<(), String> {
let dialect = config.dialect();
let report = check(text, config);
let problems = report.problems();
if problems.windows(2).any(|w| w[0].offset() > w[1].offset()) {
return Err(format!("{dialect}: problems out of order"));
}
let offsets = problems
.iter()
.flat_map(|p| std::iter::once(p.offset()).chain(p.related().map(|r| r.offset)));
for offset in offsets
.clone()
.chain(problems.iter().rev().map(jsonvet::Problem::offset))
{
if offset > text.len() || !text.is_char_boundary(offset) {
return Err(format!(
"{dialect}: offset {offset} outside the text or inside a character"
));
}
let at = report.location(offset);
if (at.line, at.column) != recount(text, offset, dialect) {
return Err(format!("{dialect}: wrong location for offset {offset}"));
}
}
if problems.iter().any(|p| p.to_string().is_empty()) {
return Err(format!("{dialect}: a problem with no message"));
}
if report.passed() != problems.iter().all(|p| p.severity() != Severity::Error) {
return Err(format!("{dialect}: `passed` disagrees with the problems"));
}
Ok(())
}
fn passes(text: &str, dialect: Dialect) -> bool {
check(
text,
&Config::new(dialect).rule(Rule::DuplicateKey, Severity::Off),
)
.passed()
}
pub(crate) fn nesting_violations(text: &str) -> Vec<String> {
let mut pairs = vec![
(Dialect::IJson, Dialect::Json),
(Dialect::Json, Dialect::Jsonc),
(Dialect::Json, Dialect::Jwcc),
(Dialect::Json, Dialect::Json5),
];
if !text.contains(['\r', '\u{2028}', '\u{2029}']) {
pairs.extend([
(Dialect::Jsonc, Dialect::Jwcc),
(Dialect::Jwcc, Dialect::Json5),
]);
}
pairs
.into_iter()
.filter(|&(narrow, wide)| passes(text, narrow) && !passes(text, wide))
.map(|(narrow, wide)| format!("passes as {narrow} but not {wide}"))
.collect()
}