Skip to main content

normalize_languages/
caddy.rs

1//! Caddyfile configuration support.
2
3use crate::{ContainerBody, Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6/// Caddy language support.
7pub struct Caddy;
8
9impl Language for Caddy {
10    fn name(&self) -> &'static str {
11        "Caddy"
12    }
13    fn extensions(&self) -> &'static [&'static str] {
14        &["caddyfile"]
15    }
16    fn grammar_name(&self) -> &'static str {
17        "caddy"
18    }
19
20    fn as_symbols(&self) -> Option<&dyn LanguageSymbols> {
21        Some(self)
22    }
23
24    fn extract_imports(&self, node: &Node, content: &str) -> Vec<Import> {
25        if node.kind() != "import" {
26            return Vec::new();
27        }
28
29        let text = &content[node.byte_range()];
30        vec![Import {
31            module: text.trim().to_string(),
32            names: Vec::new(),
33            alias: None,
34            is_wildcard: false,
35            is_relative: false,
36            line: node.start_position().row + 1,
37        }]
38    }
39
40    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
41        // Caddy: import path
42        format!("import {}", import.module)
43    }
44
45    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
46        node.child_by_field_name("body")
47    }
48
49    fn analyze_container_body(
50        &self,
51        body_node: &Node,
52        content: &str,
53        inner_indent: &str,
54    ) -> Option<ContainerBody> {
55        crate::body::analyze_brace_body(body_node, content, inner_indent)
56    }
57
58    fn node_name<'a>(&self, node: &Node, content: &'a str) -> Option<&'a str> {
59        if let Some(name_node) = node.child_by_field_name("name") {
60            return Some(&content[name_node.byte_range()]);
61        }
62        let mut cursor = node.walk();
63        if let Some(first_child) = node.children(&mut cursor).next() {
64            return Some(&content[first_child.byte_range()]);
65        }
66        None
67    }
68}
69
70impl LanguageSymbols for Caddy {}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use crate::validate_unused_kinds_audit;
76
77    #[test]
78    fn unused_node_kinds_audit() {
79        #[rustfmt::skip]
80        let documented_unused: &[&str] = &[
81            // Matcher-related (matcher_name, matcher_token, matcher_definition used in tags.scm)
82            "matcher_path", "matcher_path_regexp",
83            "standard_matcher", "uri_path_with_placeholders",
84            // Directive-related
85            "directive_import", "directive_request_body", "request_body_option_max_size",
86            "fastcgi_option_try_files", "encode_format", "log_option_format",
87            // Blocks
88            "global_options_block",
89            // structural node, not extracted as symbols
90            "directive_block",
91        ];
92        validate_unused_kinds_audit(&Caddy, documented_unused)
93            .expect("Caddy unused node kinds audit failed");
94    }
95}