Skip to main content

diagnostics/
helpers.rs

1use rules::{RuleOrigin, lookup_rule, section_for_rule_id};
2use template_model::{SourceSpan, SpanProvider, UNKNOWN_SPAN};
3
4/// Looks up the `RuleOrigin` for a rule ID from the registry.
5/// Panics if the rule is not registered - every built-in rule must be in the registry.
6/// For custom/guard rules (not in the registry), callers must set `source` directly.
7pub fn source_for_rule(rule_id: &str) -> RuleOrigin {
8    lookup_rule(rule_id).unwrap_or_else(|| panic!("Rule '{}' not found in RULE_REGISTRY", rule_id)).origin
9}
10
11/// Maps a rule ID to its template section (via `section_for_rule_id`) and
12/// looks up the span through the given `SpanProvider`. Returns `UNKNOWN_SPAN`
13/// if the rule has no associated section or the section has no span.
14pub fn resolve_section_span(rule_id: &str, span_provider: &dyn SpanProvider) -> SourceSpan {
15    match section_for_rule_id(rule_id) {
16        Some(section) => span_provider.source_location(section).unwrap_or(UNKNOWN_SPAN),
17        None => UNKNOWN_SPAN,
18    }
19}
20
21#[cfg(test)]
22mod tests {
23    use super::*;
24
25    struct FakeSpanProvider {
26        known_section: &'static str,
27        span: SourceSpan,
28    }
29
30    impl SpanProvider for FakeSpanProvider {
31        fn source_location(&self, path: &str) -> Option<SourceSpan> {
32            if path == self.known_section { Some(self.span) } else { None }
33        }
34    }
35
36    struct EmptySpanProvider;
37    impl SpanProvider for EmptySpanProvider {
38        fn source_location(&self, _path: &str) -> Option<SourceSpan> {
39            None
40        }
41    }
42
43    #[test]
44    fn resolve_section_span_returns_span_when_section_and_provider_match() {
45        let expected = SourceSpan { start_line: 5, start_column: 0, end_line: 10, end_column: 0 };
46        let provider = FakeSpanProvider { known_section: "Resources", span: expected };
47
48        let result = resolve_section_span("F0001", &provider);
49        assert_eq!(result, expected);
50    }
51
52    #[test]
53    fn resolve_section_span_returns_unknown_when_provider_has_no_match() {
54        let provider = EmptySpanProvider;
55        let result = resolve_section_span("F0001", &provider);
56        assert_eq!(result, UNKNOWN_SPAN);
57    }
58
59    #[test]
60    fn resolve_section_span_returns_unknown_for_unmapped_rule() {
61        let provider = FakeSpanProvider {
62            known_section: "Resources",
63            span: SourceSpan { start_line: 1, start_column: 0, end_line: 1, end_column: 0 },
64        };
65        let result = resolve_section_span("ZZZZZ", &provider);
66        assert_eq!(result, UNKNOWN_SPAN);
67    }
68}