Skip to main content

forge/
intent.rs

1//! Intent-config builders shared across forge subcommands.
2
3use std::collections::BTreeMap;
4
5#[cfg(test)]
6use googletest::{Result as GtestResult, prelude::*};
7use wowlab_common::sim::intent::{INTENT_VERSION, SimConfigIntent};
8use wowlab_types::{game::SpecId, sim::EncounterDefinition};
9
10pub(crate) const CANONICAL_PATCHWERK_ENEMY_LEVEL: u16 = 80;
11/// Expansion whose level-80 `ExpectedStat` row (ID 586) backs the fixture target.
12pub(crate) const CANONICAL_PATCHWERK_EXPANSION_ID: u32 = 11;
13pub(crate) const DEFAULT_PLAYER_LEVEL: u16 = 80;
14pub(crate) const PLAYER_EXPANSION_ID: u32 = 11;
15
16pub(crate) fn canonical_patchwerk_intent(
17    spec: SpecId,
18    rotation_id: impl Into<String>,
19    duration_s: f64,
20) -> anyhow::Result<SimConfigIntent> {
21    patchwerk_intent_with_context(
22        spec,
23        rotation_id,
24        duration_s,
25        CANONICAL_PATCHWERK_ENEMY_LEVEL,
26        CANONICAL_PATCHWERK_EXPANSION_ID,
27    )
28}
29
30fn patchwerk_intent_with_context(
31    spec: SpecId,
32    rotation_id: impl Into<String>,
33    duration_s: f64,
34    target_level: u16,
35    target_expansion_id: u32,
36) -> anyhow::Result<SimConfigIntent> {
37    let encounter = EncounterDefinition::patchwerk(duration_s, target_level, target_expansion_id)?;
38
39    Ok(SimConfigIntent {
40        intent_version: INTENT_VERSION.to_string(),
41        spec: spec.wow_spec_id(),
42        rotation_id: rotation_id.into(),
43        loadout: None,
44        expansion_talents: BTreeMap::new(),
45        player_level: DEFAULT_PLAYER_LEVEL,
46        player_expansion_id: PLAYER_EXPANSION_ID,
47        encounter,
48        settings: BTreeMap::new(),
49        gear: Vec::new(),
50    })
51}
52
53#[cfg(test)]
54mod tests {
55    use wowlab_common::sim::intent::{parse_sim_config, serialize_sim_config};
56    use wowlab_types::sim::{DifficultyContext, EnemyDefinition};
57
58    use super::*;
59
60    #[gtest]
61    fn patchwerk_intent_round_trips_with_typed_non_default_encounter_context() -> GtestResult<()> {
62        let intent = patchwerk_intent_with_context(
63            SpecId::Assassination,
64            "assassination_rogue_assisted",
65            137.5,
66            72,
67            10,
68        )
69        .or_fail()?;
70        let serialized = serialize_sim_config(&intent).or_fail()?;
71        let parsed = parse_sim_config(&serialized).or_fail()?;
72
73        verify_that!(
74            &parsed,
75            matches_pattern!(SimConfigIntent {
76                intent_version: eq(INTENT_VERSION),
77                spec: eq(&259),
78                rotation_id: eq("assassination_rogue_assisted"),
79                encounter: matches_pattern!(EncounterDefinition {
80                    fixed_duration_s: eq(&Some(137.5)),
81                    enemies: elements_are![matches_pattern!(EnemyDefinition {
82                        level: eq(&72),
83                        difficulty: eq(&DifficultyContext::Generic { expansion_id: 10 }),
84                        ..
85                    })],
86                    ..
87                }),
88                ..
89            })
90        )?;
91        verify_true!(parsed.settings.is_empty())?;
92        verify_true!(parsed.gear.is_empty())?;
93
94        Ok(())
95    }
96
97    #[gtest]
98    fn patchwerk_intent_uses_supplied_spec() -> GtestResult<()> {
99        let intent =
100            canonical_patchwerk_intent(SpecId::Fire, "fire_mage_assisted", 60.0).or_fail()?;
101
102        verify_that!(intent.spec, eq(63))?;
103        verify_that!(intent.rotation_id, eq("fire_mage_assisted"))?;
104        verify_that!(
105            intent.encounter.enemies[0].level,
106            eq(CANONICAL_PATCHWERK_ENEMY_LEVEL)
107        )?;
108
109        Ok(())
110    }
111}