Skip to main content

wowlab_analytics/analytics/
views.rs

1use serde::Serialize;
2
3/// Chart-ready view model returned across the WASM boundary.
4#[derive(Clone, Debug, Serialize)]
5#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
6#[cfg_attr(feature = "wasm", tsify(into_wasm_abi))]
7pub struct AnalyticsView {
8    pub core: CoreView,
9    pub distribution: Option<DistributionView>,
10    pub actions: Vec<ActionView>,
11    pub auras: Vec<AuraView>,
12    pub resources: Vec<ResourceView>,
13    pub cooldowns: Vec<CooldownView>,
14    pub execution: Option<ExecutionView>,
15    pub damage_profile: Option<DamageProfileView>,
16    pub timeline: Option<TimelineView>,
17    pub dictionary: DictionaryMap,
18}
19
20/// Headline core metrics for a simulation run (iterations, mean DPS, spread).
21#[derive(Clone, Debug, Serialize)]
22#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
23pub struct CoreView {
24    pub iterations: u64,
25    pub chunks_completed: u32,
26    pub chunks_total: u32,
27    pub total_fight_time_ms: u64,
28    pub mean_dps: f64,
29    pub min_dps: f64,
30    pub max_dps: f64,
31    pub std_dps: f64,
32    pub ci95_half_pct: f64,
33}
34
35/// DPS distribution summary: histogram plus canonical percentile buckets.
36#[derive(Clone, Debug, Serialize)]
37#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
38pub struct DistributionView {
39    pub sample_count: u64,
40    pub histogram: HistogramView,
41    pub percentiles: PercentilesView,
42}
43
44/// Equal-width DPS histogram with underflow and overflow counters.
45#[derive(Clone, Debug, Serialize)]
46#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
47pub struct HistogramView {
48    pub bin_count: u32,
49    pub min_dps: f64,
50    pub max_dps: f64,
51    pub counts: Vec<u64>,
52    pub underflow: u64,
53    pub overflow: u64,
54}
55
56/// Canonical DPS percentiles (p01 through p99) for distribution summaries.
57#[derive(Clone, Debug, Serialize)]
58#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
59pub struct PercentilesView {
60    pub p01: f64,
61    pub p05: f64,
62    pub p10: f64,
63    pub p25: f64,
64    pub p50: f64,
65    pub p75: f64,
66    pub p90: f64,
67    pub p95: f64,
68    pub p99: f64,
69}
70
71/// Per-spell action summary: casts, hits, damage, resource flow, derived DPS.
72#[derive(Clone, Debug, Serialize)]
73#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
74pub struct ActionView {
75    pub spell_id: u32,
76    pub target_id: u32,
77    pub source: String,
78    pub source_id: u32,
79    pub pull_id: Option<u32>,
80    pub group_id: Option<u32>,
81    pub casts: u64,
82    pub direct_hits: u64,
83    pub ticks: u64,
84    pub crits: u64,
85    pub misses: u64,
86    pub dodges: u64,
87    pub parries: u64,
88    pub total_damage: f64,
89    pub execute_time_ms: u64,
90    pub resource_spent: f64,
91    pub resource_gained: f64,
92    pub dps: Option<f64>,
93    pub damage_pct: Option<f64>,
94}
95
96/// Per-aura uptime stats with application and refresh counts.
97#[derive(Clone, Debug, Serialize)]
98#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
99pub struct AuraView {
100    pub aura_id: u32,
101    pub target_id: u32,
102    pub source: String,
103    pub source_id: u32,
104    pub affected: String,
105    pub affected_id: u32,
106    pub pull_id: Option<u32>,
107    pub group_id: Option<u32>,
108    pub uptime_ms: u64,
109    pub applications: u64,
110    pub refreshes: u64,
111    pub stack_seconds: f64,
112    pub uptime_pct: Option<f64>,
113}
114
115/// Per-resource flow: gained, spent, wasted, and time spent at cap or starved.
116#[derive(Clone, Debug, Serialize)]
117#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
118pub struct ResourceView {
119    pub resource_type: u32,
120    pub gained: f64,
121    pub spent: f64,
122    pub wasted: f64,
123    pub time_at_cap_ms: u64,
124    pub starved_time_ms: u64,
125    pub cap_pct: Option<f64>,
126    pub starved_pct: Option<f64>,
127}
128
129/// Per-cooldown efficiency and drift summary.
130#[derive(Clone, Debug, Serialize)]
131#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
132pub struct CooldownView {
133    pub spell_id: u32,
134    pub uses: u64,
135    pub possible_uses: u64,
136    pub drift_sum_ms: u64,
137    pub max_drift_ms: u32,
138    pub efficiency: Option<f64>,
139    pub avg_drift_ms: Option<f64>,
140}
141
142/// Execution-time breakdown (active, idle, GCD-locked) with per-bucket action rates.
143#[derive(Clone, Debug, Serialize)]
144#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
145pub struct ExecutionView {
146    pub active_time_ms: u64,
147    pub idle_time_ms: u64,
148    pub gcd_locked_time_ms: u64,
149    pub idle_gcd_count: u64,
150    pub avg_queue_lag_ms: Option<f64>,
151    pub actions_per_bucket: Vec<f64>,
152    pub bucket_samples: Vec<u64>,
153}
154
155/// Damage profile split by direct, periodic, pet, and per-target damage.
156#[derive(Clone, Debug, Serialize)]
157#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
158pub struct DamageProfileView {
159    pub direct_damage: f64,
160    pub periodic_damage: f64,
161    pub pet_damage: f64,
162    pub by_target: Vec<DamageByTargetView>,
163}
164
165/// Damage dealt to a single target, with derived share of total damage.
166#[derive(Clone, Debug, Serialize)]
167#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
168pub struct DamageByTargetView {
169    pub target_id: u32,
170    pub total_damage: f64,
171    pub damage_pct: Option<f64>,
172}
173
174/// Bucketed fight timeline with DPS series and event windows.
175#[derive(Clone, Debug, Serialize)]
176#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
177pub struct TimelineView {
178    pub duration_ms: u32,
179    pub bucket_ms: u32,
180    pub dps_per_bucket: Vec<f64>,
181    pub bucket_samples: Vec<u64>,
182    pub markers: Vec<MarkerView>,
183    pub aura_windows: Vec<AuraWindowView>,
184    pub cooldown_windows: Vec<CooldownWindowView>,
185    pub phase_markers: Vec<PhaseMarkerView>,
186    pub encounter_events: Vec<EncounterEventView>,
187}
188
189/// A single timeline marker such as a damage, cast, resource, or proc event.
190#[derive(Clone, Debug, Serialize)]
191#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
192pub struct MarkerView {
193    pub time_ms: u32,
194    pub sequence: u64,
195    pub kind: String,
196    pub spell_or_aura_id: u32,
197    pub target_id: u32,
198    pub amount: u32,
199    pub is_crit: bool,
200    pub source: String,
201    pub source_id: u32,
202    pub pull_id: Option<u32>,
203    pub group_id: Option<u32>,
204}
205
206/// Time range during which an aura was active on a target.
207#[derive(Clone, Debug, Serialize)]
208#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
209pub struct AuraWindowView {
210    pub aura_id: u32,
211    pub target_id: u32,
212    pub start_ms: u32,
213    pub end_ms: u32,
214    pub source: String,
215    pub source_id: u32,
216    pub affected: String,
217    pub affected_id: u32,
218    pub pull_id: Option<u32>,
219    pub group_id: Option<u32>,
220}
221
222/// Time range during which a cooldown-bound ability was active.
223#[derive(Clone, Debug, Serialize)]
224#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
225pub struct CooldownWindowView {
226    pub spell_id: u32,
227    pub start_ms: u32,
228    pub duration_ms: u32,
229}
230
231/// Named phase boundary in the fight timeline.
232#[derive(Clone, Debug, Serialize)]
233#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
234pub struct PhaseMarkerView {
235    pub start_ms: u32,
236    pub end_ms: u32,
237    pub key: String,
238}
239
240/// Encounter lifecycle observation in its original causal sequence.
241#[derive(Clone, Debug, Serialize)]
242#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
243pub struct EncounterEventView {
244    pub kind: String,
245    pub time_ms: u32,
246    pub sequence: u64,
247    pub actor: String,
248    pub actor_id: u32,
249    pub previous_target_id: Option<u32>,
250    pub target_id: Option<u32>,
251    pub pull_id: u32,
252    pub group_id: Option<u32>,
253    pub x: Option<f64>,
254    pub y: Option<f64>,
255    pub heading: Option<f64>,
256    pub layer_id: Option<u32>,
257}
258
259/// Lookup tables for dictionary-encoded ids in the analytics view.
260#[derive(Clone, Debug, Default, Serialize)]
261#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
262pub struct DictionaryMap {
263    pub spell_ids: Vec<u32>,
264    pub aura_ids: Vec<u32>,
265    pub targets: Vec<TargetMapEntry>,
266    pub units: Vec<UnitMapEntry>,
267}
268
269/// Dictionary entry mapping a target id to a display label.
270#[derive(Clone, Debug, Serialize)]
271#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
272pub struct TargetMapEntry {
273    pub id: u32,
274    pub label: String,
275    pub npc_id: Option<u32>,
276    pub group_id: Option<u32>,
277    pub enemy_tags: Vec<String>,
278    pub group_tags: Vec<String>,
279}
280
281/// Collision-free tagged unit dictionary entry.
282#[derive(Clone, Debug, Serialize)]
283#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
284pub struct UnitMapEntry {
285    pub kind: String,
286    pub id: u32,
287    pub label: String,
288    pub target_id: Option<u32>,
289}