wowlab_tidy/languages/toml/rules/manifest/
shared_anchors.rs1use wowlab_fs::{file, path::Path};
2
3use super::MANIFEST_PREFIX;
4use crate::{Example, TomlCtx, Violation, violation};
5
6const EXAMPLES: &[Example] = &[];
7const ORDERED_SECTIONS: &[&str] = &["auras", "spells", "auto_attacks", "hero_talents", "effects"];
8
9crate::toml_rule!(
10 toml_manifest_shared_anchors,
11 "Require shared includes to anchor every order-sensitive section they contribute.",
12 "Explicit insertion points keep generated local indices and named declaration order stable as shared manifests evolve.",
13 High,
14);
15
16fn check_toml_manifest_shared_anchors(ctx: &TomlCtx<'_>) -> Vec<Violation> {
17 if !ctx.file.rel.starts_with(MANIFEST_PREFIX)
18 || Path::new(ctx.file.rel)
19 .file_name()
20 .is_none_or(|name| name != "manifest.toml")
21 || !ctx.parse.errors.is_empty()
22 {
23 return Vec::new();
24 }
25
26 let Ok(document) = toml::from_str::<toml::Table>(ctx.file.contents) else {
27 return Vec::new();
28 };
29 let Some(includes) = document.get("shared").and_then(toml::Value::as_array) else {
30 return Vec::new();
31 };
32 let Some(class_dir) = ctx.file.path.parent().and_then(Path::parent) else {
33 return Vec::new();
34 };
35 let mut violations = Vec::new();
36
37 for include in includes {
38 let Some(include) = include.as_table() else {
39 continue;
40 };
41 let Some(relative) = include.get("path").and_then(toml::Value::as_str) else {
42 continue;
43 };
44 let shared_path = class_dir.join("shared").join(relative);
45 let Ok(source) = file::read_text(&shared_path) else {
46 continue;
47 };
48 let Ok(shared) = toml::from_str::<toml::Table>(&source) else {
49 continue;
50 };
51 let before = include.get("before").and_then(toml::Value::as_table);
52 let append = include.get("append").and_then(toml::Value::as_table);
53
54 for section in ORDERED_SECTIONS {
55 if shared.contains_key(*section)
56 && before.is_none_or(|anchors| !anchors.contains_key(*section))
57 && append.is_none_or(|sections| {
58 sections.get(*section).and_then(toml::Value::as_bool) != Some(true)
59 })
60 {
61 violations.push(violation(
62 ctx.file.rel,
63 1,
64 format!(
65 "shared include `{relative}` contributes [{section}] without shared.before.{section} or shared.append.{section} = true"
66 ),
67 ));
68 }
69 }
70 }
71
72 violations
73}