aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs81
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() {