normalize_languages/
dockerfile.rs1use crate::{Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6pub struct Dockerfile;
8
9impl Language for Dockerfile {
10 fn name(&self) -> &'static str {
11 "Dockerfile"
12 }
13 fn extensions(&self) -> &'static [&'static str] {
14 &["dockerfile"]
15 }
16 fn grammar_name(&self) -> &'static str {
17 "dockerfile"
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> {
29 if node.kind() != "from_instruction" {
30 return Vec::new();
31 }
32
33 if let Some(image) = self.extract_image_name(node, content) {
34 return vec![Import {
35 module: image,
36 names: Vec::new(),
37 alias: self.extract_stage_name(node, content),
38 is_wildcard: false,
39 is_relative: false,
40 line: node.start_position().row + 1,
41 }];
42 }
43
44 Vec::new()
45 }
46
47 fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
48 format!("FROM {}", import.module)
50 }
51
52 fn node_name<'a>(&self, _node: &Node, _content: &'a str) -> Option<&'a str> {
53 None
54 }
55}
56
57impl LanguageSymbols for Dockerfile {}
58
59impl Dockerfile {
60 fn extract_image_name(&self, node: &Node, content: &str) -> Option<String> {
62 let mut cursor = node.walk();
63 for child in node.children(&mut cursor) {
64 if child.kind() == "image_spec" {
65 return Some(content[child.byte_range()].to_string());
66 }
67 }
68 None
69 }
70
71 fn extract_stage_name(&self, node: &Node, content: &str) -> Option<String> {
73 let mut cursor = node.walk();
74 let mut found_as = false;
75 for child in node.children(&mut cursor) {
76 if found_as && child.kind() == "image_alias" {
77 return Some(content[child.byte_range()].to_string());
78 }
79 if child.kind() == "as_instruction" {
80 found_as = true;
81 }
82 }
83 None
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::validate_unused_kinds_audit;
91
92 #[test]
93 fn unused_node_kinds_audit() {
94 #[rustfmt::skip]
95 let documented_unused: &[&str] = &[
96 "add_instruction", "cmd_instruction", "copy_instruction",
98 "cross_build_instruction", "entrypoint_instruction",
99 "expose_instruction", "healthcheck_instruction", "heredoc_block",
100 "label_instruction", "maintainer_instruction", "onbuild_instruction",
101 "run_instruction", "shell_instruction", "stopsignal_instruction",
102 "user_instruction", "volume_instruction", "workdir_instruction",
103 ];
104
105 validate_unused_kinds_audit(&Dockerfile, documented_unused)
106 .expect("Dockerfile unused node kinds audit failed");
107 }
108}