use crate::diagnostics::{Diagnostic, Severity};
use crate::error::TldrError;
use regex::Regex;
use std::path::PathBuf;
pub fn parse_luacheck_output(output: &str) -> Result<Vec<Diagnostic>, TldrError> {
if output.trim().is_empty() {
return Ok(Vec::new());
}
let regex = Regex::new(r"^(.+\.lua):(\d+):(\d+):\s*\(([EW]\d+)\)\s*(.+)$")
.expect("Invalid luacheck regex pattern");
let mut diagnostics = Vec::new();
for line in output.lines() {
let line = line.trim();
if line.is_empty() {
continue;
}
if let Some(captures) = regex.captures(line) {
let file = captures.get(1).map(|m| m.as_str()).unwrap_or("");
let line_num: u32 = captures
.get(2)
.and_then(|m| m.as_str().parse().ok())
.unwrap_or(1);
let column: u32 = captures
.get(3)
.and_then(|m| m.as_str().parse().ok())
.unwrap_or(1);
let code = captures.get(4).map(|m| m.as_str().to_string());
let message = captures
.get(5)
.map(|m| m.as_str())
.unwrap_or("")
.to_string();
let severity = match code.as_ref().map(|c| c.chars().next()) {
Some(Some('E')) => Severity::Error,
Some(Some('W')) => Severity::Warning,
_ => Severity::Warning,
};
diagnostics.push(Diagnostic {
file: PathBuf::from(file),
line: line_num,
column,
end_line: None,
end_column: None,
severity,
message,
code,
source: "luacheck".to_string(),
url: None,
});
}
}
Ok(diagnostics)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_warning() {
let output = "src/main.lua:10:5: (W611) line contains trailing whitespace";
let result = parse_luacheck_output(output).unwrap();
assert_eq!(result.len(), 1);
let d = &result[0];
assert_eq!(d.file, PathBuf::from("src/main.lua"));
assert_eq!(d.line, 10);
assert_eq!(d.column, 5);
assert_eq!(d.severity, Severity::Warning);
assert_eq!(d.code, Some("W611".to_string()));
assert_eq!(d.message, "line contains trailing whitespace");
assert_eq!(d.source, "luacheck");
}
#[test]
fn test_parse_error() {
let output = "src/main.lua:15:1: (E011) expected expression near 'end'";
let result = parse_luacheck_output(output).unwrap();
assert_eq!(result.len(), 1);
let d = &result[0];
assert_eq!(d.severity, Severity::Error);
assert_eq!(d.code, Some("E011".to_string()));
}
#[test]
fn test_parse_unused_variable() {
let output = "src/utils.lua:3:7: (W211) unused variable 'helper'";
let result = parse_luacheck_output(output).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].code, Some("W211".to_string()));
assert_eq!(result[0].message, "unused variable 'helper'");
}
#[test]
fn test_parse_multiple() {
let output = r#"src/main.lua:10:5: (W611) trailing whitespace
src/main.lua:15:1: (E011) expected expression near 'end'
src/utils.lua:3:7: (W211) unused variable 'helper'"#;
let result = parse_luacheck_output(output).unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result[0].severity, Severity::Warning);
assert_eq!(result[1].severity, Severity::Error);
assert_eq!(result[2].severity, Severity::Warning);
}
#[test]
fn test_parse_empty() {
let result = parse_luacheck_output("").unwrap();
assert!(result.is_empty());
}
#[test]
fn test_parse_malformed() {
let output = "Total: 0 warnings / 0 errors in 3 files";
let result = parse_luacheck_output(output).unwrap();
assert!(result.is_empty());
}
#[test]
fn test_parse_summary_line_ignored() {
let output = r#"src/main.lua:10:5: (W611) trailing whitespace
Checking src/main.lua 1 warning
Total: 1 warning / 0 errors in 1 file"#;
let result = parse_luacheck_output(output).unwrap();
assert_eq!(result.len(), 1);
}
}