bacon 3.23.0

background rust compiler
Documentation
//! An analyzer for the Typescript compiler `tsc`

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
}

// This is an example of what we want to match:
// TLine { strings: [
//   TString { csi: "\u{1b}[96m", raw: "src/lib/handler.ts" },
//   TString { csi: "", raw: ":" },
//   TString { csi: "\u{1b}[93m", raw: "31" },
//   TString { csi: "", raw: ":" },
//   TString { csi: "\u{1b}[93m", raw: "3" },
//   TString { csi: "", raw: " - " },
//   TString { csi: "\u{1b}[91m", raw: "error" },
//   TString { csi: "\u{1b}[90m", raw: " TS2322: " },
//   TString { csi: "", raw: "Type 'string' is not assignable to type 'number'." }
// ] }
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
}

/// Build a report from the output of `tsc`
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()
}