diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 81 |
1 files changed, 57 insertions, 24 deletions
diff --git a/src/main.rs b/src/main.rs index 838a518..f82e522 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,20 @@ +use std::collections::HashMap; use std::fs::File; use std::io::{BufReader, Read, Seek}; use std::path::PathBuf; use std::str::FromStr; +use anyhow::{anyhow, Result}; use chrono::{Duration, NaiveDateTime, Weekday}; use num_traits::cast::FromPrimitive; use regex::Regex; use structopt::StructOpt; use walkdir::{DirEntry, WalkDir}; -use anyhow::{anyhow, Result}; use evtclib::{AgentKind, AgentName, EventKind, Log}; +mod guilds; mod output; - mod filters; mod csl; @@ -55,16 +56,11 @@ fn debug_enabled() -> bool { #[structopt(name = "raidgrep")] pub struct Opt { /// Path to the folder with logs. - #[structopt( - short = "d", - long = "dir", - default_value = ".", - parse(from_os_str) - )] + #[structopt(short = "d", long = "dir", default_value = ".", parse(from_os_str))] path: PathBuf, /// The fields which should be searched. - #[structopt(short = "f", long = "fields", default_value = "*")] + #[structopt(short = "f", long = "fields", default_value = "account,character")] field: CommaSeparatedList<SearchField>, /// Only display fights with the given outcome. @@ -109,17 +105,17 @@ pub struct Opt { weekdays: CommaSeparatedList<Weekday>, /// Only show logs from the given encounters. - #[structopt( - short = "e", - long = "bosses", - default_value = "*", - )] + #[structopt(short = "e", long = "bosses", default_value = "*")] bosses: CommaSeparatedList<evtclib::statistics::gamedata::Boss>, /// Print more debugging information to stderr. #[structopt(long = "debug")] debug: bool, + /// Load guild information from the API. + #[structopt(long = "guilds")] + guilds: bool, + /// The regular expression to search for. #[structopt(name = "EXPR")] expression: Regex, @@ -132,6 +128,8 @@ enum SearchField { Account, /// Only search the character name. Character, + /// Only search the guild name or tag. + Guild, } impl FromStr for SearchField { @@ -141,6 +139,7 @@ impl FromStr for SearchField { match s { "account" => Ok(SearchField::Account), "character" => Ok(SearchField::Character), + "guild" => Ok(SearchField::Guild), _ => Err("Must be account or character"), } } @@ -172,6 +171,8 @@ pub struct Player { profession: String, /// Subsquad that the player was in. subgroup: u8, + /// Guild ID, ready for API consumption. + guild_id: Option<String>, } /// Outcome of the fight. @@ -209,12 +210,10 @@ fn parse_time_arg(input: &str) -> Result<NaiveDateTime> { Err(anyhow!("unknown time format")) } -fn try_from_str_simple_error<T: FromStr>(input: &str) -> Result<T, String> -{ +fn try_from_str_simple_error<T: FromStr>(input: &str) -> Result<T, String> { T::from_str(input).map_err(|_| format!("'{}' is an invalid value", input)) } - enum ZipWrapper<R: Read + Seek> { Raw(Option<R>), Zipped(zip::ZipArchive<R>), @@ -237,7 +236,6 @@ impl<R: Read + Seek> ZipWrapper<R> { } } - fn main() { let opt = Opt::from_args(); @@ -250,6 +248,10 @@ fn main() { unsafe { DEBUG_ENABLED = true }; } + if opt.guilds { + guilds::prepare_cache(); + } + let result = grep(&opt); match result { Ok(_) => {} @@ -257,6 +259,10 @@ fn main() { eprintln!("Error: {}", e); } } + + if opt.guilds { + guilds::save_cache(); + } } /// Check if the given entry represents a log file, based on the file name. @@ -311,11 +317,11 @@ fn search_log(entry: &DirEntry, opt: &Opt) -> Result<Option<LogResult>> { let mut stream = wrapper.get_stream(); let partial = evtclib::raw::parser::parse_partial_file(&mut stream)?; - let early_ok = filters::filter_name(&partial, opt) != opt.invert - && filters::filter_boss(&partial, opt); + let early_ok = + filters::filter_name(&partial, opt) != opt.invert && filters::filter_boss(&partial, opt); if !early_ok { - return Ok(None) + return Ok(None); } let raw = evtclib::raw::parser::finish_parsing(partial, &mut stream)?; @@ -331,7 +337,8 @@ fn search_log(entry: &DirEntry, opt: &Opt) -> Result<Option<LogResult>> { let take_log = filters::filter_outcome(&info, opt) && filters::filter_weekday(&info, opt) - && filters::filter_time(&info, opt); + && filters::filter_time(&info, opt) + && filters::filter_guilds(&info, opt); if take_log { Ok(Some(info)) @@ -350,7 +357,10 @@ fn extract_info(entry: &DirEntry, log: &Log) -> LogResult { log.boss_id() ); "unknown" - }).into(); + }) + .into(); + + let guild_ids = get_guild_mapping(log); let mut players = log .players() @@ -367,9 +377,11 @@ fn extract_info(entry: &DirEntry, log: &Log) -> LogResult { character_name: character_name.clone(), profession: get_profession_name(*profession, *elite).into(), subgroup: *subgroup, + guild_id: guild_ids.get(p.addr()).cloned(), } }}}} - }).collect::<Vec<Player>>(); + }) + .collect::<Vec<Player>>(); players.sort_by_key(|p| p.subgroup); LogResult { @@ -381,6 +393,27 @@ fn extract_info(entry: &DirEntry, log: &Log) -> LogResult { } } +/// Get a mapping of agent IDs to guild API strings. +fn get_guild_mapping(log: &Log) -> HashMap<u64, String> { + log.events() + .iter() + .filter_map(|event| { + if let EventKind::Guild { + source_agent_addr, + ref api_guild_id, + .. + } = event.kind + { + api_guild_id + .as_ref() + .map(|api_id| (source_agent_addr, api_id.clone())) + } else { + None + } + }) + .collect() +} + /// Get the timestamp of the log start time. fn get_start_timestamp(log: &Log) -> u32 { for event in log.events() { |