// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use crate::{
stats::{self, Connection, Parameters, Stats},
Result,
};
use anyhow::anyhow;
use std::{collections::HashMap, fs, io};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
pub struct Query {
#[structopt(long)]
header: Option>,
#[structopt(long)]
clients: Option >,
#[structopt(long)]
servers: Option >,
#[structopt(long, short)]
filter: Vec,
#[structopt(long, short, possible_values = &*stats::QUERY_NAMES)]
query: Vec,
#[structopt(long)]
with_seed: bool,
input: Option,
}
impl Query {
pub fn run(&self) -> Result {
let input: Box = if self.input.as_ref().map_or(true, |v| v == "-") {
let reader = io::stdin();
Box::new(reader)
} else {
let reader = fs::File::open(self.input.as_ref().unwrap())?;
let reader = io::BufReader::new(reader);
Box::new(reader)
};
if self.query.is_empty() {
return Err(anyhow!("must specify at least one query"));
}
let with_seed = self.with_seed;
let queries = &self.query;
let filters = &self.filter;
let header = self.header.map_or(true, |v| v.unwrap_or(true));
if header {
let seed = if with_seed {
Some(("seed", "id"))
} else {
None
};
let queries = queries.iter().map(|q| q.name);
if emit(seed, queries).is_err() {
return Ok(());
}
}
let mut values = vec![];
let mut q = |p: &Parameters, conn: &Connection, connections: &[Connection]| -> Result {
for filter in filters {
if !filter.apply(p, conn, connections) {
return Ok(());
}
}
values.clear();
for query in queries {
if let Some(value) = query.apply(p, conn, connections) {
values.push(value);
} else {
return Ok(());
}
}
let seed = if with_seed {
Some((conn.seed, conn.id()))
} else {
None
};
emit(seed, values.iter())?;
Ok(())
};
let clients = self.clients.map_or(true, |v| v.unwrap_or(true));
let servers = self.servers.map_or(true, |v| v.unwrap_or(true));
let reader = crate::stats::Stats::reader(input);
let mut acc: HashMap> = HashMap::new();
for stat in reader {
match stat? {
Stats::Setup(_) => {
// unused
}
Stats::Parameters(p) => {
if let Some(connections) = acc.remove(&p.seed) {
for conn in &connections {
if q(&p, conn, &connections).is_err() {
return Ok(());
}
}
}
}
Stats::Connection(c) => {
if (clients && c.client_id.is_some()) || (servers && c.server_id.is_some()) {
acc.entry(c.seed).or_default().push(c);
}
}
}
}
Ok(())
}
}
fn emit<
Seed: core::fmt::Display,
Id: core::fmt::Display,
I: Iterator- ,
V: core::fmt::Display,
>(
seed: Option<(Seed, Id)>,
i: I,
) -> io::Result<()> {
use io::Write;
let stdout = io::stdout();
let mut o = stdout.lock();
let mut has_written = false;
if let Some((seed, id)) = seed {
write!(o, "{seed}\t{id}")?;
has_written = true;
}
for value in i {
if has_written {
write!(o, "\t{value}")?;
} else {
write!(o, "{value}")?;
}
has_written = true;
}
writeln!(o)?;
Ok(())
}