Skip to main content

wowlab_parsers/parsers/spell_desc/
resolver.rs

1/// Resolves effect-level values, spell-level values, and custom variables.
2pub trait EffectValueResolver {
3    /// Get an effect value by `var_type` (s/m/M/t/a/A/x/o/e/w/bc/q) for a 1-indexed effect.
4    fn get_effect_value(&self, _spell_id: u32, _effect_index: u8, _var_type: &str) -> Option<f64> {
5        None
6    }
7
8    /// Get a formatted spell-level value by `var_type` (d/n/r/h/u/i/p/z/c).
9    fn get_spell_value(&self, _spell_id: u32, _var_type: &str) -> Option<String> {
10        None
11    }
12
13    /// Get a custom variable value from the spell's `description_variables`.
14    fn get_custom_var(&self, _name: &str) -> Option<f64> {
15        None
16    }
17}
18
19/// Resolves player state: stats, gender, specialization, known spells, active auras.
20pub trait PlayerStateResolver {
21    /// Get a player stat value by name (SP/AP/RAP/MHP/SPS/PL/INT).
22    fn get_player_stat(&self, _stat: &str) -> Option<f64> {
23        None
24    }
25
26    /// Check if the player knows a spell. Used for `$?s12345` conditionals.
27    fn knows_spell(&self, _spell_id: u32) -> bool {
28        false
29    }
30
31    /// Check if an aura is active on the player. Used for `$?a12345` conditionals.
32    fn has_aura(&self, _aura_id: u32) -> bool {
33        false
34    }
35
36    /// Check the player's 1-based specialization index. Used for `$?cX` conditionals.
37    fn is_specialization(&self, _spec_index: u8) -> bool {
38        false
39    }
40
41    /// Player gender; true for male, false for female.
42    fn is_male(&self) -> bool {
43        true
44    }
45}
46
47/// Resolves recursive spell text embedding (description, name, icon).
48pub trait SpellTextResolver {
49    /// Get another spell's rendered description for `@spelldesc` embedding.
50    fn get_spell_description(&self, _spell_id: u32) -> Option<String> {
51        None
52    }
53
54    /// Get another spell's name for `@spellname` embedding.
55    fn get_spell_name(&self, _spell_id: u32) -> Option<String> {
56        None
57    }
58
59    /// Get another spell's icon path for `@spellicon` embedding.
60    fn get_spell_icon(&self, _spell_id: u32) -> Option<String> {
61        None
62    }
63}
64
65/// Composite trait auto-implemented for any type implementing all three resolver sub-traits.
66// docref:start spell-desc-resolver-trait
67pub trait SpellDescResolver: EffectValueResolver + PlayerStateResolver + SpellTextResolver {}
68// docref:end spell-desc-resolver-trait
69
70impl<T> SpellDescResolver for T where
71    T: EffectValueResolver + PlayerStateResolver + SpellTextResolver
72{
73}
74
75/// A no-op resolver that returns `None`/`false`/`true` for everything.
76#[derive(Debug, Default)]
77pub struct NullResolver;
78
79impl EffectValueResolver for NullResolver {}
80impl PlayerStateResolver for NullResolver {}
81impl SpellTextResolver for NullResolver {}
82
83#[derive(Debug, Default)]
84pub struct TestResolver {
85    pub effects: wowlab_types::sim::FastMap<(u32, u8, String), f64>,
86    pub spell_values: wowlab_types::sim::FastMap<(u32, String), String>,
87    pub player_stats: wowlab_types::sim::FastMap<String, f64>,
88    pub custom_vars: wowlab_types::sim::FastMap<String, f64>,
89    pub known_spells: wowlab_types::sim::FastSet<u32>,
90    pub active_auras: wowlab_types::sim::FastSet<u32>,
91    pub player_spec_index: Option<u8>,
92    pub is_male: bool,
93    pub spell_descriptions: wowlab_types::sim::IntMap<u32, String>,
94    pub spell_names: wowlab_types::sim::IntMap<u32, String>,
95    pub spell_icons: wowlab_types::sim::IntMap<u32, String>,
96}
97
98impl TestResolver {
99    #[must_use]
100    pub fn new() -> Self {
101        Self {
102            is_male: true,
103            ..Default::default()
104        }
105    }
106
107    #[must_use]
108    pub fn with_effect(
109        mut self,
110        spell_id: u32,
111        effect_index: u8,
112        var_type: &str,
113        value: f64,
114    ) -> Self {
115        self.effects
116            .insert((spell_id, effect_index, var_type.to_string()), value);
117
118        self
119    }
120
121    #[must_use]
122    pub fn with_spell_value(mut self, spell_id: u32, var_type: &str, value: &str) -> Self {
123        self.spell_values
124            .insert((spell_id, var_type.to_string()), value.to_string());
125
126        self
127    }
128
129    #[must_use]
130    pub fn with_player_stat(mut self, stat: &str, value: f64) -> Self {
131        self.player_stats.insert(stat.to_string(), value);
132
133        self
134    }
135
136    #[must_use]
137    pub fn with_custom_var(mut self, name: &str, value: f64) -> Self {
138        self.custom_vars.insert(name.to_string(), value);
139
140        self
141    }
142
143    #[must_use]
144    pub fn with_known_spell(mut self, spell_id: u32) -> Self {
145        self.known_spells.insert(spell_id);
146
147        self
148    }
149
150    #[must_use]
151    pub fn with_active_aura(mut self, aura_id: u32) -> Self {
152        self.active_auras.insert(aura_id);
153
154        self
155    }
156
157    #[must_use]
158    pub fn with_specialization_index(mut self, spec_index: u8) -> Self {
159        self.player_spec_index = Some(spec_index);
160
161        self
162    }
163
164    #[must_use]
165    pub fn with_gender(mut self, is_male: bool) -> Self {
166        self.is_male = is_male;
167
168        self
169    }
170
171    #[must_use]
172    pub fn with_spell_name(mut self, spell_id: u32, name: &str) -> Self {
173        self.spell_names.insert(spell_id, name.to_string());
174
175        self
176    }
177
178    #[must_use]
179    pub fn with_spell_description(mut self, spell_id: u32, description: &str) -> Self {
180        self.spell_descriptions
181            .insert(spell_id, description.to_string());
182
183        self
184    }
185
186    #[must_use]
187    pub fn with_spell_icon(mut self, spell_id: u32, icon: &str) -> Self {
188        self.spell_icons.insert(spell_id, icon.to_string());
189
190        self
191    }
192}
193
194impl EffectValueResolver for TestResolver {
195    fn get_effect_value(&self, spell_id: u32, effect_index: u8, var_type: &str) -> Option<f64> {
196        self.effects
197            .get(&(spell_id, effect_index, var_type.to_string()))
198            .copied()
199    }
200
201    fn get_spell_value(&self, spell_id: u32, var_type: &str) -> Option<String> {
202        self.spell_values
203            .get(&(spell_id, var_type.to_string()))
204            .cloned()
205    }
206
207    fn get_custom_var(&self, name: &str) -> Option<f64> {
208        self.custom_vars.get(name).copied()
209    }
210}
211
212impl PlayerStateResolver for TestResolver {
213    fn get_player_stat(&self, stat: &str) -> Option<f64> {
214        self.player_stats.get(stat).copied()
215    }
216
217    fn knows_spell(&self, spell_id: u32) -> bool {
218        self.known_spells.contains(&spell_id)
219    }
220
221    fn has_aura(&self, aura_id: u32) -> bool {
222        self.active_auras.contains(&aura_id)
223    }
224
225    fn is_specialization(&self, spec_index: u8) -> bool {
226        self.player_spec_index == Some(spec_index)
227    }
228
229    fn is_male(&self) -> bool {
230        self.is_male
231    }
232}
233
234impl SpellTextResolver for TestResolver {
235    fn get_spell_description(&self, spell_id: u32) -> Option<String> {
236        self.spell_descriptions.get(&spell_id).cloned()
237    }
238
239    fn get_spell_name(&self, spell_id: u32) -> Option<String> {
240        self.spell_names.get(&spell_id).cloned()
241    }
242
243    fn get_spell_icon(&self, spell_id: u32) -> Option<String> {
244        self.spell_icons.get(&spell_id).cloned()
245    }
246}