Skip to main content

codegen/app/
identifiers.rs

1use anyhow::bail;
2use wowlab_fs::path::Path;
3use wowlab_manifest_schema::ItemManifestEntry;
4
5use super::{generation::LoadedSpec, ownership::validate_owned_path};
6use crate::helpers::to_snake;
7
8pub(super) fn validate_rust_identifier(identifier: &str, context: &str) -> anyhow::Result<()> {
9    if syn::parse_str::<syn::Ident>(identifier).is_ok() {
10        Ok(())
11    } else {
12        bail!("{context} generates invalid Rust identifier `{identifier}`")
13    }
14}
15
16pub(super) fn validate_module_component(component: &str, context: &str) -> anyhow::Result<()> {
17    validate_owned_path(Path::new(component))?;
18
19    validate_rust_identifier(component, context)
20}
21
22pub(super) fn validate_named_identifiers<'a>(
23    names: impl Iterator<Item = &'a String>,
24    section: &str,
25    path: &Path,
26) -> anyhow::Result<()> {
27    for name in names {
28        validate_rust_identifier(name, &format!("{} [{section}.{name}]", path.display()))?;
29    }
30
31    Ok(())
32}
33
34pub(super) fn validate_spec_identifiers(spec: &LoadedSpec) -> anyhow::Result<()> {
35    let path = &spec.path;
36
37    validate_module_component(&spec.mod_name, &format!("spec manifest {}", path.display()))?;
38    validate_named_identifiers(spec.manifest.spells.keys(), "spells", path)?;
39    validate_named_identifiers(spec.manifest.auras.keys(), "auras", path)?;
40    validate_named_identifiers(spec.manifest.talents.keys(), "talents", path)?;
41    validate_named_identifiers(spec.manifest.set_bonuses.keys(), "set_bonuses", path)?;
42    validate_named_identifiers(spec.manifest.effects.keys(), "effects", path)?;
43
44    for name in spec.manifest.reported_spells.keys() {
45        validate_rust_identifier(
46            &name.to_uppercase(),
47            &format!("{} [reported_spells.{name}]", path.display()),
48        )?;
49    }
50
51    for (tree_name, tree) in &spec.manifest.hero_talents {
52        validate_rust_identifier(
53            &tree_name.to_uppercase(),
54            &format!("{} [hero_talents.{tree_name}]", path.display()),
55        )?;
56        validate_named_identifiers(
57            tree.spells.keys(),
58            &format!("hero_talents.{tree_name}.spells"),
59            path,
60        )?;
61        validate_named_identifiers(
62            tree.auras.keys(),
63            &format!("hero_talents.{tree_name}.auras"),
64            path,
65        )?;
66    }
67
68    if let Some(metric_keys) = &spec.manifest.metric_keys {
69        validate_rust_identifier(
70            &metric_keys.module_name,
71            &format!("{} [metric_keys.module_name]", path.display()),
72        )?;
73
74        for key in &metric_keys.keys {
75            validate_rust_identifier(key, &format!("{} [metric_keys.keys]", path.display()))?;
76        }
77    }
78
79    Ok(())
80}
81
82pub(super) fn validate_item_identifiers(
83    key: &str,
84    item: &ItemManifestEntry,
85    path: &Path,
86) -> anyhow::Result<()> {
87    validate_module_component(&to_snake(key), &format!("{} [items.{key}]", path.display()))?;
88    validate_named_identifiers(item.spells.keys(), &format!("items.{key}.spells"), path)?;
89    validate_named_identifiers(item.auras.keys(), &format!("items.{key}.auras"), path)?;
90
91    validate_named_identifiers(item.effects.keys(), &format!("items.{key}.effects"), path)
92}