use {
super::*,
crate::*,
anyhow::Result,
lazy_regex::*,
};
#[derive(Debug, Default)]
pub struct TypescriptAnalyzer {
lines: Vec<CommandOutputLine>,
}
#[derive(Debug)]
struct LocationCode {
location: String,
code: String,
}
#[derive(Debug)]
enum TypescriptLine {
LocationCode(LocationCode),
Other,
}
impl Analyzer for TypescriptAnalyzer {
fn start(
&mut self,
_: &Mission,
) {
self.lines.clear();
}
fn receive_line(
&mut self,
line: CommandOutputLine,
command_output: &mut CommandOutput,
) {
self.lines.push(line.clone());
command_output.push(line);
}
fn build_report(&mut self) -> Result<Report> {
Ok(build_report(&self.lines))
}
}
fn recognize_line(tline: &TLine) -> TypescriptLine {
if let Some(lc) = recognize_location_code(tline) {
return TypescriptLine::LocationCode(lc);
}
TypescriptLine::Other
}
fn recognize_location_code(tline: &TLine) -> Option<LocationCode> {
let raw = tline.to_raw();
if let Some((_, location, code)) = regex_captures!(r"([^\s:]+:\d+:\d+) - error (TS.+$)", &raw) {
return Some(LocationCode {
location: location.to_string(),
code: code.to_string(),
});
}
None
}
pub fn build_report(cmd_lines: &[CommandOutputLine]) -> Report {
let mut items = ItemAccumulator::default();
let mut last_is_blank = true;
for cmd_line in cmd_lines {
let bline = recognize_line(&cmd_line.content);
if let TypescriptLine::LocationCode(lc) = bline {
let error_line = burp::error_line(&lc.code);
items.push_error_title(error_line);
items.push_line(LineType::Location, burp::location_line(lc.location.clone()));
last_is_blank = false;
} else {
let is_blank = cmd_line.content.is_blank();
if !(is_blank && last_is_blank) {
items.push_line(LineType::Normal, cmd_line.content.clone());
}
last_is_blank = is_blank;
}
}
items.report()
}