mod load;
mod measure;
mod percentiles;
mod report;
mod rows;
#[cfg(test)]
mod tests;
use crate::{
args::CensusArgs,
materials::{Catalogue, resolve_scoring},
outcome::{CliError, CommandResult, Outcome},
};
use indicatrix_editor::templates::gallery_design;
use measure::{Context, Output, panicked, process};
use rows::Row;
use std::{
io::Write,
panic::{AssertUnwindSafe, catch_unwind},
};
pub fn run(args: &CensusArgs) -> CommandResult {
run_streaming(args, &mut std::io::sink())
}
type StoneFiles = Vec<(String, String)>;
fn collect(
args: &CensusArgs,
progress: &mut dyn Write,
) -> Result<(Vec<Row>, StoneFiles), CliError> {
let catalogue = Catalogue::open(args.db.as_deref(), None)?;
if let Some(dir) = &args.out_dir
&& !dir.is_dir()
{
return Err(CliError::io(format!(
"--out-dir {} is not an existing folder",
dir.display()
)));
}
let probe = gallery_design(1);
for target in &args.targets {
resolve_scoring(&probe, &catalogue, Some(target))?;
}
let library = args.db.as_deref().map(load::open_library).transpose()?;
let sources = load::plan_sources(args, library.as_ref())?;
let context = Context {
args,
catalogue: &catalogue,
};
let (mut rows, mut files) = (Vec::new(), Vec::new());
for (number, source) in sources.iter().enumerate() {
let subject = load::open_subject(source, library.as_ref());
let output = catch_unwind(AssertUnwindSafe(|| process(&subject, &context)))
.unwrap_or_else(|payload| panicked(&subject, &*payload));
let Output {
rows: new_rows,
files: new_files,
} = output;
rows.extend(new_rows);
files.extend(new_files);
let _ = writeln!(
progress,
"census: {}/{} {} {}",
number + 1,
sources.len(),
subject.source,
subject.key
);
}
Ok((rows, files))
}
pub fn run_streaming(args: &CensusArgs, progress: &mut dyn Write) -> CommandResult {
let (rows, files) = collect(args, progress)?;
let summary = percentiles::summarize(&rows);
let body = if args.output.json {
report::json(&rows, args.output.timings, &summary)
} else {
report::csv(&rows, args.output.timings, &summary)
};
let mut outcome = match &args.out {
Some(path) => Outcome::text(report::summary_text(&summary)).with_file(path.clone(), body),
None => Outcome::text(body),
};
if let Some(dir) = &args.out_dir {
for (name, text) in files {
outcome = outcome.with_file(dir.join(name), text);
}
}
Ok(outcome)
}