wowlab_tidy/languages/toml/rules/manifest/
section_contiguity.rs1use std::collections::HashSet;
4
5use super::MANIFEST_PREFIX;
6use crate::{Example, TomlCtx, Violation, violation};
7
8#[rustfmt::skip]
9const EXAMPLES: &[Example] = &[
10 Example { label: "contiguous sections", code: "schema_version = 1\n\n[spec]\nid = 1\n\n[auras.ONE]\nid = 2\n\n[auras.TWO]\nid = 3\n\n[spells.ONE]\nid = 4\n", pass: true },
11 Example { label: "aura section resumes", code: "schema_version = 1\n\n[spec]\nid = 1\n\n[auras.ONE]\nid = 2\n\n[spells.ONE]\nid = 3\n\n[auras.TWO]\nid = 4\n", pass: false },
12];
13
14crate::toml_rule!(
15 toml_manifest_section_contiguity,
16 "Require each top-level manifest section to occupy one contiguous region.",
17 "Returning to auras, spells, or another section later in a file hides append-only fragments and makes declaration order difficult to review.",
18 Medium,
19);
20
21fn check_toml_manifest_section_contiguity(ctx: &TomlCtx<'_>) -> Vec<Violation> {
22 if !ctx.file.rel.starts_with(MANIFEST_PREFIX) || !ctx.parse.errors.is_empty() {
23 return Vec::new();
24 }
25
26 let mut closed = HashSet::new();
27 let mut previous: Option<&str> = None;
28 let mut violations = Vec::new();
29
30 for (index, line) in ctx.file.lines.iter().enumerate() {
31 let Some(section) = top_level_section(line.trim()) else {
32 continue;
33 };
34
35 if previous == Some(section) {
36 continue;
37 }
38
39 if let Some(previous) = previous {
40 closed.insert(previous);
41 }
42
43 if closed.contains(section) {
44 violations.push(violation(
45 ctx.file.rel,
46 index + 1,
47 format!("section `{section}` resumes after another top-level section"),
48 ));
49 }
50
51 previous = Some(section);
52 }
53
54 violations
55}
56
57fn top_level_section(header: &str) -> Option<&str> {
58 let inner = header
59 .strip_prefix("[[")
60 .and_then(|value| value.strip_suffix("]]"))
61 .or_else(|| {
62 header
63 .strip_prefix('[')
64 .and_then(|value| value.strip_suffix(']'))
65 })?;
66
67 inner.split('.').next()
68}
69
70crate::tidy_toml_test!(check_toml_manifest_section_contiguity, {
71 crate::example_tests!(EXAMPLES, check_toml_manifest_section_contiguity);
72});