Skip to content

Commit c38a6b5

Browse files
authored
Support repos "without source code" (#1580)
1 parent fe35e62 commit c38a6b5

File tree

10 files changed

+466
-208
lines changed

10 files changed

+466
-208
lines changed

src/info/langs/mod.rs

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
use anyhow::{Context, Result};
21
use language::{Language, LanguageType};
32
use std::collections::HashMap;
43
use std::path::Path;
54
use strum::IntoEnumIterator;
65

76
pub mod language;
87

9-
pub fn get_main_language(loc_by_language: &[(Language, usize)]) -> Language {
10-
loc_by_language[0].0
8+
pub 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)
1110
}
1211

1312
/// Returns a vector of tuples containing all the languages detected inside the repository.
@@ -18,21 +17,10 @@ pub fn get_loc_by_language_sorted(
1817
globs_to_exclude: &[String],
1918
language_types: &[LanguageType],
2019
include_hidden: bool,
21-
) -> Result<Vec<(Language, usize)>> {
20+
) -> Option<Vec<(Language, usize)>> {
2221
let locs = get_locs(dir, globs_to_exclude, language_types, include_hidden);
23-
24-
let loc_by_language = get_loc_by_language(&locs).with_context(|| {
25-
format!(
26-
"No source code found in the repository at '{}'.\n\
27-
Note: Some language types (prose, data) are excluded by default. \
28-
Consider using the '--type' option to include them.",
29-
dir.display()
30-
)
31-
})?;
32-
33-
let loc_by_language_sorted = sort_by_loc(loc_by_language);
34-
35-
Ok(loc_by_language_sorted)
22+
let loc_by_language_opt = get_loc_by_language(&locs);
23+
loc_by_language_opt.map(sort_by_loc)
3624
}
3725

3826
fn sort_by_loc(map: HashMap<Language, usize>) -> Vec<(Language, usize)> {
@@ -44,7 +32,7 @@ fn sort_by_loc(map: HashMap<Language, usize>) -> Vec<(Language, usize)> {
4432
fn get_loc_by_language(languages: &tokei::Languages) -> Option<HashMap<Language, usize>> {
4533
let mut loc_by_language = HashMap::new();
4634

47-
for (language_name, language) in languages.iter() {
35+
for (language_name, language) in languages {
4836
let loc = language::loc(language_name, language);
4937

5038
if loc == 0 {

src/info/mod.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ mod url;
5353
pub mod utils;
5454
mod version;
5555

56-
#[derive(Serialize)]
56+
#[derive(Serialize, Default)]
5757
#[serde(rename_all = "camelCase")]
5858
pub struct Info {
5959
title: Option<Title>,
@@ -65,7 +65,7 @@ pub struct Info {
6565
#[serde(skip_serializing)]
6666
no_bold: bool,
6767
#[serde(skip_serializing)]
68-
pub dominant_language: Language,
68+
pub dominant_language: Option<Language>,
6969
#[serde(skip_serializing)]
7070
pub ascii_colors: Vec<DynColors>,
7171
}
@@ -158,11 +158,11 @@ pub fn build_info(cli_options: &CliOptions) -> Result<Info> {
158158
let loc_by_language = loc_by_language_sorted_handle
159159
.join()
160160
.ok()
161-
.context("BUG: panic in language statistics thread")??;
162-
let dominant_language = langs::get_main_language(&loc_by_language);
161+
.context("BUG: panic in language statistics thread")?;
162+
let dominant_language = langs::get_main_language(loc_by_language.as_ref());
163163
let ascii_colors = get_ascii_colors(
164+
dominant_language.as_ref(),
164165
cli_options.ascii.ascii_language.as_ref(),
165-
&dominant_language,
166166
&cli_options.ascii.ascii_colors,
167167
true_color,
168168
);
@@ -185,7 +185,7 @@ pub fn build_info(cli_options: &CliOptions) -> Result<Info> {
185185
.version(&repo, manifest.as_ref())?
186186
.created(&git_metrics, iso_time)
187187
.languages(
188-
&loc_by_language,
188+
loc_by_language.as_ref(),
189189
true_color,
190190
number_of_languages_to_display,
191191
&text_colors,
@@ -208,7 +208,7 @@ pub fn build_info(cli_options: &CliOptions) -> Result<Info> {
208208
globs_to_exclude,
209209
number_separator,
210210
)?
211-
.loc(&loc_by_language, number_separator)
211+
.loc(loc_by_language.as_ref(), number_separator)
212212
.size(&repo, number_separator)
213213
.license(&repo_path, manifest.as_ref())?
214214
.build(cli_options, text_colors, dominant_language, ascii_colors))
@@ -318,21 +318,23 @@ impl InfoBuilder {
318318

319319
fn languages(
320320
mut self,
321-
loc_by_language: &[(Language, usize)],
321+
loc_by_language_opt: Option<&Vec<(Language, usize)>>,
322322
true_color: bool,
323323
number_of_languages: usize,
324324
text_colors: &TextColors,
325325
cli_options: &CliOptions,
326326
) -> Self {
327327
if !self.disabled_fields.contains(&InfoType::Languages) {
328-
let languages = LanguagesInfo::new(
329-
loc_by_language,
330-
true_color,
331-
number_of_languages,
332-
text_colors.info,
333-
cli_options.visuals.nerd_fonts,
334-
);
335-
self.info_fields.push(Box::new(languages));
328+
if let Some(loc_by_language) = loc_by_language_opt {
329+
let languages = LanguagesInfo::new(
330+
loc_by_language,
331+
true_color,
332+
number_of_languages,
333+
text_colors.info,
334+
cli_options.visuals.nerd_fonts,
335+
);
336+
self.info_fields.push(Box::new(languages));
337+
}
336338
}
337339
self
338340
}
@@ -429,12 +431,14 @@ impl InfoBuilder {
429431

430432
fn loc(
431433
mut self,
432-
loc_by_language: &[(Language, usize)],
434+
loc_by_language_opt: Option<&Vec<(Language, usize)>>,
433435
number_separator: NumberSeparator,
434436
) -> Self {
435437
if !self.disabled_fields.contains(&InfoType::LinesOfCode) {
436-
let lines_of_code = LocInfo::new(loc_by_language, number_separator);
437-
self.info_fields.push(Box::new(lines_of_code));
438+
if let Some(loc_by_language) = loc_by_language_opt {
439+
let lines_of_code = LocInfo::new(loc_by_language, number_separator);
440+
self.info_fields.push(Box::new(lines_of_code));
441+
}
438442
}
439443
self
440444
}
@@ -443,7 +447,7 @@ impl InfoBuilder {
443447
self,
444448
cli_options: &CliOptions,
445449
text_colors: TextColors,
446-
dominant_language: Language,
450+
dominant_language: Option<Language>,
447451
ascii_colors: Vec<DynColors>,
448452
) -> Info {
449453
Info {

src/main.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use clap::{CommandFactory, Parser};
55
use human_panic::setup_panic;
66
use onefetch::cli::{self, CliOptions};
77
use onefetch::info::build_info;
8-
use onefetch::ui::printer::Printer;
8+
use onefetch::ui::printer::factory::PrinterFactory;
99
use std::io;
1010

1111
fn main() -> Result<()> {
@@ -32,9 +32,11 @@ fn main() -> Result<()> {
3232

3333
let info = build_info(&cli_options)?;
3434

35-
let mut printer = Printer::new(io::BufWriter::new(io::stdout()), info, cli_options)?;
35+
let printer = PrinterFactory::new(info, cli_options)?.create()?;
3636

37-
printer.print()?;
37+
let mut writer = io::BufWriter::new(io::stdout());
38+
39+
printer.print(&mut writer)?;
3840

3941
Ok(())
4042
}

src/ui/mod.rs

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,26 @@ pub mod printer;
55
pub mod text_colors;
66

77
pub fn get_ascii_colors(
8-
ascii_language: Option<&Language>,
9-
dominant_language: &Language,
8+
language_opt: Option<&Language>,
9+
override_language_opt: Option<&Language>,
1010
ascii_colors: &[u8],
1111
true_color: bool,
1212
) -> Vec<DynColors> {
13-
let language = if let Some(ascii_language) = ascii_language {
14-
ascii_language
15-
} else {
16-
dominant_language
13+
let language_colors = match language_opt {
14+
Some(lang) => override_language_opt.unwrap_or(lang).get_colors(true_color),
15+
None => vec![DynColors::Ansi(AnsiColors::White)],
1716
};
17+
if ascii_colors.is_empty() {
18+
return language_colors;
19+
}
1820

19-
let mut language_colors: Vec<DynColors> = language.get_colors(true_color);
21+
let mut colors: Vec<DynColors> = ascii_colors.iter().map(num_to_color).collect();
2022

21-
if ascii_colors.is_empty() {
22-
language_colors
23-
} else {
24-
let mut colors: Vec<DynColors> = ascii_colors.iter().map(num_to_color).collect();
25-
26-
if language_colors.len() > colors.len() {
27-
let mut missing = language_colors.drain(colors.len()..).collect();
28-
colors.append(&mut missing);
29-
}
30-
colors
23+
if language_colors.len() > colors.len() {
24+
colors.extend(language_colors.into_iter().skip(colors.len()));
3125
}
26+
27+
colors
3228
}
3329

3430
pub fn num_to_color(num: &u8) -> DynColors {
@@ -63,9 +59,33 @@ mod test {
6359
assert_eq!(num_to_color(&u8::MAX), DynColors::Ansi(AnsiColors::Default));
6460
}
6561

62+
#[test]
63+
fn get_ascii_colors_no_language_no_custom_language_custom_colors() {
64+
let colors = get_ascii_colors(None, None, &[3, 5, 8], false);
65+
assert_eq!(colors.len(), 3);
66+
assert_eq!(
67+
colors,
68+
vec![num_to_color(&3), num_to_color(&5), num_to_color(&8)]
69+
);
70+
}
71+
72+
#[test]
73+
fn get_ascii_colors_no_language_no_custom_language() {
74+
let colors = get_ascii_colors(None, None, &[], false);
75+
assert_eq!(colors.len(), 1);
76+
assert_eq!(colors, vec![DynColors::Ansi(AnsiColors::White)]);
77+
}
78+
79+
#[test]
80+
fn get_ascii_colors_no_language_with_custom_language() {
81+
let colors = get_ascii_colors(None, Some(&Language::Python), &[], false);
82+
assert_eq!(colors.len(), 1);
83+
assert_eq!(colors, vec![DynColors::Ansi(AnsiColors::White)]);
84+
}
85+
6686
#[test]
6787
fn get_ascii_colors_no_custom_language_no_custom_colors_no_true_color() {
68-
let colors = get_ascii_colors(None.as_ref(), &Language::Rust, &[], false);
88+
let colors = get_ascii_colors(Some(&Language::Rust), None, &[], false);
6989
assert_eq!(colors.len(), 2);
7090
assert_eq!(
7191
colors,
@@ -78,7 +98,7 @@ mod test {
7898

7999
#[test]
80100
fn get_ascii_colors_no_custom_language_no_custom_colors_true_color() {
81-
let colors = get_ascii_colors(None, &Language::Rust, &[], true);
101+
let colors = get_ascii_colors(Some(&Language::Rust), None, &[], true);
82102
assert_eq!(colors.len(), 2);
83103
assert_eq!(
84104
colors,
@@ -88,14 +108,14 @@ mod test {
88108

89109
#[test]
90110
fn get_ascii_colors_custom_language_no_custom_colors_no_true_color() {
91-
let colors = get_ascii_colors(Some(&Language::Sh), &Language::Rust, &[], false);
111+
let colors = get_ascii_colors(Some(&Language::Rust), Some(&Language::Sh), &[], false);
92112
assert_eq!(colors.len(), 1);
93113
assert_eq!(colors, vec![DynColors::Ansi(AnsiColors::Green)]);
94114
}
95115

96116
#[test]
97117
fn get_ascii_colors_no_custom_language_custom_colors_no_true_color() {
98-
let colors = get_ascii_colors(None.as_ref(), &Language::Rust, &[2, 3], false);
118+
let colors = get_ascii_colors(Some(&Language::Rust), None, &[2, 3], false);
99119
assert_eq!(colors.len(), 2);
100120
assert_eq!(colors, vec![num_to_color(&2), num_to_color(&3)]);
101121
}
@@ -104,7 +124,7 @@ mod test {
104124
fn get_ascii_colors_fill_custom_colors_with_language_colors() {
105125
// When custom ascii colors are not enough for the given language,
106126
// language colors should be used as default
107-
let colors = get_ascii_colors(None, &Language::Go, &[0], false);
127+
let colors = get_ascii_colors(Some(&Language::Go), None, &[0], false);
108128
assert_eq!(colors.len(), 3);
109129
assert_eq!(
110130
colors,

0 commit comments

Comments
 (0)