onefetch/info/langs/
mod.rs1use language::{Language, LanguageType};
2use std::collections::HashMap;
3use std::path::Path;
4use strum::IntoEnumIterator;
5
6pub mod language;
7
8pub fn get_main_language(loc_by_language_opt: Option<&Vec<(Language, usize)>>) -> Option<Language> {
9 loc_by_language_opt.map(|loc_by_language| loc_by_language[0].0)
10}
11
12pub fn get_loc_by_language_sorted(
16 dir: &Path,
17 globs_to_exclude: &[String],
18 language_types: &[LanguageType],
19 include_hidden: bool,
20) -> Option<Vec<(Language, usize)>> {
21 let locs = get_locs(dir, globs_to_exclude, language_types, include_hidden);
22 let loc_by_language_opt = get_loc_by_language(&locs);
23 loc_by_language_opt.map(sort_by_loc)
24}
25
26fn sort_by_loc(map: HashMap<Language, usize>) -> Vec<(Language, usize)> {
27 let mut vec: Vec<(Language, usize)> = map.into_iter().collect();
28 vec.sort_by(|a, b| b.1.cmp(&a.1));
29 vec
30}
31
32fn get_loc_by_language(languages: &tokei::Languages) -> Option<HashMap<Language, usize>> {
33 let mut loc_by_language = HashMap::new();
34
35 for (language_name, language) in languages {
36 let loc = language::loc(language_name, language);
37
38 if loc == 0 {
39 continue;
40 }
41
42 loc_by_language.insert(Language::from(*language_name), loc);
43 }
44
45 let total_loc: usize = loc_by_language.values().sum();
46 if total_loc == 0 {
47 None
48 } else {
49 Some(loc_by_language)
50 }
51}
52
53pub fn get_total_loc(loc_by_language: &[(Language, usize)]) -> usize {
54 let total_loc: usize = loc_by_language.iter().map(|(_, v)| v).sum();
55 total_loc
56}
57
58fn get_locs(
59 dir: &Path,
60 globs_to_exclude: &[String],
61 language_types: &[LanguageType],
62 include_hidden: bool,
63) -> tokei::Languages {
64 let mut languages = tokei::Languages::new();
65 let filtered_languages = filter_languages_on_type(language_types);
66
67 let tokei_config = tokei::Config {
68 types: Some(filtered_languages),
69 hidden: Some(include_hidden),
70 ..tokei::Config::default()
71 };
72 let ignored: Vec<&str> = globs_to_exclude.iter().map(AsRef::as_ref).collect();
73 languages.get_statistics(&[&dir], &ignored, &tokei_config);
74 languages
75}
76
77fn filter_languages_on_type(types: &[LanguageType]) -> Vec<tokei::LanguageType> {
78 Language::iter()
79 .filter(|language| types.contains(&language.get_type()))
80 .map(std::convert::Into::into)
81 .collect()
82}
83
84#[cfg(test)]
85mod test {
86 use super::*;
87 use tokei;
88
89 #[test]
90 fn get_loc_by_language_counts_md_comments() {
91 let js = tokei::Language {
92 blanks: 25,
93 comments: 50,
94 code: 100,
95 ..Default::default()
96 };
97 let js_type = tokei::LanguageType::JavaScript;
98
99 let md = tokei::Language {
100 blanks: 50,
101 comments: 200,
102 code: 100,
103 ..Default::default()
104 };
105 let md_type = tokei::LanguageType::Markdown;
106
107 let mut languages = tokei::Languages::new();
108 languages.insert(js_type, js);
109 languages.insert(md_type, md);
110
111 let loc_by_language = get_loc_by_language(&languages).unwrap();
112
113 assert_eq!(loc_by_language[&Language::JavaScript], 100);
115 assert_eq!(loc_by_language[&Language::Markdown], 300);
116 }
117
118 #[test]
119 fn deeply_nested_total_loc() {
120 let mut bash_code_stats = tokei::CodeStats::new();
121 bash_code_stats.code = 5;
123 bash_code_stats.blanks = 1;
124 bash_code_stats.comments = 2;
125
126 let mut md_code_stats = tokei::CodeStats::new();
127 md_code_stats.code = 10;
128 md_code_stats.blanks = 2;
129 md_code_stats.comments = 4;
130 md_code_stats
131 .blobs
132 .insert(tokei::LanguageType::Bash, bash_code_stats);
133 let mut md_report = tokei::Report::new("/tmp/file.ipynb".into());
135 md_report.stats = md_code_stats;
136
137 let mut jupyter_notebook = tokei::Language::default();
138 jupyter_notebook
139 .children
140 .insert(tokei::LanguageType::Markdown, vec![md_report]);
141
142 let mut languages = tokei::Languages::new();
143 languages.insert(tokei::LanguageType::Jupyter, jupyter_notebook);
144
145 let loc_by_language = get_loc_by_language(&languages).unwrap();
146
147 assert_eq!(loc_by_language[&Language::Jupyter], 21);
148 }
149
150 #[test]
152 fn get_loc_by_language_should_not_panic_when_children_language_is_not_supported() {
153 let mut stylus_code_stats = tokei::CodeStats::new();
154 stylus_code_stats.code = 10;
155 stylus_code_stats.blanks = 2;
156 stylus_code_stats.comments = 4;
157
158 let mut stylus_report = tokei::Report::new("/tmp/file.vue".into());
159 stylus_report.stats = stylus_code_stats;
160
161 let mut vue = tokei::Language {
162 blanks: 50,
163 comments: 200,
164 code: 100,
165 ..Default::default()
166 };
167
168 vue.children
169 .insert(tokei::LanguageType::Stylus, vec![stylus_report]);
170
171 let mut languages = tokei::Languages::new();
172 languages.insert(tokei::LanguageType::Vue, vue);
173
174 let loc_by_language = get_loc_by_language(&languages).unwrap();
175
176 assert_eq!(loc_by_language[&Language::Vue], 110);
177 }
178
179 #[test]
180 fn test_get_loc_by_language_sorted() {
181 let mut map = HashMap::new();
182 map.insert(Language::Ada, 300);
183 map.insert(Language::Java, 40);
184 map.insert(Language::Rust, 1200);
185 map.insert(Language::Go, 8);
186
187 let sorted_map = sort_by_loc(map);
188
189 let expected_order = vec![
190 (Language::Rust, 1200),
191 (Language::Ada, 300),
192 (Language::Java, 40),
193 (Language::Go, 8),
194 ];
195 let actual_order: Vec<_> = sorted_map.into_iter().collect();
196
197 assert_eq!(expected_order, actual_order);
198 }
199
200 #[test]
201 fn test_get_total_loc() {
202 let loc_by_language = [(Language::JavaScript, 100), (Language::Markdown, 300)];
203 assert_eq!(get_total_loc(&loc_by_language), 400);
204 }
205}