log_parser_kma 0.1.3

Rust-based log file parser, helping extract datetime, log levels and messages
Documentation
use anyhow::Result;
use clap::{Arg, ArgMatches, Command};
use log_parser_kma::{LogLevel, LogLine, LogParser};
use std::fs::File;
use std::io::Write;

fn main() -> Result<()> {
    let matches = Command::new("Log Parser")
        .version("0.1.2")
        .subcommand(
            Command::new("credits")
                        .about("Displays the credits")
        )
        .subcommand(
            Command::new("parse")
                        .about("Parses a log file with different options")
                        .arg(
                            Arg::new("file")
                                .short('f')
                                .help("The file to parse")
                                .required(true),
                        )
                        .arg(
                            Arg::new("output")
                                .short('o')
                                .help("Path to output file")
                                .required(true),
                        )
                        .arg(
                            Arg::new("level")
                                .short('l')
                                .help("Extract logs of specified level")
                                .required(false),
                        )
                        .arg(
                            Arg::new("to-json")
                                .short('j')
                                .help("Toggle to-JSON conversion")
                                .required(false)
                                .action(clap::ArgAction::SetTrue),
                        )
        )
        .get_matches();

    match matches.subcommand() {
        Some(("help", _)) => {
            println!("Log Parser CLI - Help");
            println!("Usage: log_parser_kma [COMMAND] [OPTIONS]");
            println!("Commands:");
            println!("  parse   Parse a log file with various options");
            println!("  help    Display this help message");
            println!("  credits Show credits");
            println!("Use `log_parser_kma --help` for more details on the parse command");
        },
        Some(("credits", _)) => {
            println!("Log Parser");
            println!("Made by Krachylo Illia");
            println!("For NaUKMA Rust Programming Course of 2024");
            println!("Version 0.1.2");
        },
        Some(("parse", parse_matches)) => {
            process(parse_matches)?;
        },
        _ => {
            println!("Unknown command")
        }
    }

   
    Ok(())
}

fn process(matches: &ArgMatches) -> Result<()> {
    let output_path = matches
        .get_one::<String>("output")
        .expect("Output path is required!");
    let loglevel = matches.get_one::<String>("level");
    let to_json = matches.get_flag("to-json");

    if let Some(filepath) = matches.get_one::<String>("file") {
        let result = LogParser::parse_file(filepath)?;

        let filtered: Vec<LogLine> = if let Some(level) = loglevel {
            let level_enum = match level.as_str() {
                "INFO" => LogLevel::Info,
                "WARNING" => LogLevel::Warning,
                "ERROR" => LogLevel::Error,
                _ => return Err(anyhow::anyhow!("Invalid log level")),
            };
            result
                .into_iter()
                .filter(|line| line.level == level_enum)
                .collect()
        } else {
            result
        };

        let mut file = File::create(output_path)?;

        if to_json {
            let json_logs = serde_json::to_string(&filtered)?;
            writeln!(file, "{}", json_logs)?;
        } else {
            for l in filtered {
                writeln!(
                    file,
                    "{} {} {}",
                    l.datetime,
                    log_level_string(&l.level),
                    l.message
                )?;
            }
        }
    }

    Ok(())
}

fn log_level_string(level: &LogLevel) -> &str {
    match level {
        LogLevel::Info => "INFO",
        LogLevel::Warning => "WARNING",
        LogLevel::Error => "ERROR",
    }
}