use serde_json::{Value as JsonValue, json};
const SEVERITY_ERROR: i32 = 1;
#[must_use]
pub fn publish_diagnostics(uri: &str, text: &str) -> Option<String> {
let diagnostics = collect(text);
let params = json!({
"uri": uri,
"diagnostics": diagnostics,
});
let note = json!({
"jsonrpc": "2.0",
"method": "textDocument/publishDiagnostics",
"params": params,
});
Some(note.to_string())
}
#[must_use]
pub fn collect(text: &str) -> Vec<JsonValue> {
let mut diagnostics = Vec::new();
if let Err(err) = noyalib::from_str::<noyalib::Value>(text) {
let (line, character) = location_from_error(&err, text);
diagnostics.push(json!({
"range": {
"start": {"line": line, "character": character},
"end": {"line": line, "character": character + 1},
},
"severity": SEVERITY_ERROR,
"source": "noyalib",
"message": err.to_string(),
}));
}
diagnostics
}
fn location_from_error(_err: &noyalib::Error, _text: &str) -> (usize, usize) {
(0, 0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn collect_returns_empty_on_valid_yaml() {
assert!(collect("a: 1\nb: 2\n").is_empty());
}
#[test]
fn collect_returns_one_diagnostic_per_parse_error() {
let d = collect("a: [\n");
assert_eq!(d.len(), 1);
assert_eq!(d[0]["severity"].as_i64(), Some(1));
assert_eq!(d[0]["source"].as_str(), Some("noyalib"));
assert!(!d[0]["message"].as_str().unwrap().is_empty());
}
#[test]
fn publish_diagnostics_wraps_in_jsonrpc_envelope() {
let s = publish_diagnostics("file:///tmp/a.yaml", "a: 1\n").unwrap();
let v: JsonValue = serde_json::from_str(&s).unwrap();
assert_eq!(v["jsonrpc"].as_str(), Some("2.0"));
assert_eq!(
v["method"].as_str(),
Some("textDocument/publishDiagnostics"),
);
assert_eq!(v["params"]["uri"].as_str(), Some("file:///tmp/a.yaml"));
let diags = v["params"]["diagnostics"].as_array().unwrap();
assert!(diags.is_empty());
}
#[test]
fn publish_diagnostics_includes_errors_for_invalid_yaml() {
let s = publish_diagnostics("file:///tmp/a.yaml", "k: [\n").unwrap();
let v: JsonValue = serde_json::from_str(&s).unwrap();
let diags = v["params"]["diagnostics"].as_array().unwrap();
assert_eq!(diags.len(), 1);
}
#[test]
fn diagnostic_range_is_well_formed() {
let d = collect("a: [\n");
let range = &d[0]["range"];
assert!(range["start"]["line"].is_u64());
assert!(range["end"]["line"].is_u64());
}
}