Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: number formatted printing #591

Merged
merged 3 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ term_size = "0.3.2"
toml = "0.5.6"
parking_lot = "0.11.0"
dashmap = { version = "3.11.7", features = ["serde"] }
num-format = "0.4.0"
once_cell = "1.4.0"
regex = "1.3.9"
serde_json = "1.0.56"
Expand Down
22 changes: 22 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::mem;
use std::process;

use clap::{clap_app, crate_description, ArgMatches};
use tokei::{Config, LanguageType, Sort};
Expand All @@ -18,6 +19,7 @@ pub struct Cli<'a> {
pub print_languages: bool,
pub sort: Option<Sort>,
pub types: Option<Vec<LanguageType>>,
pub number_format: num_format::CustomFormat,
pub verbose: u64,
}

Expand Down Expand Up @@ -74,6 +76,12 @@ impl<'a> Cli<'a> {
(@arg types: -t --type
+takes_value
"Filters output by language type, seperated by a comma. i.e. -t=Rust,Markdown")
(@arg num_format_style: -n --("num-format")
possible_values(NumberFormatStyle::all())
conflicts_with[output]
+takes_value
"Format of printed numbers, i.e. plain (1234, default), commas (1,234), dots \
(1.234), or underscores (1_234). Cannot be used with --output.")
(@arg verbose: -v --verbose ...
"Set log output level:
1: to show unknown file extensions,
Expand All @@ -97,6 +105,19 @@ impl<'a> Cli<'a> {
.collect()
});

let num_format_style: NumberFormatStyle = matches
.value_of("num_format_style")
.map(parse_or_exit::<NumberFormatStyle>)
.unwrap_or_default();

let number_format = match num_format_style.get_format() {
Ok(format) => format,
Err(e) => {
eprintln!("Error:\n{}", e);
process::exit(1);
}
};

// Sorting category should be restricted by clap but parse before we do
// work just in case.
let sort = matches.value_of("sort").map(parse_or_exit::<Sort>);
Expand All @@ -120,6 +141,7 @@ impl<'a> Cli<'a> {
sort,
types,
verbose,
number_format,
};

debug!("CLI Config: {:#?}", cli);
Expand Down
147 changes: 128 additions & 19 deletions src/cli_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use std::{
};

use clap::crate_version;
use num_format::ToFormattedString;

use crate::input::Format;
use tokei::{CodeStats, Language, LanguageType, Report};
use tokei::{find_char_boundary, CodeStats, Language, LanguageType, Report};

pub const FALLBACK_ROW_LEN: usize = 79;
const NO_LANG_HEADER_ROW_LEN: usize = 67;
Expand Down Expand Up @@ -58,24 +59,89 @@ where
})
}

#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub enum NumberFormatStyle {
// 1234 (Default)
Plain,
NickHackman marked this conversation as resolved.
Show resolved Hide resolved
// 1,234
Commas,
// 1.234
Dots,
NickHackman marked this conversation as resolved.
Show resolved Hide resolved
// 1_234
Underscores,
}

impl Default for NumberFormatStyle {
fn default() -> Self {
Self::Plain
}
}

impl FromStr for NumberFormatStyle {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"plain" => Ok(Self::Plain),
"commas" => Ok(Self::Commas),
"dots" => Ok(Self::Dots),
"underscores" => Ok(Self::Underscores),
_ => Err(format!(
"Expected 'plain', 'commas', 'underscores', or 'dots' for num-format, but got '{}'",
s,
)),
}
}
}

impl NumberFormatStyle {
fn separator(self) -> &'static str {
match self {
Self::Plain => "",
Self::Commas => ",",
Self::Dots => ".",
Self::Underscores => "_",
}
}

pub fn all() -> &'static [&'static str] {
&["commas", "dots", "plain", "underscores"]
}

pub fn get_format(self) -> Result<num_format::CustomFormat, num_format::Error> {
num_format::CustomFormat::builder()
.grouping(num_format::Grouping::Standard)
.separator(self.separator())
.build()
}
}

pub struct Printer<W> {
writer: W,
columns: usize,
path_length: usize,
row: String,
subrow: String,
list_files: bool,
number_format: num_format::CustomFormat,
}

impl<W> Printer<W> {
pub fn new(columns: usize, list_files: bool, writer: W) -> Self {
pub fn new(
columns: usize,
list_files: bool,
writer: W,
number_format: num_format::CustomFormat,
) -> Self {
Self {
columns,
list_files,
path_length: columns - NO_LANG_ROW_LEN_NO_SPACES,
writer,
row: "=".repeat(columns),
subrow: "-".repeat(columns),
number_format,
}
}
}
Expand Down Expand Up @@ -114,11 +180,14 @@ impl<W: Write> Printer<W> {
writeln!(
self.writer,
"{:>6} {:>12} {:>12} {:>12} {:>12}",
language.reports.len(),
language.lines(),
language.code,
language.comments,
language.blanks
language
.reports
.len()
.to_formatted_string(&self.number_format),
language.lines().to_formatted_string(&self.number_format),
language.code.to_formatted_string(&self.number_format),
language.comments.to_formatted_string(&self.number_format),
language.blanks.to_formatted_string(&self.number_format),
)
}

Expand Down Expand Up @@ -171,11 +240,11 @@ impl<W: Write> Printer<W> {
writeln!(
self.writer,
" {:>6} {:>12} {:>12} {:>12} {:>12}",
stats.len(),
code + comments + blanks,
code,
comments,
blanks,
stats.len().to_formatted_string(&self.number_format),
(code + comments + blanks).to_formatted_string(&self.number_format),
code.to_formatted_string(&self.number_format),
comments.to_formatted_string(&self.number_format),
blanks.to_formatted_string(&self.number_format),
)
} else {
Ok(())
Expand All @@ -197,7 +266,7 @@ impl<W: Write> Printer<W> {
subtotal.stats.code += summary.code;
subtotal.stats.comments += summary.comments;
subtotal.stats.blanks += summary.blanks;
writeln!(self.writer, "{:1$}", subtotal, self.path_length)?;
self.print_report_with_name(&subtotal)?;

Ok(())
}
Expand Down Expand Up @@ -291,10 +360,10 @@ impl<W: Write> Printer<W> {
self.writer,
" {:>6} {:>12} {:>12} {:>12} {:>12}",
" ",
stats.lines(),
stats.code,
stats.comments,
stats.blanks,
stats.lines().to_formatted_string(&self.number_format),
stats.code.to_formatted_string(&self.number_format),
stats.comments.to_formatted_string(&self.number_format),
stats.blanks.to_formatted_string(&self.number_format),
)
}

Expand All @@ -308,17 +377,57 @@ impl<W: Write> Printer<W> {
subtotal.stats.comments += report.stats.comments;
subtotal.stats.blanks += report.stats.blanks;

// writeln!(sink, "{}", row)?;
for (language_type, stats) in &report.stats.blobs {
self.print_report(*language_type, stats, inaccurate)?;
subtotal.stats += stats.summarise();
}

writeln!(self.writer, "{:1$}", subtotal, self.path_length)?;
self.print_report_with_name(&report)?;

Ok(())
}

fn print_report_with_name(&mut self, report: &Report) -> io::Result<()> {
let name = report.name.to_string_lossy();
let name_length = name.len();

if name_length <= self.path_length {
self.print_report_total_formatted(name, self.path_length, report)?;
} else {
let mut formatted = String::from("|");
// Add 1 to the index to account for the '|' we add to the output string
let from = find_char_boundary(&name, name_length + 1 - self.path_length);
formatted.push_str(&name[from..]);
self.print_report_total_formatted(name, self.path_length, report)?;
}

Ok(())
}

fn print_report_total_formatted(
&mut self,
name: std::borrow::Cow<'_, str>,
max_len: usize,
report: &Report,
) -> io::Result<()> {
writeln!(
self.writer,
" {: <max$} {:>12} {:>12} {:>12} {:>12}",
name,
report
.stats
.lines()
.to_formatted_string(&self.number_format),
report.stats.code.to_formatted_string(&self.number_format),
report
.stats
.comments
.to_formatted_string(&self.number_format),
report.stats.blanks.to_formatted_string(&self.number_format),
max = max_len
)
}

pub fn print_total(&mut self, languages: tokei::Languages) -> io::Result<()> {
let mut total = Language::new();

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,5 @@ pub use self::{
config::Config,
language::{Language, LanguageType, Languages},
sort::Sort,
stats::{CodeStats, Report},
stats::{find_char_boundary, CodeStats, Report},
};
7 changes: 6 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ fn main() -> Result<(), Box<dyn Error>> {
process::exit(0);
}

let mut printer = Printer::new(columns, cli.files, io::BufWriter::new(io::stdout()));
let mut printer = Printer::new(
columns,
cli.files,
io::BufWriter::new(io::stdout()),
cli.number_format,
);

if languages.iter().any(|(_, lang)| lang.inaccurate) {
printer.print_inaccuracy_warning()?;
Expand Down
3 changes: 2 additions & 1 deletion src/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ impl ops::AddAssign<CodeStats> for Report {
}
}

fn find_char_boundary(s: &str, index: usize) -> usize {
#[doc(hidden)]
pub fn find_char_boundary(s: &str, index: usize) -> usize {
for i in 0..4 {
if s.is_char_boundary(index + i) {
return index + i;
Expand Down