use {
crate::*,
anyhow::Result,
lazy_regex::*,
};
#[derive(Debug, Default)]
pub struct RuffAnalyzer {
lines: Vec<CommandOutputLine>,
}
#[derive(Debug)]
struct LocationTitle<'l> {
path: &'l str,
line: &'l str,
column: &'l str,
message: &'l [TString],
}
#[derive(Debug)]
enum RuffLine<'l> {
LocationTitle(LocationTitle<'l>),
Other,
}
impl Analyzer for RuffAnalyzer {
fn start(
&mut self,
_mission: &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) -> RuffLine<'_> {
if let Some(lt) = recognize_location_message(tline) {
return RuffLine::LocationTitle(lt);
}
RuffLine::Other
}
fn recognize_location_message(tline: &TLine) -> Option<LocationTitle<'_>> {
let tstrings = &tline.strings;
if tstrings.len() < 8 {
return None;
}
if tstrings[0].csi != "\u{1b}[1m" || tstrings[1].raw.is_empty() || tstrings[1].csi != "\u{1b}[36m" || tstrings[1].raw != ":"
|| tstrings[2].is_styled() || !regex_is_match!(r"^\d+$", &tstrings[2].raw) || tstrings[3].csi != "\u{1b}[36m" || tstrings[3].raw != ":"
|| tstrings[4].is_styled() || !regex_is_match!(r"^\d+$", &tstrings[4].raw) || tstrings[5].csi != "\u{1b}[36m" || tstrings[5].raw != ":"
|| tstrings[6].is_styled() || tstrings[6].raw != " "
{
return None;
}
Some(LocationTitle {
path: &tstrings[0].raw,
line: &tstrings[2].raw,
column: &tstrings[4].raw,
message: &tstrings[7..],
})
}
pub fn build_report(cmd_lines: &[CommandOutputLine]) -> Report {
let mut items = ItemAccumulator::default();
let mut last_is_blank = true;
let mut i = 0;
for cmd_line in cmd_lines {
if i < 5 {
info!("cmd_line: {:#?}", &cmd_line);
i += 1;
}
let bline = recognize_line(&cmd_line.content);
if let RuffLine::LocationTitle(LocationTitle {
path,
line,
column,
message,
}) = bline
{
items.push_error_title(burp::error_line_ts(message));
items.push_line(
LineType::Location,
burp::location_line(format!("{path}:{line}:{column}")),
);
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()
}