1pub fn language_from_extension(extension: &str) -> &str {
7 match extension.to_lowercase().as_str() {
8 "rs" => "rust",
9 "py" => "python",
10 "js" => "javascript",
11 "ts" => "typescript",
12 "jsx" => "jsx",
13 "tsx" => "tsx",
14 "rb" => "ruby",
15 "go" => "go",
16 "java" => "java",
17 "kt" | "kts" => "kotlin",
18 "c" | "h" => "c",
19 "cpp" | "cc" | "cxx" | "hpp" | "hxx" => "cpp",
20 "cs" => "csharp",
21 "swift" => "swift",
22 "php" => "php",
23 "scala" => "scala",
24 "sh" | "bash" | "zsh" | "fish" => "bash",
25 "ps1" => "powershell",
26 "html" | "htm" => "html",
27 "css" => "css",
28 "scss" => "scss",
29 "sass" => "sass",
30 "less" => "less",
31 "json" => "json",
32 "yaml" | "yml" => "yaml",
33 "toml" => "toml",
34 "xml" => "xml",
35 "sql" => "sql",
36 "md" | "markdown" => "markdown",
37 "dockerfile" => "dockerfile",
38 "lua" => "lua",
39 "r" => "r",
40 "dart" => "dart",
41 "zig" => "zig",
42 "nim" => "nim",
43 "ex" | "exs" => "elixir",
44 "erl" | "hrl" => "erlang",
45 "hs" => "haskell",
46 "ml" | "mli" => "ocaml",
47 "clj" | "cljs" => "clojure",
48 "tf" => "hcl",
49 "vue" => "vue",
50 "svelte" => "svelte",
51 "graphql" | "gql" => "graphql",
52 "proto" => "protobuf",
53 "makefile" | "mk" => "makefile",
54 _ => extension,
55 }
56}
57
58pub fn language_for_path(path: &str) -> &str {
63 let filename = path.rsplit('/').next().unwrap_or(path);
64 match filename.rsplit_once('.') {
65 Some((_, ext)) => language_from_extension(ext),
66 None => language_from_extension(filename),
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn known_extensions() {
76 assert_eq!(language_from_extension("rs"), "rust");
77 assert_eq!(language_from_extension("py"), "python");
78 assert_eq!(language_from_extension("js"), "javascript");
79 assert_eq!(language_from_extension("ts"), "typescript");
80 assert_eq!(language_from_extension("go"), "go");
81 assert_eq!(language_from_extension("java"), "java");
82 assert_eq!(language_from_extension("cpp"), "cpp");
83 assert_eq!(language_from_extension("cc"), "cpp");
84 assert_eq!(language_from_extension("c"), "c");
85 assert_eq!(language_from_extension("h"), "c");
86 assert_eq!(language_from_extension("hpp"), "cpp");
87 assert_eq!(language_from_extension("cs"), "csharp");
88 assert_eq!(language_from_extension("rb"), "ruby");
89 assert_eq!(language_from_extension("kt"), "kotlin");
90 assert_eq!(language_from_extension("kts"), "kotlin");
91 assert_eq!(language_from_extension("sh"), "bash");
92 assert_eq!(language_from_extension("bash"), "bash");
93 assert_eq!(language_from_extension("zsh"), "bash");
94 assert_eq!(language_from_extension("fish"), "bash");
95 assert_eq!(language_from_extension("yaml"), "yaml");
96 assert_eq!(language_from_extension("yml"), "yaml");
97 assert_eq!(language_from_extension("json"), "json");
98 assert_eq!(language_from_extension("toml"), "toml");
99 assert_eq!(language_from_extension("md"), "markdown");
100 assert_eq!(language_from_extension("ex"), "elixir");
101 assert_eq!(language_from_extension("exs"), "elixir");
102 assert_eq!(language_from_extension("hs"), "haskell");
103 assert_eq!(language_from_extension("tf"), "hcl");
104 assert_eq!(language_from_extension("vue"), "vue");
105 assert_eq!(language_from_extension("svelte"), "svelte");
106 assert_eq!(language_from_extension("proto"), "protobuf");
107 assert_eq!(language_from_extension("graphql"), "graphql");
108 assert_eq!(language_from_extension("gql"), "graphql");
109 }
110
111 #[test]
112 fn case_insensitive() {
113 assert_eq!(language_from_extension("RS"), "rust");
114 assert_eq!(language_from_extension("Py"), "python");
115 assert_eq!(language_from_extension("JS"), "javascript");
116 }
117
118 #[test]
119 fn unknown_extension_returns_as_is() {
120 assert_eq!(language_from_extension("xyz"), "xyz");
121 assert_eq!(language_from_extension("foo"), "foo");
122 }
123
124 #[test]
125 fn extensionless_filenames() {
126 assert_eq!(language_from_extension("Dockerfile"), "dockerfile");
127 assert_eq!(language_from_extension("dockerfile"), "dockerfile");
128 assert_eq!(language_from_extension("Makefile"), "makefile");
129 assert_eq!(language_from_extension("makefile"), "makefile");
130 }
131
132 #[test]
133 fn language_for_path_basic_extension() {
134 assert_eq!(language_for_path("src/main.rs"), "rust");
135 }
136
137 #[test]
138 fn language_for_path_dockerfile_in_subdir() {
139 assert_eq!(language_for_path("docker/Dockerfile"), "dockerfile");
140 }
141
142 #[test]
143 fn language_for_path_dotted_directory() {
144 assert_eq!(language_for_path("some.config/Dockerfile"), "dockerfile");
145 }
146
147 #[test]
148 fn language_for_path_makefile_in_subdir() {
149 assert_eq!(language_for_path("build/Makefile"), "makefile");
150 }
151
152 #[test]
153 fn language_for_path_multiple_dots() {
154 assert_eq!(language_for_path("file.test.ts"), "typescript");
155 }
156
157 #[test]
158 fn language_for_path_dotfile() {
159 assert_eq!(language_for_path(".gitignore"), "gitignore");
160 }
161}