1#[cfg(test)]
2use googletest::{Result as GtestResult, prelude::*};
3
4#[derive(Debug)]
5pub(super) struct Category {
6 pub name: &'static str,
7 pub patterns: &'static [&'static str],
8 pub description: &'static str,
9}
10
11#[rustfmt::skip]
12pub(super) static CATEGORIES: &[Category] = &[
13 Category {
14 name: "rotation",
15 patterns: &["wowlab_engine::rotation", "inkwell", "jit"],
16 description: "JIT-compiled rotation evaluation (Cranelift). Condition checks, expression trees, context population.",
17 },
18 Category {
19 name: "cast_pipeline",
20 patterns: &["wowlab_engine::cast"],
21 description: "CastEngine: unified cast pipeline. Costs, cooldowns, GCD, hooks (PreCast/PostCast/ModifyDamage).",
22 },
23 Category {
24 name: "damage",
25 patterns: &["wowlab_engine::combat::damage"],
26 description: "DamagePipeline: base + AP*coef + SP*coef, crit roll, multipliers, armor reduction.",
27 },
28 Category {
29 name: "auras",
30 patterns: &["wowlab_engine::combat::aura"],
31 description: "Buff/debuff tracking. AuraTracker manages active auras, stacking, duration, periodic ticks.",
32 },
33 Category {
34 name: "cooldowns",
35 patterns: &["wowlab_engine::combat::cooldown"],
36 description: "Cooldown and charge tracking per spell.",
37 },
38 Category {
39 name: "procs",
40 patterns: &["wowlab_engine::combat::proc"],
41 description: "Proc system: RPPM, proc flags, trigger dispatch via TriggerBus.",
42 },
43 Category {
44 name: "resources",
45 patterns: &["wowlab_engine::combat::resource"],
46 description: "Resource pools (Focus, Fury, etc.), regen, spending, waste tracking.",
47 },
48 Category {
49 name: "stats",
50 patterns: &["wowlab_engine::combat::stats", "wowlab_engine::combat::math"],
51 description: "Stat attributes, ratings, scaling coefficients, combat math.",
52 },
53 Category {
54 name: "actors",
55 patterns: &["wowlab_engine::combat::actor"],
56 description: "Player/enemy/pet actors. Initialization, stat setup, pet summoning.",
57 },
58 Category {
59 name: "sim_loop",
60 patterns: &["wowlab_engine::sim"],
61 description: "SimState, event queue, simulation executor, batch runner.",
62 },
63 Category {
64 name: "modules",
65 patterns: &["wowlab_engine::module", "wowlab_engine::specs"],
66 description: "Module composition (class + spec modules), ModuleHandler adapter, hook dispatch.",
67 },
68 Category {
69 name: "metrics",
70 patterns: &["wowlab_engine::metrics"],
71 description: "Metrics collection, descriptors, per-spell/aura tracking, exporters.",
72 },
73 Category {
74 name: "defs",
75 patterns: &["wowlab_engine::defs"],
76 description: "SpellDefinitionDraft, AuraDefinitionDraft, BuilderSpellDef, BuilderAuraDef, TalentDef. Spell/aura execution.",
77 },
78 Category {
79 name: "host",
80 patterns: &["wowlab_engine::host", "wowlab_engine::intent"],
81 description: "Host API, simulation request handling, intent schema.",
82 },
83 Category {
84 name: "data",
85 patterns: &[
86 "wowlab_engine::data",
87 "wowlab_engine::config",
88 "wowlab_parsers",
89 "wowlab_types::data",
90 "csv::",
91 "csv_core",
92 "serde",
93 "toml",
94 ],
95 description: "Data loading (DBC CSV parsing, resolver, item resolution).",
96 },
97 Category {
98 name: "collections",
99 patterns: &["hashbrown", "hash_map", "btree"],
100 description: "Hash maps, btrees, collection operations (not engine-specific).",
101 },
102 Category {
103 name: "memory",
104 patterns: &[
105 "alloc::",
106 "<alloc::",
107 "drop_in_place",
108 "dealloc",
109 "mi_",
110 "mmap",
111 ],
112 description: "Allocations, deallocations, Vec resizing, drop glue, mimalloc.",
113 },
114 Category {
115 name: "tracing",
116 patterns: &["tracing", "smallvec", "sharded_slab"],
117 description: "Tracing subscriber overhead (fmt layer, span storage, SmallVec alloc/drop).",
118 },
119];
120
121pub(super) fn classify(name: &str) -> &'static str {
122 let lower = name.to_lowercase();
123
124 for cat in CATEGORIES {
125 if cat
126 .patterns
127 .iter()
128 .any(|p| lower.contains(&p.to_lowercase()))
129 {
130 return cat.name;
131 }
132 }
133
134 if name.starts_with("std::") || name.starts_with("core::") || name.starts_with('_') {
135 return "stdlib";
136 }
137
138 "other"
139}
140
141pub(super) fn description(name: &str) -> &'static str {
142 for cat in CATEGORIES {
143 if cat.name == name {
144 return cat.description;
145 }
146 }
147
148 match name {
149 "stdlib" => "Rust stdlib/core (hashing, iterators, formatting, etc.).",
150 "other" => "Unclassified: anything not matching the above patterns.",
151 _ => "",
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[gtest]
160 fn classify_routes_jit_symbols_to_rotation() -> GtestResult<()> {
161 verify_that!(classify("wowlab_engine::rotation::eval"), eq("rotation"))?;
162 verify_that!(classify("inkwell::execution_engine::run"), eq("rotation"))?;
163 verify_that!(classify("jit_compile"), eq("rotation"))?;
164
165 Ok(())
166 }
167
168 #[gtest]
169 fn classify_routes_cast_pipeline() -> GtestResult<()> {
170 verify_that!(
171 classify("wowlab_engine::cast::engine::run"),
172 eq("cast_pipeline")
173 )?;
174
175 Ok(())
176 }
177
178 #[gtest]
179 fn classify_routes_stdlib_prefixes_to_stdlib() -> GtestResult<()> {
180 verify_that!(classify("std::vec::Vec::push"), eq("stdlib"))?;
181 verify_that!(classify("core::option::Option::map"), eq("stdlib"))?;
182 verify_that!(classify("_ZN3std9panicking12default_hook"), eq("stdlib"))?;
183
184 Ok(())
185 }
186
187 #[gtest]
188 fn classify_unknown_falls_through_to_other() -> GtestResult<()> {
189 verify_that!(classify("totally_unrelated_function_name"), eq("other"))?;
190
191 Ok(())
192 }
193
194 #[gtest]
195 fn classify_is_case_insensitive() -> GtestResult<()> {
196 verify_that!(classify("WOWLAB_ENGINE::ROTATION::EVAL"), eq("rotation"))?;
197
198 Ok(())
199 }
200
201 #[gtest]
202 fn description_returns_table_entry_for_known_category() -> GtestResult<()> {
203 let desc = description("rotation");
204
205 verify_true!(desc.contains("JIT"))?;
206
207 Ok(())
208 }
209
210 #[gtest]
211 fn description_returns_synthetic_for_stdlib_and_other() -> GtestResult<()> {
212 verify_true!(description("stdlib").contains("stdlib"))?;
213 verify_true!(description("other").contains("Unclassified"))?;
214
215 Ok(())
216 }
217
218 #[gtest]
219 fn description_returns_empty_for_unknown_category() -> GtestResult<()> {
220 verify_that!(description("not_a_real_category"), eq(""))?;
221
222 Ok(())
223 }
224}