Skip to main content

forge/items/
mod.rs

1//! Items subcommand: measure each registered item's DPS contribution per spec, then emit an HTML overview matrix.
2
3mod charts;
4mod report;
5mod runner;
6mod types;
7
8use anyhow::{Context, Result, anyhow};
9use wowlab_common::output;
10use wowlab_fs::{directory, path::PathBuf};
11use wowlab_types::game::SpecId;
12
13use crate::run::{FightDurationSeconds, IterationCount, RunParameters, RunParametersError};
14
15const DEFAULT_ITERATIONS: u32 = 100;
16const DEFAULT_DURATION: u32 = 60;
17
18#[derive(Debug, clap::Args)]
19pub(crate) struct ItemsArgs {
20    #[arg(value_name = "SPEC")]
21    pub specs: Vec<String>,
22
23    #[arg(long, short = 'n', default_value_t = DEFAULT_ITERATIONS)]
24    pub iterations: u32,
25
26    #[arg(long, short = 'd', default_value_t = DEFAULT_DURATION)]
27    pub duration: u32,
28
29    #[arg(long)]
30    pub only: Option<u32>,
31
32    #[arg(long, default_value = "benchmarks/items.html")]
33    pub output: PathBuf,
34
35    #[arg(long)]
36    pub no_html: bool,
37}
38
39impl TryFrom<&ItemsArgs> for RunParameters {
40    type Error = RunParametersError;
41
42    fn try_from(args: &ItemsArgs) -> Result<Self, Self::Error> {
43        let fight_duration = FightDurationSeconds::try_from(args.duration)?;
44        let iterations = IterationCount::try_from(args.iterations)?;
45
46        Ok(Self::new(iterations, fight_duration))
47    }
48}
49
50pub(crate) fn run(args: &ItemsArgs) -> Result<()> {
51    let parameters = RunParameters::try_from(args)?;
52    let spec_filter: Vec<SpecId> = args
53        .specs
54        .iter()
55        .map(|slug| {
56            SpecId::from_manifest_slug(slug).ok_or_else(|| anyhow!("Unknown spec slug: {slug}"))
57        })
58        .collect::<Result<Vec<_>>>()?;
59
60    let matrix = runner::run_matrix(&spec_filter, parameters, args.only)
61        .context("items matrix run failed")?;
62
63    report::print_console_summary(&matrix);
64
65    if !args.no_html {
66        report::generate(&args.output, &matrix)
67            .with_context(|| format!("failed to write HTML report to {}", args.output.display()))?;
68        let displayed_path =
69            directory::canonicalize(&args.output).unwrap_or_else(|_| args.output.clone());
70
71        output::success(&format!(
72            "Report written to file://{}",
73            displayed_path.display(),
74        ));
75    }
76
77    Ok(())
78}