use std::{fs::{self, File}, io::Write, process::exit}; use anyhow::{Error, Result}; use clap::Parser; use dialoguer::MultiSelect; use once_cell::sync::Lazy; use rayon::{ iter::{IntoParallelIterator, ParallelIterator}, str::ParallelString, }; use regex::Regex; /// Simple program to greet a person #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { #[arg(short, long, help = "Path to log file")] input: String, #[arg( short, long, help = "Format of output file", default_value_t, value_enum )] format: OutputFormat, #[arg(short, long, help = "Path to output file")] output: String, } #[derive(clap::ValueEnum, Debug, Clone, Default)] enum OutputFormat { Image, CSV, #[default] TXT, } /// follow up by extracting just the user and the message static EXTRACT_MSG: Lazy = Lazy::new(|| Regex::new(r"<.*> .*").unwrap()); static EXTRACT_TIME: Lazy = Lazy::new(|| Regex::new(r"^\[.*?\]").unwrap()); fn main() -> Result<()> { let args = Args::parse(); if !fs::exists(&args.input)? { println!("Please provide a valid file path!"); exit(1) } let log_contents = fs::read_to_string(&args.input)?; let split_log = log_contents.par_split('\n'); let filtered_log: Vec<&str> = split_log.filter(|x| is_possible_chat_msg(x)).collect(); let extracted: Vec = filtered_log .clone() .into_par_iter() .map(|x| { format!( "{} {}", EXTRACT_TIME .captures(x) .expect("Unable to extract time") .get(0) .unwrap() .as_str(), EXTRACT_MSG .captures(x) .expect("Unable to extract message") .get(0) .unwrap() .as_str() ) }) .collect(); let selection = MultiSelect::new() .with_prompt("What messages do you want to render?") .items(&extracted) .interact()?; match &args.format { OutputFormat::Image => todo!(), OutputFormat::CSV => todo!(), OutputFormat::TXT => save_txt_file(&args.output, extracted, selection)?, } Ok(()) } fn save_txt_file(output: &String, extracted: Vec, selection: Vec) -> Result<(), Error> { let mut out_file = File::create(output)?; Ok(for msg in selection { out_file.write(extracted[msg].as_bytes())?; out_file.write(b"\n")?; }) } fn is_possible_chat_msg(input: &str) -> bool { /// first pass to find all possible lines that could have a chat message static SERVER_MSG_RE: Lazy = Lazy::new(|| { Regex::new( r"\[.*\] \[Server thread\/INFO\] \[net.minecraft.server.MinecraftServer\/\]: <.*>*", ) .unwrap() }); static CLIENT_MSG_RE: Lazy = Lazy::new(|| { Regex::new(r"\[.*\] \[Render thread\/INFO\] \[minecraft\/ChatComponent\]: \[CHAT\] <.*>*") .unwrap() }); return SERVER_MSG_RE.is_match(input) || CLIENT_MSG_RE.is_match(input); }