mirror of
https://github.com/XAMPPRocky/tokei
synced 2024-10-05 23:39:28 +00:00
VERSION 1.1, added sorting, added support for 26 languages, replaced getopts with clap
This commit is contained in:
parent
ee045d302c
commit
3bbc39f937
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -1,17 +1,24 @@
|
|||
[root]
|
||||
name = "rusty-cloc"
|
||||
version = "0.1.0"
|
||||
name = "tokei"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"getopts 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.11"
|
||||
name = "ansi_term"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ansi_term 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -20,15 +27,12 @@ version = "0.2.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.1.8"
|
||||
name = "strsim"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.1"
|
||||
name = "yaml-rust"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -1,14 +1,14 @@
|
|||
[package]
|
||||
name = "rusty-cloc"
|
||||
version = "0.1.0"
|
||||
name = "tokei"
|
||||
version = "1.1.0"
|
||||
authors = ["Aaronepower <theaaronepower@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
getopts = "0.2"
|
||||
glob = "*"
|
||||
|
||||
[profile.dev]
|
||||
debug = true
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
||||
[dependencies]
|
||||
clap = {version = "*", features = ["yaml"]}
|
||||
glob = "*"
|
||||
|
|
68
README.md
68
README.md
|
@ -1,11 +1,67 @@
|
|||
# rusty-cloc
|
||||
A CLOC(Count Lines Of Code) program, written in Rust.
|
||||
# Tokei
|
||||
A blazingly fast CLOC(Count Lines Of Code) program, written in Rust.
|
||||
|
||||
|
||||
# Options
|
||||
```
|
||||
Aaron P. <theaaronepower@gmail.com>
|
||||
A quick CLOC (Count Lines Of Code) tool
|
||||
|
||||
`--exclude-dir` exclude one, or more directories from the search.
|
||||
###### Example
|
||||
`rusty-cloc --exclude-dir=node_modules`
|
||||
USAGE:
|
||||
tokei [FLAGS] [OPTIONS] [--] <input>...
|
||||
|
||||
Will ignore everything within a folder named `node_modules`
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-l, --languages prints out supported languages and their extensions
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-e, --exclude <exclude>... Will ignore all files and directories containing the word ie --exclude node_modules
|
||||
-s, --sort <sort> Will sort based on a certain column ie --sort=files will sort by file count.
|
||||
|
||||
ARGS:
|
||||
input... The input file(s)/directory(ies)
|
||||
|
||||
```
|
||||
|
||||
# Supported Languages
|
||||
```
|
||||
ActionScript (as)
|
||||
C (c)
|
||||
ColdFusion CFScript (cfc)
|
||||
ColdFusion (cfm)
|
||||
Clojure (clj)
|
||||
CoffeeScript (coffee)
|
||||
C++ (cpp)
|
||||
C# (cs)
|
||||
CSS (css)
|
||||
D (d)
|
||||
Dart (dart)
|
||||
LISP (el)
|
||||
Go (go)
|
||||
C Header (h)
|
||||
C++ Header (hpp)
|
||||
Haskell (hs)
|
||||
HTML (html)
|
||||
Java (java)
|
||||
JavaScript (js)
|
||||
JSON (json)
|
||||
JSX (jsx)
|
||||
Objective-C (m)
|
||||
Objective-C++ (mm)
|
||||
Pascal (pas)
|
||||
PHP (php)
|
||||
Perl (pl)
|
||||
Python (py)
|
||||
R (r)
|
||||
Ruby (rb)
|
||||
Ruby HTML (rhtml)
|
||||
Rust (rs)
|
||||
Sass (sass)
|
||||
BASH (sh)
|
||||
SQL (sql)
|
||||
Swift (swift)
|
||||
TypeScript (ts)
|
||||
XML (xml)
|
||||
YAML (yml)
|
||||
```
|
||||
|
|
25
cli.yml
Normal file
25
cli.yml
Normal file
|
@ -0,0 +1,25 @@
|
|||
name: Tokei
|
||||
version: 1.1
|
||||
author: Aaron P. <theaaronepower@gmail.com>
|
||||
about: A quick CLOC (Count Lines Of Code) tool
|
||||
args:
|
||||
- exclude:
|
||||
short: e
|
||||
long: exclude
|
||||
multiple: true
|
||||
help: Will ignore all files and directories containing the word ie --exclude node_modules
|
||||
takes_value: true
|
||||
- sort:
|
||||
short: s
|
||||
long: sort
|
||||
takes_value: true
|
||||
help: Will sort based on a certain column ie --sort=files will sort by file count.
|
||||
- input:
|
||||
index: 1
|
||||
multiple: true
|
||||
required: true
|
||||
help: The input file(s)/directory(ies)
|
||||
- languages:
|
||||
short: l
|
||||
long: languages
|
||||
help: prints out supported languages and their extensions
|
|
@ -49,13 +49,16 @@ pub fn get_all_files(path: String, ignored_directories: &Vec<String>) -> Vec<Str
|
|||
|
||||
if let Ok(result) = metadata(&path) {
|
||||
if result.is_dir() {
|
||||
let dir = fs::read_dir(&path).unwrap();
|
||||
let dir = match fs::read_dir(&path) {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("ERROR: {:?}", err),
|
||||
};
|
||||
'file: for entry in dir {
|
||||
let entry = entry.unwrap();
|
||||
let entry = unwrap_rs_cont!(entry);
|
||||
let file_path = entry.path();
|
||||
let file_str = file_path.to_str().unwrap();
|
||||
let file_str = unwrap_opt_cont!(file_path.to_str());
|
||||
let file_string = file_str.to_owned();
|
||||
let path_metadata = metadata(&file_string).unwrap();
|
||||
let path_metadata = unwrap_rs_cont!(metadata(file_str));
|
||||
|
||||
if path_metadata.is_dir() {
|
||||
for ignored_directory in ignored_directories {
|
||||
|
@ -74,8 +77,12 @@ pub fn get_all_files(path: String, ignored_directories: &Vec<String>) -> Vec<Str
|
|||
files.push(path);
|
||||
}
|
||||
} else {
|
||||
for path_buf in glob(&path).unwrap() {
|
||||
let file_path = path_buf.unwrap().as_path().to_str().unwrap().to_owned();
|
||||
let iter = match glob(&path) {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("{:?}", err)
|
||||
};
|
||||
for path_buf in iter {
|
||||
let file_path = unwrap_opt_cont!(unwrap_rs_cont!(path_buf).as_path().to_str()).to_owned();
|
||||
files.push(file_path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,12 @@ pub struct Language<'a> {
|
|||
pub multi_line: &'a str,
|
||||
pub multi_line_end: &'a str,
|
||||
pub files: Vec<String>,
|
||||
pub code: u32,
|
||||
pub comments: u32,
|
||||
pub blanks: u32,
|
||||
pub lines: u32,
|
||||
pub code: usize,
|
||||
pub comments: usize,
|
||||
pub blanks: usize,
|
||||
pub lines: usize,
|
||||
pub total: usize,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl<'a> Language<'a> {
|
||||
|
@ -30,6 +31,71 @@ impl<'a> Language<'a> {
|
|||
blanks: 0,
|
||||
lines: 0,
|
||||
total: 0,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_c(name: &'a str) -> Language<'a> {
|
||||
Language {
|
||||
name: name,
|
||||
line_comment: "//",
|
||||
multi_line: "/*",
|
||||
multi_line_end: "*/",
|
||||
files: Vec::new(),
|
||||
code: 0,
|
||||
comments: 0,
|
||||
blanks: 0,
|
||||
lines: 0,
|
||||
total: 0,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_html(name: &'a str) -> Language<'a> {
|
||||
Language {
|
||||
name: name,
|
||||
line_comment: "<!--",
|
||||
multi_line: "<!--",
|
||||
multi_line_end: "-->",
|
||||
files: Vec::new(),
|
||||
code: 0,
|
||||
comments: 0,
|
||||
blanks: 0,
|
||||
lines: 0,
|
||||
total: 0,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_blank(name: &'a str) -> Language<'a> {
|
||||
Language {
|
||||
name: name,
|
||||
line_comment: "",
|
||||
multi_line: "",
|
||||
multi_line_end: "",
|
||||
files: Vec::new(),
|
||||
code: 0,
|
||||
comments: 0,
|
||||
blanks: 0,
|
||||
lines: 0,
|
||||
total: 0,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_single(name: &'a str, line_comment: &'a str) -> Language<'a> {
|
||||
Language {
|
||||
name: name,
|
||||
line_comment: line_comment,
|
||||
multi_line: "",
|
||||
multi_line_end: "",
|
||||
files: Vec::new(),
|
||||
code: 0,
|
||||
comments: 0,
|
||||
blanks: 0,
|
||||
lines: 0,
|
||||
total: 0,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,13 +106,11 @@ impl<'a> Language<'a> {
|
|||
|
||||
impl<'a> fmt::Display for Language<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut total;
|
||||
|
||||
if self.total == 0 {
|
||||
total = self.files.len()
|
||||
let total = if self.total == 0 {
|
||||
self.files.len()
|
||||
} else {
|
||||
total = self.total;
|
||||
}
|
||||
self.total
|
||||
};
|
||||
write!(f," {: <15} {: >15} {:>15} {:>15} {:>15} {:>15}", self.name, total, self.lines, self.blanks, self.comments, self.code)
|
||||
}
|
||||
}
|
19
src/macros.rs
Normal file
19
src/macros.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
#[inline(always)]
|
||||
macro_rules! unwrap_opt_cont {
|
||||
($option:expr) => {
|
||||
match $option {
|
||||
Some(result) => result,
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
macro_rules! unwrap_rs_cont {
|
||||
($result:expr) => {
|
||||
match $result {
|
||||
Ok(result) => result,
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
}
|
201
src/main.rs
201
src/main.rs
|
@ -1,121 +1,139 @@
|
|||
extern crate getopts;
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub mod language;
|
||||
pub mod fsutil;
|
||||
|
||||
use std::env;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::collections::HashMap;
|
||||
use getopts::Options;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use clap::App;
|
||||
|
||||
use language::Language;
|
||||
use fsutil::{get_all_files, contains_comments};
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mut opts = Options::new();
|
||||
let yaml = load_yaml!("../cli.yml");
|
||||
let matches = App::from_yaml(yaml).get_matches();
|
||||
|
||||
opts.optflag("h", "help", "Print this help menu");
|
||||
opts.optopt("", "exclude-dir",
|
||||
"Example: --exclude-dir=docs",
|
||||
"\tDirectories wanted to be ignored");
|
||||
let mut languages: BTreeMap<&str, Language> = BTreeMap::new();
|
||||
languages.insert("as" , Language::new_c("ActionScript"));
|
||||
languages.insert("c" , Language::new_c("C"));
|
||||
languages.insert("cs" , Language::new_c("C#"));
|
||||
languages.insert("clj" , Language::new_single("Clojure", ";,#,#_"));
|
||||
languages.insert("coffee" , Language::new("CoffeeScript", "#", "###", "###"));
|
||||
languages.insert("cfm" , Language::new("ColdFusion", "<!---", "<!---", "--->"));
|
||||
languages.insert("cfc" , Language::new_c("ColdFusion CFScript"));
|
||||
languages.insert("cpp" , Language::new_c("C++"));
|
||||
languages.insert("css" , Language::new_c("CSS"));
|
||||
languages.insert("d" , Language::new_c("D"));
|
||||
languages.insert("dart" , Language::new_c("Dart"));
|
||||
languages.insert("go" , Language::new_c("Go"));
|
||||
languages.insert("h" , Language::new_c("C Header"));
|
||||
languages.insert("hs" , Language::new_single("Haskell", "--"));
|
||||
languages.insert("hpp" , Language::new_c("C++ Header"));
|
||||
languages.insert("html" , Language::new_html("HTML"));
|
||||
languages.insert("java" , Language::new_c("Java"));
|
||||
languages.insert("js" , Language::new_c("JavaScript"));
|
||||
languages.insert("json" , Language::new_blank("JSON"));
|
||||
languages.insert("jsx" , Language::new_c("JSX"));
|
||||
languages.insert("el" , Language::new("LISP", ";", "#|", "|#"));
|
||||
languages.insert("m" , Language::new_c("Objective-C"));
|
||||
languages.insert("mm" , Language::new_c("Objective-C++"));
|
||||
languages.insert("php" , Language::new("PHP", "#,//","/*","*/"));
|
||||
languages.insert("pas" , Language::new("Pascal", "//,(*","{","}"));
|
||||
languages.insert("pl" , Language::new("Perl", "#","=","=cut"));
|
||||
languages.insert("py" , Language::new("Python", "#","'''","'''"));
|
||||
languages.insert("rs" , Language::new("Rust", "//,///,//!", "/*", "*/"));
|
||||
languages.insert("r" , Language::new("R", "#","",""));
|
||||
languages.insert("rb" , Language::new("Ruby", "#","=begin","=end"));
|
||||
languages.insert("rhtml" , Language::new_html("Ruby HTML"));
|
||||
languages.insert("sass" , Language::new_c("Sass"));
|
||||
languages.insert("sh" , Language::new_single("BASH", "#"));
|
||||
languages.insert("sql" , Language::new("SQL", "--", "/*", "*/"));
|
||||
languages.insert("swift" , Language::new_c("Swift"));
|
||||
languages.insert("ts" , Language::new_c("TypeScript"));
|
||||
languages.insert("xml" , Language::new_html("XML"));
|
||||
languages.insert("yml" , Language::new_single("YAML", "#"));
|
||||
|
||||
if matches.is_present("languages") {
|
||||
for (ext, language) in languages {
|
||||
println!("{:<25} ({})", language.name, ext);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let paths = matches.values_of("input").unwrap();
|
||||
|
||||
let matches = opts.parse(&args[1..]).unwrap();
|
||||
let mut ignored_directories: Vec<String> = Vec::new();
|
||||
ignored_directories.push(".git".to_string());
|
||||
|
||||
if matches.opt_present("h") {
|
||||
let brief = format!("Usage: {} [options] [paths]", args[0].clone());
|
||||
println!("{}", opts.usage(&brief));
|
||||
return;
|
||||
}
|
||||
|
||||
if matches.opt_present("exclude-dir") {
|
||||
let exclude_args = matches.opt_str("exclude-dir").unwrap();
|
||||
let exclude_vec = exclude_args.split(",");
|
||||
|
||||
for excluded in exclude_vec {
|
||||
ignored_directories.push(excluded.to_string());
|
||||
if let Some(user_ignored) = matches.values_of("exclude") {
|
||||
for ignored in user_ignored {
|
||||
ignored_directories.push(ignored.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
if matches.free.is_empty() {
|
||||
println!("ERROR: ");
|
||||
println!("You must provide a file, or folder path as an argument.");
|
||||
return;
|
||||
let mut sort = String::new();
|
||||
if let Some(sort_by) = matches.value_of("sort") {
|
||||
match &*sort_by.to_lowercase() {
|
||||
"files" | "total" | "blanks" | "comments" | "code" => sort.push_str(&*sort_by.to_lowercase()),
|
||||
_ => println!("--sort must be any of the following files, total, blanks, comments, code"),
|
||||
}
|
||||
}
|
||||
let sort_empty = sort.is_empty();
|
||||
|
||||
|
||||
let row = "----------------------------------------------------------------------------------------------------";
|
||||
let row = "--------------------------------------------------------------------------------------------------";
|
||||
|
||||
println!("{}", row);
|
||||
println!(" {:<15} {:>15} {:>15} {:>15} {:>15} {:>15}",
|
||||
"language", "files", "total", "blanks", "comments", "code");
|
||||
"Language", "Files", "Total", "Blanks", "Comments", "Code");
|
||||
println!("{}", row);
|
||||
let mut languages: HashMap<&str, Language> = HashMap::new();
|
||||
languages.insert("cpp" , Language::new("C++", "//","/*","*/"));
|
||||
languages.insert("hpp" , Language::new("C++ Header", "//","/*","*/"));
|
||||
languages.insert("c" , Language::new("C", "//","/*","*/"));
|
||||
languages.insert("h" , Language::new("C Header", "//","/*","*/"));
|
||||
languages.insert("css" , Language::new("CSS", "//","/*","*/"));
|
||||
languages.insert("java" , Language::new("Java", "//","/*","*/"));
|
||||
languages.insert("js" , Language::new("JavaScript", "//","/*","*/"));
|
||||
languages.insert("rs" , Language::new("Rust", "//","/*","*/"));
|
||||
languages.insert("xml" , Language::new("XML", "<!--","<!--","-->"));
|
||||
languages.insert("html" , Language::new("HTML", "<!--","<!--","-->"));
|
||||
languages.insert("py" , Language::new("Python", "#","'''","'''"));
|
||||
languages.insert("rb" , Language::new("Ruby", "#","=begin","=end"));
|
||||
languages.insert("php" , Language::new("PHP", "#,//","/*","*/"));
|
||||
|
||||
for path in matches.free {
|
||||
let files = get_all_files(path, &ignored_directories);
|
||||
for path in paths {
|
||||
let files = get_all_files(path.to_owned(), &ignored_directories);
|
||||
|
||||
for file in files {
|
||||
let extension = match Path::new(&file).extension() {
|
||||
Some(result) => result.to_str().unwrap(),
|
||||
None => continue,
|
||||
};
|
||||
let extension = unwrap_opt_cont!(unwrap_opt_cont!(Path::new(&file).extension()).to_str());
|
||||
|
||||
let mut language = match languages.get_mut(extension) {
|
||||
Some(result) => result,
|
||||
None => continue,
|
||||
};
|
||||
language.files.push(file.to_string());
|
||||
let lowercase: &str = &extension.to_lowercase();
|
||||
|
||||
let mut language = unwrap_opt_cont!(languages.get_mut(lowercase));
|
||||
language.files.push(file.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
let mut total = Language::new("Total", "", "", "");
|
||||
let mut total = Language::new_blank("Total");
|
||||
|
||||
for (_, language) in languages.iter_mut() {
|
||||
for (_, language) in &mut languages {
|
||||
|
||||
for file in language.files.iter() {
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
|
||||
let mut file_ref = match File::open(&file) {
|
||||
Ok(result) => result,
|
||||
_ => continue,
|
||||
};
|
||||
let mut file_ref = unwrap_rs_cont!(File::open(&file));
|
||||
let mut contents = String::new();
|
||||
|
||||
let _ = file_ref.read_to_end(&mut buffer);
|
||||
|
||||
let contents = match String::from_utf8(buffer) {
|
||||
Ok(result) => result,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let _ = unwrap_rs_cont!(file_ref.read_to_string(&mut contents));
|
||||
|
||||
let mut is_in_comments = false;
|
||||
|
||||
for line in contents.lines() {
|
||||
'line: for line in contents.lines() {
|
||||
let line = line.trim();
|
||||
language.lines += 1;
|
||||
|
||||
if line.trim().is_empty() {
|
||||
language.blanks += 1;
|
||||
continue;
|
||||
}
|
||||
if !language.multi_line.is_empty() {
|
||||
if line.starts_with(language.multi_line) {
|
||||
language.comments += 1;
|
||||
is_in_comments = true;
|
||||
} else if contains_comments(line, language.multi_line) {
|
||||
language.code += 1;
|
||||
is_in_comments = true;
|
||||
}
|
||||
}
|
||||
|
||||
if is_in_comments {
|
||||
|
@ -129,16 +147,15 @@ fn main() {
|
|||
for single in single_comments {
|
||||
if line.starts_with(single) {
|
||||
language.comments += 1;
|
||||
} else if line.trim().is_empty() {
|
||||
language.blanks += 1;
|
||||
} else {
|
||||
continue 'line;
|
||||
}
|
||||
}
|
||||
language.code += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !language.is_empty() {
|
||||
|
||||
if !language.is_empty() && sort_empty {
|
||||
println!("{}", language);
|
||||
}
|
||||
|
||||
|
@ -149,6 +166,34 @@ fn main() {
|
|||
total.code += language.code;
|
||||
}
|
||||
|
||||
if !sort_empty {
|
||||
let mut unsorted_vec:Vec<(&&str, &Language)> = languages.iter().collect();
|
||||
match &*sort {
|
||||
"files" => {
|
||||
unsorted_vec.sort_by(|a, b| b.1.files.len().cmp(&a.1.files.len()))
|
||||
},
|
||||
"total" => {
|
||||
unsorted_vec.sort_by(|a, b| b.1.lines.cmp(&a.1.lines))
|
||||
},
|
||||
"blanks" => {
|
||||
unsorted_vec.sort_by(|a, b| b.1.blanks.cmp(&a.1.blanks))
|
||||
},
|
||||
"comments" => {
|
||||
unsorted_vec.sort_by(|a, b| b.1.comments.cmp(&a.1.comments))
|
||||
},
|
||||
"code" => {
|
||||
unsorted_vec.sort_by(|a, b| b.1.code.cmp(&a.1.code))
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
for (_, language) in unsorted_vec {
|
||||
if !language.is_empty() {
|
||||
println!("{}", language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("{}", row);
|
||||
println!("{}", total);
|
||||
println!("{}", row);
|
||||
|
|
Loading…
Reference in a new issue