diff --git a/Cargo.lock b/Cargo.lock index ffc1db0..a0524fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,12 +61,30 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bstr" version = "1.11.3" @@ -82,9 +100,12 @@ name = "cargo-warloc" version = "0.1.0" dependencies = [ "clap", + "csv", "ignore", + "serde", + "serde_json", + "serde_yaml_bw", "utf8-chars", - "walkdir", ] [[package]] @@ -158,6 +179,33 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "globset" version = "0.4.15" @@ -171,6 +219,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + [[package]] name = "heck" version = "0.5.0" @@ -193,12 +247,28 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + [[package]] name = "log" version = "0.4.25" @@ -211,6 +281,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.20.3" @@ -235,6 +314,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + [[package]] name = "regex-automata" version = "0.4.9" @@ -252,6 +343,12 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "same-file" version = "1.0.6" @@ -261,26 +358,83 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "saphyr-parser-bw" +version = "0.0.611" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dec0c833db75dc98957956b303fe447ffc5eb13f2325ef4c2350f7f3aa69e3" +dependencies = [ + "arraydeque", + "smallvec", + "thiserror", +] + [[package]] name = "serde" -version = "1.0.217" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_yaml_bw" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eef5e25868f1a82b8be2395c14a744924ce5f6d2154a746dbfe2fbc22a8b5033" +dependencies = [ + "base64", + "indexmap", + "itoa", + "num-traits", + "regex", + "saphyr-parser-bw", + "serde", + "unsafe-libyaml-norway", + "zmij", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "strsim" version = "0.11.1" @@ -298,12 +452,38 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +[[package]] +name = "unsafe-libyaml-norway" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39abd59bf32521c7f2301b52d05a6a2c975b6003521cbd0c6dc1582f0a22104" + [[package]] name = "utf8-chars" version = "3.0.5" @@ -410,3 +590,9 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 604f464..55dc36b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,9 @@ categories = ["development-tools", "development-tools::cargo-plugins"] [dependencies] clap = { version = "4.5", features = ["derive"] } +csv = "1.4.0" ignore = "0.4.23" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.149" +serde_yaml_bw = "2.5.5" utf8-chars = "3.0.5" -walkdir = "2.5.0" diff --git a/README.md b/README.md index af3cad5..79ddc19 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,19 @@ about our project documentation being bigger than the code itself. * Understands (to a limit) Rust syntax, so is more accurate in its counts then most generic LOC counters. * Does not count ignored (by `.gitignore`) files. * Can optionally give you stats file-by-file. +* Output stats in tables, CSV, JSON, or YAML. + +Here is the output for the `cargo` repository: -Here is the output for `cargo` repository: ``` File count: 1188 -Type | Code | Blank | Doc comments | Comments | Total +Type | Code | Blank | Doc comments | Comments | Total -------------|--------------|--------------|--------------|--------------|------------- -Main | 82530 | 9682 | 12625 | 6220 | 111057 -Tests | 144421 | 20538 | 588 | 10151 | 175698 -Examples | 169 | 27 | 5 | 19 | 220 +Main | 82530 | 9682 | 12625 | 6220 | 111057 +Tests | 144421 | 20538 | 588 | 10151 | 175698 +Examples | 169 | 27 | 5 | 19 | 220 -------------|--------------|--------------|--------------|--------------|------------- - | 227120 | 30247 | 13218 | 16390 | 286975 + | 227120 | 30247 | 13218 | 16390 | 286975 ``` # Installation @@ -34,7 +36,7 @@ cargo install cargo-warloc # Usage ```shell -cargo warloc +cargo warloc [--by-file] [-o tabular|csv|json|yaml] ``` # Contributing diff --git a/src/cli.rs b/src/cli.rs index 03eadea..3b821db 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,6 @@ -use clap::Parser; +use std::fmt::Display; + +use clap::{Parser, ValueEnum}; /// Wise analysis of Rust lines of code /// @@ -14,6 +16,31 @@ pub struct Cli { /// If set, will print out stats for each file separately #[arg(long)] pub by_file: bool, + /// Output format to print to standard output + #[arg(short, long, default_value_t = OutputFormat::Tabular)] + pub output_format: OutputFormat, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, ValueEnum)] +#[value(rename_all = "lower")] +pub enum OutputFormat { + #[default] + Tabular, + Json, + Csv, + Yaml, +} + +impl Display for OutputFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::Tabular => "tabular", + Self::Yaml => "yaml", + Self::Csv => "csv", + Self::Json => "json", + }; + f.write_str(s) + } } #[derive(Parser)] diff --git a/src/main.rs b/src/main.rs index 0462d41..9a37464 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,12 @@ mod cli; +mod output; mod visitor; mod warlocs; -use std::path::{Path, PathBuf}; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, +}; use clap::Parser; use cli::{CargoCli, Cli}; @@ -10,82 +14,28 @@ use ignore::Walk; use visitor::Visitor; use warlocs::Warlocs; +use crate::output::{output_multiple_file_stats, output_total_stats}; + fn main() { let CargoCli::Command(args) = CargoCli::parse(); - let mut stats = Warlocs::default(); + let root_dir = PathBuf::from("."); - for rust_file in enumerate_rust_files(".") { - let file_stats = calculate_file_stats(&rust_file, &args); - stats += file_stats; - } - - println!("File count: {}", stats.file_count); - pretty_print_stats(&stats); -} - -fn calculate_file_stats(file_path: impl AsRef, args: &Cli) -> Warlocs { - let stats = Visitor::new(&file_path, args.debug).visit_file(); + let files_stats: BTreeMap = enumerate_rust_files(&root_dir) + .map(|p| (p.clone(), calculate_file_stats(&p, &args))) + .collect(); if args.by_file { - println!("File name: {}", file_path.as_ref().to_str().unwrap()); - pretty_print_stats(&stats); - println!(); + output_multiple_file_stats(&args.output_format, files_stats); + } else { + let file_count = files_stats.len(); + let total_stats = files_stats.into_values().sum(); + output_total_stats(file_count as u64, &total_stats, &args.output_format); } - - stats } -fn pretty_print_stats(stats: &Warlocs) { - println!( - "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", - "Type", "Code", "Blank", "Doc comments", "Comments", "Total", - ); - println!( - "{0:-<12}-|-{1:-<12}-|-{2:-<12}-|-{3:-<12}-|-{4:-<12}-|-{5:-<12}", - "", "", "", "", "", "", - ); - - println!( - "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", - "Main", - stats.main.code, - stats.main.whitespaces, - stats.main.docs, - stats.main.comments, - stats.main.sum(), - ); - println!( - "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", - "Tests", - stats.tests.code, - stats.tests.whitespaces, - stats.tests.docs, - stats.tests.comments, - stats.tests.sum(), - ); - println!( - "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", - "Examples", - stats.examples.code, - stats.examples.whitespaces, - stats.examples.docs, - stats.examples.comments, - stats.examples.sum(), - ); - println!( - "{0:-<12}-|-{1:-<12}-|-{2:-<12}-|-{3:-<12}-|-{4:-<12}-|-{5:-<12}", - "", "", "", "", "", "", - ); - println!( - "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", - "", - stats.code(), - stats.whitespaces(), - stats.docs(), - stats.comments(), - stats.sum(), - ); +fn calculate_file_stats(file_path: impl AsRef, args: &Cli) -> Warlocs { + Visitor::new(&file_path, args.debug).visit_file() } fn enumerate_rust_files(root: impl AsRef) -> impl Iterator { diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..d37afad --- /dev/null +++ b/src/output.rs @@ -0,0 +1,278 @@ +//! Output routines. + +use std::{collections::BTreeMap, path::PathBuf}; + +use csv::Writer as CsvWriter; +use serde::Serialize; + +use crate::{cli::OutputFormat, warlocs::Warlocs}; + +/// Prints to stdout multiple stats formatted based on the given `output_format`. +pub fn output_multiple_file_stats(output_format: &OutputFormat, stats: BTreeMap) { + match output_format { + OutputFormat::Tabular => { + println!("Total file count: {}", stats.len()); + for (path, warloc) in stats.iter() { + println!("\nFile path : {}", path.to_str().unwrap()); + single_stat_tabular(warloc); + } + } + OutputFormat::Json => { + let multistats = SerializableMultiFileStats::from_file_stats(stats); + println!( + "{}", + serde_json::to_string(&multistats).expect("JSON serialization should work") + ) + } + OutputFormat::Yaml => { + let multistats = SerializableMultiFileStats::from_file_stats(stats); + print!( + "{}", + serde_yaml_bw::to_string(&multistats).expect("YAML serialization should work") + ) + } + OutputFormat::Csv => { + let multistats = SerializableMultiFileStats::from_file_stats(stats); + let mut buff: Vec = Vec::new(); + csv_output::output_csv(&multistats.totals, &multistats.files, &mut buff); + print!( + "{}", + str::from_utf8(&buff).expect("CSV output should be UTF-8") + ) + } + } +} + +/// Prints to stdout the provided total [Warloc] stats formatted to the given `output_format`. +pub fn output_total_stats(file_count: u64, stats: &Warlocs, output_format: &OutputFormat) { + match output_format { + OutputFormat::Tabular => { + println!("Total file count: {file_count}",); + single_stat_tabular(stats) + } + OutputFormat::Json => { + let stats = SerializableTotalStats { file_count, stats }; + println!( + "{}", + serde_json::to_string(&stats).expect("JSON serialization should work") + ) + } + OutputFormat::Yaml => { + let stats = SerializableTotalStats { file_count, stats }; + print!( + "{}", + serde_yaml_bw::to_string(&stats).expect("YAML serialization should work") + ) + } + OutputFormat::Csv => { + let mut buff: Vec = Vec::new(); + let mut writer = CsvWriter::from_writer(&mut buff); + writer + .write_field("File Count") + .expect("Write CSV first row"); + csv_output::write_csv_header_row(&mut writer); + writer + .write_field(file_count.to_string()) + .expect("Write CSV row field"); + csv_output::write_single_csv_value(stats, &mut writer); + drop(writer); + print!( + "{}", + str::from_utf8(&buff).expect("CSV output should be UTF-8") + ) + } + } +} + +/// Prints to stdout a tabular representation for a single [Warlocs] values. +fn single_stat_tabular(stats: &Warlocs) { + println!( + "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", + "Type", "Code", "Blank", "Doc comments", "Comments", "Total", + ); + println!( + "{0:-<12}-|-{1:-<12}-|-{2:-<12}-|-{3:-<12}-|-{4:-<12}-|-{5:-<12}", + "", "", "", "", "", "", + ); + + println!( + "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", + "Main", + stats.main.code, + stats.main.whitespaces, + stats.main.docs, + stats.main.comments, + stats.main.sum(), + ); + println!( + "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", + "Tests", + stats.tests.code, + stats.tests.whitespaces, + stats.tests.docs, + stats.tests.comments, + stats.tests.sum(), + ); + println!( + "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", + "Examples", + stats.examples.code, + stats.examples.whitespaces, + stats.examples.docs, + stats.examples.comments, + stats.examples.sum(), + ); + println!( + "{0:-<12}-|-{1:-<12}-|-{2:-<12}-|-{3:-<12}-|-{4:-<12}-|-{5:-<12}", + "", "", "", "", "", "", + ); + println!( + "{0: <12} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12}", + "", + stats.code(), + stats.whitespaces(), + stats.docs(), + stats.comments(), + stats.sum(), + ); +} + +#[derive(Serialize)] +struct SerializableTotalStats<'a> { + file_count: u64, + #[serde(flatten)] + stats: &'a Warlocs, +} + +/// Simple wrapper struct representing the structure of serialized multi-file output. +#[derive(Serialize)] +struct SerializableMultiFileStats { + file_count: u64, + totals: Warlocs, + files: BTreeMap, +} + +impl SerializableMultiFileStats { + fn from_file_stats(files: BTreeMap) -> Self { + SerializableMultiFileStats { + file_count: files.len() as u64, + totals: files.values().fold(Warlocs::default(), |acc, v| acc + *v), + files, + } + } +} + +/// Module defining CSV output routines. +mod csv_output { + use std::{collections::BTreeMap, io::Write, path::PathBuf}; + + use csv::Writer as CsvWriter; + + use crate::warlocs::Warlocs; + + /// Performs CSV serialization and outputs to the provided [Write]. + pub fn output_csv(totals: &Warlocs, files: &BTreeMap, writer: impl Write) { + let mut csvw = csv::Writer::from_writer(writer); + + csvw.write_field("File").expect("Write CSV columns row"); + write_csv_header_row(&mut csvw); + + for (path, stats) in files.iter() { + csvw.write_field(path.to_str().expect("Filepaths should be UTF-8")) + .expect("Write CSV stats value"); + write_single_csv_value(stats, &mut csvw); + } + + let file_count = files.len(); + csvw.write_field(format!("{file_count} files total")) + .expect("Write CSV totals row"); + write_single_csv_value(totals, &mut csvw); + } + + /// Writes out the column titles row for the CSV output of [Warlocs]. + pub fn write_csv_header_row(writer: &mut CsvWriter) { + let columns = [ + "Main Code", + "Main Docs", + "Main Comments", + "Main Spaces", + "Test Code", + "Test Docs", + "Test Comments", + "Test Spaces", + "Example Code", + "Example Docs", + "Example Comments", + "Example Spaces", + ]; + writer.write_record(columns).expect("Write CSV columns"); + } + + pub fn write_single_csv_value(stats: &Warlocs, writer: &mut CsvWriter) { + let s = stats; + + let row: Vec = [ + s.main.code, + s.main.docs, + s.main.comments, + s.main.whitespaces, + s.tests.code, + s.tests.docs, + s.tests.comments, + s.tests.whitespaces, + s.examples.code, + s.examples.docs, + s.examples.comments, + s.examples.whitespaces, + ] + .iter() + .map(|v| v.to_string()) + .collect(); + + writer.write_record(row).expect("Write CSV row"); + } +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use crate::{ + cli::OutputFormat, + output::{output_multiple_file_stats, output_total_stats}, + warlocs::Warlocs, + }; + + macro_rules! all_output_formats { + () => { + [ + OutputFormat::Csv, + OutputFormat::Json, + OutputFormat::Tabular, + OutputFormat::Yaml, + ] + }; + } + + #[test] + fn test_output_single_no_panics() { + let val = Warlocs::default(); + + for output_format in all_output_formats!() { + output_total_stats(1, &val, &output_format); + } + } + + #[test] + fn test_output_multiple_no_panics() { + for output_format in all_output_formats!() { + let files = [ + (PathBuf::from("abc"), Warlocs::default()), + (PathBuf::from("123"), Warlocs::default()), + ] + .into_iter() + .collect(); + output_multiple_file_stats(&output_format, files); + } + } +} diff --git a/src/visitor.rs b/src/visitor.rs index f2cbd87..0d675d9 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -109,8 +109,6 @@ impl Visitor { impl Visitor { pub fn visit_file(mut self) -> Warlocs { - self.stats.file_count += 1; - self.visit_code(self.context); self.stats @@ -479,7 +477,6 @@ mod tests { let file = "\n"; let stats = stats(file); - assert_eq!(stats.file_count, 1); assert_eq!(stats.main.whitespaces, 1); assert_eq!(stats.main.sum(), 1); } @@ -489,7 +486,6 @@ mod tests { let file = " \t\t \n"; let stats = stats(file); - assert_eq!(stats.file_count, 1); assert_eq!(stats.main.whitespaces, 1); assert_eq!(stats.main.sum(), 1); } @@ -541,10 +537,10 @@ mod tests { #[test] fn multiline_comment_block() { - let file = r#" /* + let file = r#" /* - comment - */ + comment + */ "#; let stats = stats(file); @@ -567,8 +563,8 @@ mod tests { fn multiline_doc_comment_block() { let file = r#" /*! - comment - */ + comment + */ "#; let stats = stats(file); diff --git a/src/warlocs.rs b/src/warlocs.rs index 0e93b72..7761089 100644 --- a/src/warlocs.rs +++ b/src/warlocs.rs @@ -1,14 +1,17 @@ -use std::ops::{Add, AddAssign}; +use serde::Serialize; +use std::{ + iter::Sum, + ops::{Add, AddAssign}, +}; -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, Serialize)] pub struct Warlocs { - pub file_count: u64, pub main: Locs, pub tests: Locs, pub examples: Locs, } -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, Serialize)] pub struct Locs { pub whitespaces: u64, pub code: u64, @@ -44,12 +47,17 @@ impl Locs { } } +impl Sum for Warlocs { + fn sum>(iter: I) -> Self { + iter.fold(Warlocs::default(), Warlocs::add) + } +} + impl Add for Warlocs { type Output = Self; fn add(self, rhs: Warlocs) -> Self::Output { Self { - file_count: self.file_count + rhs.file_count, main: self.main + rhs.main, tests: self.tests + rhs.tests, examples: self.examples + rhs.examples,