Skip to main content

babyrite/
utils.rs

1//! Utility functions for babyrite.
2
3/// Returns the language identifier for syntax highlighting based on file extension.
4///
5/// Used for code block language hints when expanding GitHub permalinks.
6pub 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
58/// Returns the language identifier for syntax highlighting based on a file path.
59///
60/// Uses the file extension when present; extensionless filenames
61/// (e.g. `Dockerfile`, `Makefile`) are looked up by name.
62pub 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}