1use proc_macro2::TokenStream;
2use quote::{format_ident, quote};
3use wowlab_manifest_schema::{Manifest, SharedBindings};
4
5use crate::{
6 gen_aura::aura_functions,
7 gen_build::{build_function, handler_factory},
8 gen_constants::{
9 constants, damage_action_expr, effect_constant, effects, local_indices, metric_keys,
10 talent_table,
11 },
12 gen_descriptor::{descriptor, smoke_test},
13 gen_spells::spell_functions,
14 helpers::spec_from_wow_id,
15 rust_source::{FileComment, inspect_link, lit_int, lit_str, render_rust, rust_ident},
16};
17
18fn spec_imports(manifest: &Manifest) -> TokenStream {
19 let aura_imports = (!manifest.auras.is_empty()).then(|| {
20 quote! {
21 AuraDefinitionDraft,
22 LocalAuraIdx,
23 }
24 });
25 let spell_imports = (!manifest.spells.is_empty()).then(|| {
26 quote! {
27 LocalSpellIdx,
28 SpellDefinitionDraft,
29 }
30 });
31 let has_server_proc_entries = manifest
32 .spells
33 .values()
34 .any(|spell| !spell.proc_attributes.is_empty())
35 || manifest
36 .auras
37 .values()
38 .any(|aura| !aura.proc_attributes.is_empty());
39 let proc_import = has_server_proc_entries.then(|| {
40 quote!(
41 use wowlab_engine_domain::dbc::ProcAttributes;
42 )
43 });
44 let hero_import = (!manifest.hero_talents.is_empty()).then(|| quote!(HeroTalentTreeDesc,));
45 let server_proc_import = has_server_proc_entries.then(|| quote!(DeclaredServerProcEntry,));
46
47 quote! {
48 use wowlab_types::{
49 combat::ResourceType,
50 game::SpecId,
51 };
52 #proc_import
53 use wowlab_engine_combat::{
54 CombatSystemBuilder,
55 CombatSystemDraft,
56 #aura_imports
57 #spell_imports
58 };
59 use wowlab_engine_ports::{
60 DeclaredSpecMetadata,
61 SpecDescriptor,
62 SpecHandler,
63 #hero_import
64 #server_proc_import
65 };
66 use wowlab_engine_gamedata::ResolvedGameData;
67 }
68}
69
70pub(super) fn generate_spec_file(
71 manifest: &Manifest,
72 source_path: &str,
73 module_name: &str,
74 display_name: &str,
75) -> anyhow::Result<String> {
76 let spec_variant = format!("{:?}", spec_from_wow_id(manifest.spec.id)?);
77 let imports = spec_imports(manifest);
78 let identity_module = rust_ident(module_name)?;
79 let damage_namespace = quote!(
80 wowlab_engine_combat::generated_identities::specs::#identity_module::damage
81 );
82 let constants = constants(manifest, &damage_namespace)?;
83 let local_indices = local_indices(manifest, module_name)?;
84 let effects = effects(manifest)?;
85 let talents = talent_table(manifest)?;
86 let metric_keys = metric_keys(manifest)?;
87 let auras = aura_functions(manifest, module_name)?;
88 let spells = spell_functions(manifest, module_name)?;
89 let build = build_function(manifest, &spec_variant, module_name)?;
90 let handler = handler_factory(manifest)?;
91 let descriptor = descriptor(manifest, &spec_variant, display_name)?;
92 let smoke_test = smoke_test();
93 let preamble = [
94 FileComment::inner_doc(format!(
95 "@generated by codegen-cli from manifests/specs/{source_path} -- do not edit by hand."
96 ))?,
97 FileComment::line("Generated code: naming follows game data conventions")?,
98 ];
99 let lint_level = format_ident!("allow");
100
101 Ok(render_rust(
102 quote! {
103 #![#lint_level(non_snake_case, dead_code)]
104
105 #imports
106 #constants
107 #local_indices
108 #effects
109 #talents
110 #metric_keys
111 #auras
112 #spells
113 #build
114 #handler
115 #descriptor
116 #smoke_test
117 },
118 &preamble,
119 )?)
120}
121
122pub(super) fn generate_shared_file(
123 bindings: &SharedBindings,
124 source_path: &str,
125 identity_module: &str,
126) -> anyhow::Result<String> {
127 let identity_module = rust_ident(identity_module)?;
128 let damage_namespace = quote!(
129 wowlab_engine_combat::generated_identities::shared::#identity_module::damage
130 );
131 let reported_spells = bindings
132 .reported_spells
133 .iter()
134 .map(|(name, id)| {
135 let name = rust_ident(&name.to_uppercase())?;
136 let id_literal = lit_int(id);
137 let documentation = lit_str(&inspect_link("spell", id));
138
139 Ok::<_, syn::Error>(quote! {
140 #[doc = #documentation]
141 pub(crate) const #name: u32 = #id_literal;
142 })
143 })
144 .collect::<Result<Vec<_>, _>>()?;
145 let reported_spells = (!reported_spells.is_empty()).then(|| {
146 quote! {
147 pub(crate) mod REPORTED_SPELL {
148 #(#reported_spells)*
149 }
150 }
151 });
152 let effects = bindings
153 .effects
154 .iter()
155 .map(|(name, effect)| {
156 let documentation = lit_str(&inspect_link("spell", effect.spell_id));
157 let constant = effect_constant(name, effect.spell_id, effect.effect)?;
158
159 Ok::<_, anyhow::Error>(quote! {
160 #[doc = #documentation]
161 #constant
162 })
163 })
164 .collect::<anyhow::Result<Vec<_>>>()?;
165 let effects = (!effects.is_empty()).then(|| {
166 quote! {
167 pub(crate) mod EFFECT {
168 #(#effects)*
169 }
170 }
171 });
172 let damage_reported = bindings
173 .reported_spells
174 .iter()
175 .map(|(name, spell_id)| {
176 let name = rust_ident(&name.to_uppercase())?;
177 let action = damage_action_expr(&damage_namespace, *spell_id);
178
179 Ok::<_, anyhow::Error>(quote! {
180 pub(crate) const #name: wowlab_engine_combat::DamageActionId =
181 #action;
182 })
183 })
184 .collect::<anyhow::Result<Vec<_>>>()?;
185 let damage_effects = bindings
186 .effects
187 .iter()
188 .map(|(name, effect)| {
189 let name = rust_ident(name)?;
190 let effect = crate::gen_constants::damage_effect_expr(
191 &damage_namespace,
192 effect.spell_id,
193 effect.effect,
194 );
195
196 Ok::<_, anyhow::Error>(quote! {
197 pub(crate) const #name: wowlab_engine_combat::DamageEffectId =
198 #effect;
199 })
200 })
201 .collect::<anyhow::Result<Vec<_>>>()?;
202 let preamble = [
203 FileComment::inner_doc(format!(
204 "@generated by codegen-cli from manifests/specs/{source_path} -- do not edit by hand."
205 ))?,
206 FileComment::line("Generated shared manifest bindings")?,
207 ];
208 let lint_level = format_ident!("allow");
209
210 Ok(render_rust(
211 quote! {
212 #![#lint_level(non_snake_case, dead_code)]
213
214 #reported_spells
215 #effects
216 pub(crate) mod DAMAGE_REPORTED {
217 #(#damage_reported)*
218 }
219 pub(crate) mod DAMAGE_EFFECT {
220 #(#damage_effects)*
221 }
222 },
223 &preamble,
224 )?)
225}