tex-fmt 0.4.0

LaTeX formatter written in Rust
use crate::colors::*;
use crate::Cli;
use env_logger::Builder;
use log::Level;
use log::Level::{Error, Info, Trace, Warn};
use log::LevelFilter;
use std::cmp::Reverse;
use std::io::Write;
use std::path::Path;
use std::time::Instant;

#[derive(Debug)]
pub struct Log {
    pub level: Level,
    pub time: Instant,
    pub file: String,
    pub linum_new: Option<usize>,
    pub linum_old: Option<usize>,
    pub line: Option<String>,
    pub message: String,
}

fn record_log(
    logs: &mut Vec<Log>,
    level: Level,
    file: &str,
    linum_new: Option<usize>,
    linum_old: Option<usize>,
    line: Option<String>,
    message: &str,
) {
    let log = Log {
        level,
        time: Instant::now(),
        file: file.to_string(),
        linum_new,
        linum_old,
        line,
        message: message.to_string(),
    };
    logs.push(log);
}

pub fn record_file_log(
    logs: &mut Vec<Log>,
    level: Level,
    file: &str,
    message: &str,
) {
    record_log(logs, level, file, None, None, None, message);
}

pub fn record_line_log(
    logs: &mut Vec<Log>,
    level: Level,
    file: &str,
    linum_new: usize,
    linum_old: usize,
    line: &str,
    message: &str,
) {
    record_log(
        logs,
        level,
        file,
        Some(linum_new),
        Some(linum_old),
        Some(line.to_string()),
        message,
    );
}

fn get_log_style(log_level: Level) -> String {
    match log_level {
        Info => CYAN.to_string(),
        Warn => YELLOW.to_string(),
        Error => RED.to_string(),
        Trace => GREEN.to_string(),
        _ => panic!(),
    }
}

fn get_log_level(args: &Cli) -> LevelFilter {
    if args.trace {
        LevelFilter::Trace
    } else if args.verbose {
        LevelFilter::Info
    } else {
        LevelFilter::Warn
    }
}

pub fn init_logger(args: &Cli) {
    Builder::new()
        .filter_level(get_log_level(args))
        .format(|buf, record| {
            writeln!(
                buf,
                "{}{}:{} {}",
                get_log_style(record.level()),
                record.level(),
                RESET,
                record.args()
            )
        })
        .init();
}

pub fn print_logs(mut logs: Vec<Log>) {
    logs.sort_by_key(|l| {
        (
            l.level,
            l.linum_new,
            l.linum_old,
            l.message.clone(),
            Reverse(l.time),
        )
    });
    logs.dedup_by(|a, b| {
        (a.level, a.linum_new, a.linum_old, &a.message)
            == (b.level, b.linum_new, b.linum_old, &b.message)
    });
    logs.sort_by_key(|l| l.time);

    for log in logs {
        let linum_new = match log.linum_new {
            Some(i) => format!("Line {} ", i),
            None => "".to_string(),
        };

        let linum_old = match log.linum_old {
            Some(i) => format!("({}). ", i),
            None => "".to_string(),
        };

        let line = match &log.line {
            Some(l) => l.trim_start().to_string(),
            None => "".to_string(),
        };

        let log_string = format!(
            "{}tex-fmt {}{}: {}{}{}{}{} {}{}",
            PINK,
            PURPLE,
            Path::new(&log.file).file_name().unwrap().to_str().unwrap(),
            WHITE,
            linum_new,
            linum_old,
            YELLOW,
            log.message,
            RESET,
            line,
        );

        match log.level {
            Error => log::error!("{}", log_string),
            Warn => log::warn!("{}", log_string),
            Info => log::info!("{}", log_string),
            Trace => log::trace!("{}", log_string),
            _ => panic!(),
        }
    }
}