Skip to content

Commit

Permalink
implement "total lines" as a reporter
Browse files Browse the repository at this point in the history
  • Loading branch information
aslilac committed May 11, 2024
1 parent 6d076ef commit 77d9e8f
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 95 deletions.
36 changes: 10 additions & 26 deletions src/options.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
use colored::Colorize;
use std::collections::HashSet;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::exit;
use terminal_size::terminal_size;
use terminal_size::Height;
use terminal_size::Width;

use crate::langs::Language;
use crate::reporters::Reporter;
use crate::reporters::Reporter::*;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Options {
pub root_dir: String,
pub width: usize,
pub root_dir: PathBuf,
pub reporter: Reporter,
pub include_hidden: bool,
pub include_ignored: bool,
Expand All @@ -22,20 +19,12 @@ pub struct Options {
pub head: Option<usize>,
pub excluded: HashSet<Language>,
pub only_include: HashSet<Language>,
pub total_lines_only: bool,
}

impl Default for Options {
fn default() -> Self {
let term_size = terminal_size();
let width = match term_size {
Some((Width(w), Height(_))) => w.into(),
None => 100,
};

Self {
root_dir: ".".to_string(),
width,
root_dir: ".".into(),
reporter: Terminal,
include_hidden: false,
include_ignored: false,
Expand All @@ -44,7 +33,6 @@ impl Default for Options {
head: None,
excluded: Default::default(),
only_include: Default::default(),
total_lines_only: false,
}
}
}
Expand All @@ -66,7 +54,7 @@ where
(arg.len() >= 2 && arg.starts_with('-')) || (arg.len() >= 3 && arg.starts_with("--"));

if !is_flag {
options.root_dir = arg.to_string();
options.root_dir = arg.into();
continue;
}

Expand Down Expand Up @@ -147,13 +135,9 @@ where
);
}
}
"-l" | "-lines" | "--lines" => {
if options.head.is_some() {
println!("{} is incompatible with -t/--top", arg);
exit(64);
}

options.total_lines_only = true;
"-l" | "-lines" | "--lines" | "-total" | "--total" | "-total-lines" | "--total-lines"
| "-totalLines" | "--totalLines" => {
options.reporter = TotalLines;
}
_ => {
println!("unrecognized option: {}", arg);
Expand Down Expand Up @@ -230,7 +214,7 @@ mod tests {
assert_eq!(
["./test"].into_iter().collect::<Options>(),
Options {
root_dir: "./test".to_string(),
root_dir: "./test".into(),
..Default::default()
},
);
Expand All @@ -242,15 +226,15 @@ mod tests {
Options {
excluded: [TypeScript].into(),
head: Some(10),
root_dir: "./test".to_string(),
root_dir: "./test".into(),
..Default::default()
},
);

assert_eq!(
["-l"].into_iter().collect::<Options>(),
Options {
total_lines_only: true,
reporter: TotalLines,
..Default::default()
},
);
Expand Down
5 changes: 4 additions & 1 deletion src/reporters.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::str::FromStr;

pub mod terminal;
pub mod total_lines;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Reporter {
Terminal,
TotalLines,
}

impl FromStr for Reporter {
Expand All @@ -13,13 +15,14 @@ impl FromStr for Reporter {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_ref() {
"terminal" => Ok(Self::Terminal),
"total" | "total_lines" | "total-lines" | "totalLines" => Ok(Self::TotalLines),
_ => Err(()),
}
}
}

impl Reporter {
pub fn help() -> &'static str {
"\"terminal\""
"\"terminal\", \"total-lines\""
}
}
71 changes: 71 additions & 0 deletions src/reporters/terminal.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,81 @@
use anyhow::anyhow;
use colored::Colorize;
use std::fmt;
use std::fmt::Display;
use terminal_size::terminal_size;
use terminal_size::Width;

use crate::langs::Language;
use crate::langs::LanguageInfo;
use crate::langs::LanguageSummary;
use crate::options::Options;

pub struct TerminalReporter;

impl TerminalReporter {
pub fn report(
summaries: Vec<(Language, LanguageSummary)>,
options: Options,
) -> anyhow::Result<()> {
let dir_path = &options.root_dir;
let term_size = terminal_size();
let width = match term_size {
Some((Width(w), _)) => w.into(),
None => 80,
};
let inner_width = width - 2; // we have a padding of 1 character on each side

println!();
for (_, summary) in summaries.iter() {
println!(
" {:width$}",
summary.to_terminal_display(&options),
width = inner_width
)
}

let total_lines = summaries
.iter()
.map(|(_, summary)| summary.lines)
.reduce(|acc, lines| acc + lines)
.ok_or_else(|| anyhow!("no code found in \"{}\"", dir_path.display()))?;

let mut filled = 0;

for (_, stat) in summaries.iter() {
// If there are 0 total lines, then just say everything is 0%.
let percent = (stat.lines * inner_width)
.checked_div(total_lines)
.unwrap_or(0);
if percent == 0 {
continue;
}

// Print padding and such on first fill
if filled == 0 {
println!();
print!(" ");
}
filled += percent;

let lang = LanguageInfo::from(&stat.language);
match lang.color {
Some(color) => print!("{}", color.on_color(&*" ".repeat(percent))),
None => print!("{}", " ".repeat(percent).on_white()),
};
}

// Don't print a bar at all if it'd just all be uncategorized.
if filled != 0 {
print!("{}", " ".repeat(inner_width - filled).on_white());
println!();
println!();
}

Ok(())
}
}

pub struct TerminalLanguageSummary<'a, 'b>(&'a LanguageSummary, &'b Options);

impl<'a, 'b> TerminalLanguageSummary<'a, 'b> {
Expand Down
18 changes: 18 additions & 0 deletions src/reporters/total_lines.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::langs::Language;
use crate::langs::LanguageSummary;
use crate::options::Options;

pub struct TotalLinesReporter;

impl TotalLinesReporter {
pub fn report(summaries: Vec<(Language, LanguageSummary)>, _: Options) -> anyhow::Result<()> {
let total_lines = summaries
.iter()
.map(|(_, summary)| summary.lines)
.sum::<usize>();

println!("{}", total_lines);

Ok(())
}
}
76 changes: 8 additions & 68 deletions src/scan.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
use anyhow::anyhow;
use colored::Colorize;
use std::collections::HashMap;
use std::ffi::OsString;
use std::path::Path;
use std::sync::mpsc::channel;
use std::thread::spawn;

use crate::config::default_ignore_rule;
use crate::fc::FileContent;
use crate::langs::Language;
use crate::langs::LanguageInfo;
use crate::langs::LanguageSummary;
use crate::options::Options;
use crate::reporters::terminal::TerminalReporter;
use crate::reporters::total_lines::TotalLinesReporter;
use crate::reporters::Reporter::*;

pub fn scan(options: Options) -> anyhow::Result<()> {
let mut summaries: HashMap<Language, LanguageSummary> = Default::default();
let dir = &OsString::from(&options.root_dir);
let dir_path = Path::new(dir);
let dir_path = &options.root_dir;

if !dir_path.is_dir() {
return Err(anyhow!("{} is not a directory", dir_path.display()));
Expand Down Expand Up @@ -63,7 +61,7 @@ pub fn scan(options: Options) -> anyhow::Result<()> {
summary.files.push(path);
}

let mut summaries = summaries.iter().collect::<Vec<_>>();
let mut summaries = summaries.into_iter().collect::<Vec<_>>();
summaries.sort_by(|a, b| b.1.lines.cmp(&a.1.lines));

if !options.excluded.is_empty() {
Expand All @@ -78,66 +76,8 @@ pub fn scan(options: Options) -> anyhow::Result<()> {
summaries.truncate(*max);
}

// We wait until here to handle `--lines` because it allows us to still respect
// other options like `--exclude` and `--top`.
if options.total_lines_only {
let total_lines = summaries
.iter()
.map(|(_, summary)| summary.lines)
.sum::<usize>();

println!("{}", total_lines);
return Ok(());
}

let inner_width = options.width - 2; // we have a padding of 1 character on each side

println!();
for (_, summary) in summaries.iter() {
println!(
" {:width$}",
summary.to_terminal_display(&options),
width = inner_width
)
match options.reporter {
Terminal => TerminalReporter::report(summaries, options),
TotalLines => TotalLinesReporter::report(summaries, options),
}

let total_lines = summaries
.iter()
.map(|(_, summary)| summary.lines)
.reduce(|acc, lines| acc + lines)
.ok_or_else(|| anyhow!("no code found in \"{}\"", dir_path.display()))?;

let mut filled = 0;

for (_, stat) in summaries.iter() {
// If there are 0 total lines, then just say everything is 0%.
let percent = (stat.lines * inner_width)
.checked_div(total_lines)
.unwrap_or(0);
if percent == 0 {
continue;
}

// Print padding and such on first fill
if filled == 0 {
println!();
print!(" ");
}
filled += percent;

let lang = LanguageInfo::from(&stat.language);
match lang.color {
Some(color) => print!("{}", color.on_color(&*" ".repeat(percent))),
None => print!("{}", " ".repeat(percent).on_white()),
};
}

// Don't print a bar at all if it'd just all be uncategorized.
if filled != 0 {
print!("{}", " ".repeat(inner_width - filled).on_white());
println!();
println!();
}

Ok(())
}

0 comments on commit 77d9e8f

Please sign in to comment.