use crate::diagnostics::{Diagnostic, Severity};
use crate::error::TldrError;
use regex::Regex;
use std::path::PathBuf;
pub fn tsc_output_regex() -> Regex {
Regex::new(r"^(.+)\((\d+),(\d+)\):\s*(error|warning)\s+(TS\d+):\s*(.+)$")
.expect("Invalid tsc regex pattern")
}
pub fn parse_tsc_text(output: &str) -> Result<Vec<Diagnostic>, TldrError> {
let regex = tsc_output_regex();
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 severity_str = captures.get(4).map(|m| m.as_str()).unwrap_or("error");
let code = captures.get(5).map(|m| m.as_str().to_string());
let message = captures
.get(6)
.map(|m| m.as_str())
.unwrap_or("")
.to_string();
let severity = match severity_str.to_lowercase().as_str() {
"error" => Severity::Error,
"warning" => Severity::Warning,
_ => Severity::Error,
};
diagnostics.push(Diagnostic {
file: PathBuf::from(file),
line: line_num,
column,
end_line: None,
end_column: None,
severity,
message,
code,
source: "tsc".to_string(),
url: None,
});
}
}
Ok(diagnostics)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_error() {
let output =
"src/auth.ts(42,5): error TS2339: Property 'foo' does not exist on type 'Bar'.";
let result = parse_tsc_text(output).unwrap();
assert_eq!(result.len(), 1);
let d = &result[0];
assert_eq!(d.file, PathBuf::from("src/auth.ts"));
assert_eq!(d.line, 42);
assert_eq!(d.column, 5);
assert_eq!(d.severity, Severity::Error);
assert_eq!(d.code, Some("TS2339".to_string()));
assert_eq!(d.source, "tsc");
}
#[test]
fn test_parse_warning() {
let output = "src/utils.ts(15,1): warning TS6385: 'x' is deprecated.";
let result = parse_tsc_text(output).unwrap();
assert_eq!(result.len(), 1);
let d = &result[0];
assert_eq!(d.severity, Severity::Warning);
assert_eq!(d.code, Some("TS6385".to_string()));
}
#[test]
fn test_parse_multiple() {
let output = r#"src/auth.ts(42,5): error TS2339: Property 'foo' does not exist.
src/auth.ts(58,10): error TS2345: Argument type mismatch.
src/utils.ts(15,1): warning TS6385: Deprecated."#;
let result = parse_tsc_text(output).unwrap();
assert_eq!(result.len(), 3);
}
#[test]
fn test_empty_output() {
let result = parse_tsc_text("").unwrap();
assert!(result.is_empty());
}
#[test]
fn test_regex_pattern() {
let regex = tsc_output_regex();
let line = "src/auth.ts(42,5): error TS2339: Property 'foo' does not exist on type 'Bar'.";
let captures = regex.captures(line).unwrap();
assert_eq!(captures.get(1).unwrap().as_str(), "src/auth.ts");
assert_eq!(captures.get(2).unwrap().as_str(), "42");
assert_eq!(captures.get(3).unwrap().as_str(), "5");
assert_eq!(captures.get(4).unwrap().as_str(), "error");
assert_eq!(captures.get(5).unwrap().as_str(), "TS2339");
}
}