mirror of
https://github.com/XAMPPRocky/tokei
synced 2024-08-28 11:50:31 +00:00
first attempt at moving stuff around
This commit is contained in:
parent
5ecad644f4
commit
76be54b67d
38
Cargo.lock
generated
38
Cargo.lock
generated
|
@ -9,10 +9,10 @@ dependencies = [
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_cbor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_cbor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_codegen 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_codegen 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_yaml 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_yaml 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
"toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -24,10 +24,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aster"
|
name = "aster"
|
||||||
version = "0.16.0"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"syntex_syntax 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -133,20 +133,20 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quasi"
|
name = "quasi"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"syntex_syntax 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quasi_codegen"
|
name = "quasi_codegen"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aster 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex_syntax 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -188,14 +188,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_codegen"
|
name = "serde_codegen"
|
||||||
version = "0.7.5"
|
version = "0.7.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aster 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quasi 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quasi_codegen 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quasi_codegen 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex_syntax 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -223,15 +223,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syntex"
|
name = "syntex"
|
||||||
version = "0.32.0"
|
version = "0.33.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"syntex_syntax 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syntex_syntax"
|
name = "syntex_syntax"
|
||||||
version = "0.32.0"
|
version = "0.33.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -22,8 +22,8 @@ path = "src/main.rs"
|
||||||
|
|
||||||
# For building serde in stable.
|
# For building serde in stable.
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_codegen = "0.7.5"
|
serde_codegen = "0.7.7"
|
||||||
syntex = "0.32.0"
|
syntex = "0.33.0"
|
||||||
|
|
||||||
# Dependencies, and why they are used.
|
# Dependencies, and why they are used.
|
||||||
# - Clap: For CLI argument parsing.
|
# - Clap: For CLI argument parsing.
|
||||||
|
|
150
src/language/language.rs
Normal file
150
src/language/language.rs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::ops::AddAssign;
|
||||||
|
|
||||||
|
use utils::*;
|
||||||
|
use stats::Stats;
|
||||||
|
use super::LanguageName;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
pub struct LanguageStatistics {
|
||||||
|
pub blanks: usize,
|
||||||
|
pub code: usize,
|
||||||
|
pub comments: usize,
|
||||||
|
#[serde(skip_deserializing, skip_serializing)]
|
||||||
|
pub files: Vec<PathBuf>,
|
||||||
|
pub stats: Vec<Stats>,
|
||||||
|
pub lines: usize,
|
||||||
|
#[serde(skip_deserializing, skip_serializing)]
|
||||||
|
pub line_comment: Vec<&'static str>,
|
||||||
|
#[serde(skip_deserializing, skip_serializing)]
|
||||||
|
pub multi_line: Vec<(&'static str, &'static str)>,
|
||||||
|
#[serde(skip_deserializing, skip_serializing)]
|
||||||
|
pub nested: bool,
|
||||||
|
pub total_files: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanguageStatistics {
|
||||||
|
pub fn new(line_comment: Vec<&'static str>,
|
||||||
|
multi_line: Vec<(&'static str, &'static str)>)
|
||||||
|
-> Self {
|
||||||
|
|
||||||
|
LanguageStatistics {
|
||||||
|
line_comment: line_comment,
|
||||||
|
multi_line: multi_line,
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nested(mut self) -> Self {
|
||||||
|
self.nested = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_c() -> Self {
|
||||||
|
LanguageStatistics {
|
||||||
|
line_comment: vec!["//"],
|
||||||
|
multi_line: vec![("/*", "*/")],
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_html() -> Self {
|
||||||
|
LanguageStatistics { multi_line: vec![("<!--", "-->")], ..Self::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_blank() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_func() -> Self {
|
||||||
|
LanguageStatistics { multi_line: vec![("(*", "*)")], ..Self::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_hash() -> Self {
|
||||||
|
Self::new_single(vec!["#"])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_multi(multi_line: Vec<(&'static str, &'static str)>) -> Self {
|
||||||
|
LanguageStatistics { multi_line: multi_line, ..Self::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_pro() -> Self {
|
||||||
|
LanguageStatistics {
|
||||||
|
line_comment: vec!["%"],
|
||||||
|
multi_line: vec![("/*", "*/")],
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_single(line_comment: Vec<&'static str>) -> Self {
|
||||||
|
LanguageStatistics { line_comment: line_comment, ..Self::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.code == 0 && self.comments == 0 && self.blanks == 0 && self.lines == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_blank(&self) -> bool {
|
||||||
|
self.line_comment.is_empty() && self.multi_line.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sort_by(&mut self, category: &str) {
|
||||||
|
match category {
|
||||||
|
BLANKS => self.stats.sort_by(|a, b| b.blanks.cmp(&a.blanks)),
|
||||||
|
COMMENTS => self.stats.sort_by(|a, b| b.comments.cmp(&a.comments)),
|
||||||
|
CODE => self.stats.sort_by(|a, b| b.code.cmp(&a.code)),
|
||||||
|
TOTAL => self.stats.sort_by(|a, b| b.lines.cmp(&a.lines)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print(&self, name: LanguageName) {
|
||||||
|
println!(" {: <18} {: >6} {:>12} {:>12} {:>12} {:>12}",
|
||||||
|
name.name(),
|
||||||
|
self.total_files,
|
||||||
|
self.lines,
|
||||||
|
self.code,
|
||||||
|
self.comments,
|
||||||
|
self.blanks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign for LanguageStatistics {
|
||||||
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
|
self.total_files += rhs.total_files;
|
||||||
|
self.lines += rhs.lines;
|
||||||
|
self.comments += rhs.comments;
|
||||||
|
self.blanks += rhs.blanks;
|
||||||
|
self.code += rhs.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AddAssign<&'a LanguageStatistics> for LanguageStatistics {
|
||||||
|
fn add_assign(&mut self, rhs: &'a Self) {
|
||||||
|
self.total_files += rhs.total_files;
|
||||||
|
self.lines += rhs.lines;
|
||||||
|
self.comments += rhs.comments;
|
||||||
|
self.blanks += rhs.blanks;
|
||||||
|
self.code += rhs.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AddAssign<&'a mut LanguageStatistics> for LanguageStatistics {
|
||||||
|
fn add_assign(&mut self, rhs: &mut Self) {
|
||||||
|
self.total_files += rhs.total_files;
|
||||||
|
self.lines += rhs.lines;
|
||||||
|
self.comments += rhs.comments;
|
||||||
|
self.blanks += rhs.blanks;
|
||||||
|
self.code += rhs.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign<Stats> for LanguageStatistics {
|
||||||
|
fn add_assign(&mut self, rhs: Stats) {
|
||||||
|
self.lines += rhs.lines;
|
||||||
|
self.code += rhs.code;
|
||||||
|
self.comments += rhs.comments;
|
||||||
|
self.blanks += rhs.blanks;
|
||||||
|
self.stats.push(rhs);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use utils::fs::*;
|
use utils::*;
|
||||||
use self::LanguageName::*;
|
use self::LanguageName::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
use super::{Language, LanguageName};
|
use std::borrow::Cow;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::collections::btree_map;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::iter::IntoIterator;
|
||||||
|
|
||||||
|
use serde_cbor;
|
||||||
|
use serde_json;
|
||||||
|
use serde_yaml;
|
||||||
|
use rustc_serialize::hex::FromHex;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
|
use utils::*;
|
||||||
|
use super::{Language, LanguageName};
|
||||||
|
use super::LanguageName::*;
|
||||||
|
use self::LanguageError::*;
|
||||||
|
use stats::Stats;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Languages {
|
pub struct Languages {
|
||||||
|
@ -8,34 +24,65 @@ pub struct Languages {
|
||||||
|
|
||||||
|
|
||||||
impl Languages {
|
impl Languages {
|
||||||
pub fn convert_input(contents: String) -> Option<BTreeMap<String, Language>> {
|
pub fn from_previous(previous: String) -> Result<Self, LanguageError> {
|
||||||
if contents.is_empty() {
|
let mut _self = Self::new();
|
||||||
None
|
let map: Result<BTreeMap<LanguageName, Language>, LanguageError> = {
|
||||||
} else if let Ok(result) = serde_json::from_str(&*contents) {
|
if previous.is_empty() {
|
||||||
Some(result)
|
Err(SerdeEmpty)
|
||||||
} else if let Ok(result) = serde_yaml::from_str(&*contents) {
|
} else if let Ok(result) = serde_json::from_str(&*previous) {
|
||||||
Some(result)
|
Ok(result)
|
||||||
} else if let Ok(result) = serde_cbor::from_slice(&*contents.from_hex().unwrap()) {
|
} else if let Ok(result) = serde_yaml::from_str(&*previous) {
|
||||||
Some(result)
|
Ok(result)
|
||||||
} else {
|
} else if let Ok(result) = serde_cbor::from_slice(&*previous.from_hex().unwrap()) {
|
||||||
None
|
Ok(result)
|
||||||
|
} else {
|
||||||
|
Err(InvalidFormat)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match map {
|
||||||
|
Ok(map) => {
|
||||||
|
for (name, input_language) in map {
|
||||||
|
if let Some(language) = _self.get_mut(&LanguageName::from(name)) {
|
||||||
|
*language += input_language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(_self)
|
||||||
|
}
|
||||||
|
Err(error) => Err(error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_statistics<I, P>(paths: P, ignored: P)
|
pub fn add_previous(&mut self, previous: String) -> Result<(), LanguageError> {
|
||||||
where I: Iterator<Item = &str>,
|
let previous_languages = match Self::from_previous(previous) {
|
||||||
P: Into<Cow<'a, I>>
|
Ok(result) => result,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (key, previous) in previous_languages {
|
||||||
|
if let Some(language) = self.get_mut(&key) {
|
||||||
|
*language += previous;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_statistics<'a, I, C>(&mut self, paths: C, ignored: C)
|
||||||
|
where I: 'a + Iterator<Item = &'a str> + Clone,
|
||||||
|
C: Into<Cow<'a, I>>
|
||||||
{
|
{
|
||||||
|
|
||||||
get_all_files(paths.into(), ignored_directories.into(), &mut self.inner);
|
get_all_files(paths.into(), ignored.into(), &mut self.inner);
|
||||||
|
|
||||||
self.inner.par_iter_mut().for_each(|&mut (name, ref mut language)| {
|
let mut language_iter: Vec<_> = self.inner.iter_mut().collect();
|
||||||
|
|
||||||
|
language_iter.par_iter_mut().for_each(|&mut (name, ref mut language)| {
|
||||||
if language.files.is_empty() {
|
if language.files.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
language.total_files = language.files.len();
|
language.total_files = language.files.len();
|
||||||
let is_fortran = name == FortranModern || name == FortranLegacy;
|
let is_fortran = name == &FortranModern || name == &FortranLegacy;
|
||||||
|
|
||||||
let files: Vec<_> = language.files.drain(..).collect();
|
let files: Vec<_> = language.files.drain(..).collect();
|
||||||
for file in files {
|
for file in files {
|
||||||
|
@ -46,12 +93,12 @@ impl Languages {
|
||||||
|
|
||||||
let contents = {
|
let contents = {
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
let _ = rs_or_cont!(rs_or_cont!(File::open(file)).read_to_string(&mut contents));
|
let _ = rs_or_cont!(rs_or_cont!(File::open(file))
|
||||||
|
.read_to_string(&mut contents));
|
||||||
contents
|
contents
|
||||||
};
|
};
|
||||||
|
|
||||||
let lines = contents.lines();
|
let lines = contents.lines();
|
||||||
stats.lines += lines.size_hint().0;
|
|
||||||
|
|
||||||
if language.is_blank() {
|
if language.is_blank() {
|
||||||
stats.code += lines.count();
|
stats.code += lines.count();
|
||||||
|
@ -59,8 +106,11 @@ impl Languages {
|
||||||
}
|
}
|
||||||
|
|
||||||
'line: for line in lines {
|
'line: for line in lines {
|
||||||
// FORTRAN has a rule where it only counts as a comment if it's the first character
|
stats.lines += 1;
|
||||||
// in the column, so removing starting whitespace could cause a miscount.
|
|
||||||
|
// FORTRAN has a rule where it only counts as a comment if it's the first
|
||||||
|
// character in the column, so removing starting whitespace could cause a
|
||||||
|
// miscount.
|
||||||
let line = if is_fortran {
|
let line = if is_fortran {
|
||||||
line
|
line
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,7 +124,10 @@ impl Languages {
|
||||||
|
|
||||||
for &(multi_line, multi_line_end) in &language.multi_line {
|
for &(multi_line, multi_line_end) in &language.multi_line {
|
||||||
if line.starts_with(multi_line) ||
|
if line.starts_with(multi_line) ||
|
||||||
has_trailing_comments(line, multi_line, multi_line_end, language.nested) {
|
has_trailing_comments(line,
|
||||||
|
multi_line,
|
||||||
|
multi_line_end,
|
||||||
|
language.nested) {
|
||||||
previous_comment_start = multi_line;
|
previous_comment_start = multi_line;
|
||||||
is_in_comments = true;
|
is_in_comments = true;
|
||||||
if language.nested {
|
if language.nested {
|
||||||
|
@ -86,7 +139,8 @@ impl Languages {
|
||||||
|
|
||||||
if is_in_comments {
|
if is_in_comments {
|
||||||
for &(multi_line, multi_line_end) in &language.multi_line {
|
for &(multi_line, multi_line_end) in &language.multi_line {
|
||||||
if multi_line == previous_comment_start && line.contains(multi_line_end) {
|
if multi_line == previous_comment_start &&
|
||||||
|
line.contains(multi_line_end) {
|
||||||
if language.nested {
|
if language.nested {
|
||||||
comment_depth -= 1;
|
comment_depth -= 1;
|
||||||
if comment_depth == 0 {
|
if comment_depth == 0 {
|
||||||
|
@ -110,12 +164,17 @@ impl Languages {
|
||||||
stats.code += 1;
|
stats.code += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*language += stats;
|
**language += stats;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, key: &LanguageName) -> Option<&mut Language> {
|
||||||
|
self.inner.get_mut(key)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
use super::LanguageName::*;
|
||||||
let map = btreemap! {
|
let map = btreemap! {
|
||||||
ActionScript => Language::new_c(),
|
ActionScript => Language::new_c(),
|
||||||
Assembly => Language::new_single(vec![";"]),
|
Assembly => Language::new_single(vec![";"]),
|
||||||
|
@ -194,4 +253,48 @@ impl Languages {
|
||||||
|
|
||||||
Languages { inner: map }
|
Languages { inner: map }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_cbor(&self) -> Result<Vec<u8>, serde_cbor::Error> {
|
||||||
|
serde_cbor::to_vec(&self.inner)
|
||||||
|
}
|
||||||
|
pub fn to_json(&self) -> Result<String, serde_json::Error> {
|
||||||
|
serde_json::to_string(&self.inner)
|
||||||
|
}
|
||||||
|
pub fn to_yaml(&self) -> Result<String, serde_yaml::Error> {
|
||||||
|
serde_yaml::to_string(&self.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl IntoIterator for Languages {
|
||||||
|
type Item = <BTreeMap<LanguageName, Language> as IntoIterator>::Item;
|
||||||
|
type IntoIter = <BTreeMap<LanguageName, Language> as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.inner.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a Languages {
|
||||||
|
type Item = (&'a LanguageName, &'a Language);
|
||||||
|
type IntoIter = btree_map::Iter<'a, LanguageName, Language>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.inner.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a mut Languages {
|
||||||
|
type Item = (&'a LanguageName, &'a mut Language);
|
||||||
|
type IntoIter = btree_map::IterMut<'a, LanguageName, Language>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.inner.iter_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum LanguageError {
|
||||||
|
SerdeEmpty,
|
||||||
|
InvalidFormat,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,157 +2,10 @@
|
||||||
// Use of this source code is governed by the MIT/APACHE2.0 license that can be
|
// Use of this source code is governed by the MIT/APACHE2.0 license that can be
|
||||||
// found in the LICENCE-{APACHE - MIT} file.
|
// found in the LICENCE-{APACHE - MIT} file.
|
||||||
|
|
||||||
mod language_name;
|
pub mod language;
|
||||||
|
pub mod languages;
|
||||||
|
pub mod language_name;
|
||||||
|
|
||||||
pub use language_name::*;
|
pub use self::languages::Languages;
|
||||||
|
pub use self::language::LanguageStatistics;
|
||||||
use std::path::PathBuf;
|
pub use self::language_name::*;
|
||||||
use std::ops::AddAssign;
|
|
||||||
|
|
||||||
use consts::*;
|
|
||||||
use language_name::LanguageName;
|
|
||||||
use stats::Stats;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
|
||||||
pub struct Language {
|
|
||||||
pub blanks: usize,
|
|
||||||
pub code: usize,
|
|
||||||
pub comments: usize,
|
|
||||||
#[serde(skip_deserializing, skip_serializing)]
|
|
||||||
pub files: Vec<PathBuf>,
|
|
||||||
pub stats: Vec<Stats>,
|
|
||||||
pub lines: usize,
|
|
||||||
#[serde(skip_deserializing, skip_serializing)]
|
|
||||||
pub line_comment: Vec<&'static str>,
|
|
||||||
#[serde(skip_deserializing, skip_serializing)]
|
|
||||||
pub multi_line: Vec<(&'static str, &'static str)>,
|
|
||||||
#[serde(skip_deserializing, skip_serializing)]
|
|
||||||
pub nested: bool,
|
|
||||||
pub total_files: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Language {
|
|
||||||
pub fn new(line_comment: Vec<&'static str>,
|
|
||||||
multi_line: Vec<(&'static str, &'static str)>)
|
|
||||||
-> Self {
|
|
||||||
|
|
||||||
Language {
|
|
||||||
line_comment: line_comment,
|
|
||||||
multi_line: multi_line,
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nested(mut self) -> Self {
|
|
||||||
self.nested = true;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_c() -> Self {
|
|
||||||
Language {
|
|
||||||
line_comment: vec!["//"],
|
|
||||||
multi_line: vec![("/*", "*/")],
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_html() -> Self {
|
|
||||||
Language { multi_line: vec![("<!--", "-->")], ..Self::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_blank() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_func() -> Self {
|
|
||||||
Language { multi_line: vec![("(*", "*)")], ..Self::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_hash() -> Self {
|
|
||||||
Self::new_single(vec!["#"])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_multi(multi_line: Vec<(&'static str, &'static str)>) -> Self {
|
|
||||||
Language { multi_line: multi_line, ..Self::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_pro() -> Self {
|
|
||||||
Language {
|
|
||||||
line_comment: vec!["%"],
|
|
||||||
multi_line: vec![("/*", "*/")],
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_single(line_comment: Vec<&'static str>) -> Self {
|
|
||||||
Language { line_comment: line_comment, ..Self::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.code == 0 && self.comments == 0 && self.blanks == 0 && self.lines == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_blank(&self) -> bool {
|
|
||||||
self.line_comment.is_empty() && self.multi_line.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sort_by(&mut self, category: &str) {
|
|
||||||
match category {
|
|
||||||
BLANKS => self.stats.sort_by(|a, b| b.blanks.cmp(&a.blanks)),
|
|
||||||
COMMENTS => self.stats.sort_by(|a, b| b.comments.cmp(&a.comments)),
|
|
||||||
CODE => self.stats.sort_by(|a, b| b.code.cmp(&a.code)),
|
|
||||||
TOTAL => self.stats.sort_by(|a, b| b.lines.cmp(&a.lines)),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print(&self, name: LanguageName) {
|
|
||||||
println!(" {: <18} {: >6} {:>12} {:>12} {:>12} {:>12}",
|
|
||||||
name.name(),
|
|
||||||
self.total_files,
|
|
||||||
self.lines,
|
|
||||||
self.code,
|
|
||||||
self.comments,
|
|
||||||
self.blanks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign for Language {
|
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
|
||||||
self.total_files += rhs.total_files;
|
|
||||||
self.lines += rhs.lines;
|
|
||||||
self.comments += rhs.comments;
|
|
||||||
self.blanks += rhs.blanks;
|
|
||||||
self.code += rhs.code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AddAssign<&'a Language> for Language {
|
|
||||||
fn add_assign(&mut self, rhs: &'a Self) {
|
|
||||||
self.total_files += rhs.total_files;
|
|
||||||
self.lines += rhs.lines;
|
|
||||||
self.comments += rhs.comments;
|
|
||||||
self.blanks += rhs.blanks;
|
|
||||||
self.code += rhs.code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AddAssign<&'a mut Language> for Language {
|
|
||||||
fn add_assign(&mut self, rhs: &mut Self) {
|
|
||||||
self.total_files += rhs.total_files;
|
|
||||||
self.lines += rhs.lines;
|
|
||||||
self.comments += rhs.comments;
|
|
||||||
self.blanks += rhs.blanks;
|
|
||||||
self.code += rhs.code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign<Stats> for Language {
|
|
||||||
fn add_assign(&mut self, rhs: Stats) {
|
|
||||||
self.lines += rhs.lines;
|
|
||||||
self.code += rhs.code;
|
|
||||||
self.comments += rhs.comments;
|
|
||||||
self.blanks += rhs.blanks;
|
|
||||||
self.stats.push(rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -1,2 +1,13 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate maplit;
|
||||||
|
extern crate glob;
|
||||||
|
extern crate rayon;
|
||||||
|
extern crate rustc_serialize;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate serde_cbor;
|
||||||
|
extern crate serde_json;
|
||||||
|
extern crate serde_yaml;
|
||||||
|
extern crate toml;
|
||||||
|
extern crate walkdir;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||||
|
|
|
@ -1,18 +1,6 @@
|
||||||
extern crate glob;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate maplit;
|
|
||||||
extern crate rayon;
|
|
||||||
extern crate rustc_serialize;
|
|
||||||
extern crate serde;
|
|
||||||
extern crate serde_cbor;
|
|
||||||
extern crate serde_json;
|
|
||||||
extern crate serde_yaml;
|
|
||||||
extern crate toml;
|
|
||||||
extern crate walkdir;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod utils;
|
mod utils;
|
||||||
mod language;
|
pub mod language;
|
||||||
mod stats;
|
pub mod stats;
|
||||||
|
|
||||||
pub use language::Languages;
|
pub use language::*;
|
||||||
|
|
207
src/main.rs
207
src/main.rs
|
@ -1,3 +1,208 @@
|
||||||
|
// Copyright (c) 2015 Aaron Power
|
||||||
|
// Use of this source code is governed by the MIT/APACHE2.0 license that can be
|
||||||
|
// found in the LICENCE-{APACHE, MIT} file.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
extern crate tokei;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/main.rs"));
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
|
use clap::App;
|
||||||
|
|
||||||
|
use tokei::language::{Language, Languages};
|
||||||
|
|
||||||
|
pub const ROW: &'static str = "-------------------------------------------------------------------------------";
|
||||||
|
pub const FILES: &'static str = "files";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Get options at the beginning, so the program doesn't have to make any extra calls to get the
|
||||||
|
// information, and there isn't any magic strings.
|
||||||
|
let yaml = load_yaml!("../cli.yml");
|
||||||
|
let matches = App::from_yaml(yaml).get_matches();
|
||||||
|
let files_option = matches.is_present(FILES);
|
||||||
|
let input_option = matches.value_of("file_input");
|
||||||
|
let output_option = matches.value_of("output");
|
||||||
|
let language_option = matches.is_present("languages");
|
||||||
|
let sort_option = matches.value_of("sort");
|
||||||
|
let paths = matches.values_of("input").unwrap();
|
||||||
|
let ignored_directories = {
|
||||||
|
let mut ignored_directories = vec![".git"];
|
||||||
|
if let Some(user_ignored) = matches.values_of("exclude") {
|
||||||
|
for ignored in user_ignored {
|
||||||
|
ignored_directories.push(ignored);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ignored_directories
|
||||||
|
};
|
||||||
|
|
||||||
|
let languages = Languages::new();
|
||||||
|
|
||||||
|
if language_option {
|
||||||
|
for key in languages.keys() {
|
||||||
|
println!("{:<25}", key);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(input) = input_option {
|
||||||
|
match File::open(input) {
|
||||||
|
Ok(mut file) => {
|
||||||
|
let contents = {
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents).unwrap();
|
||||||
|
contents
|
||||||
|
};
|
||||||
|
|
||||||
|
language.add_previous(contents)
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
if input == "stdin" {
|
||||||
|
let mut stdin = std::io::stdin();
|
||||||
|
let mut buffer = String::new();
|
||||||
|
|
||||||
|
let _ = stdin.read_to_string(&mut buffer);
|
||||||
|
language.add_previous(buffer)
|
||||||
|
} else {
|
||||||
|
language.add_previous(String::from(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if output_option == None {
|
||||||
|
println!("{}", ROW);
|
||||||
|
println!(" {:<12} {:>12} {:>12} {:>12} {:>12} {:>12}",
|
||||||
|
"Language",
|
||||||
|
"Files",
|
||||||
|
"Lines",
|
||||||
|
"Code",
|
||||||
|
"Comments",
|
||||||
|
"Blanks");
|
||||||
|
println!("{}", ROW);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut total = LanguageStatistics::new_blank();
|
||||||
|
|
||||||
|
let print_animation = output_option == None;
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
let child = thread::spawn(move || {
|
||||||
|
loop {
|
||||||
|
if let Ok(_) = rx.try_recv() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if print_animation {
|
||||||
|
print!(" Counting files. \r");
|
||||||
|
thread::sleep(Duration::from_millis(4));
|
||||||
|
print!(" Counting files..\r");
|
||||||
|
thread::sleep(Duration::from_millis(4));
|
||||||
|
print!(" Counting files...\r");
|
||||||
|
thread::sleep(Duration::from_millis(4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
languages.get_statistics(paths, ignored_directories);
|
||||||
|
|
||||||
|
if output_option == None {
|
||||||
|
print!("{}", CLEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
for language in &languages {
|
||||||
|
if !language.is_empty() {
|
||||||
|
if sort_option == None && output_option == None {
|
||||||
|
if files_option {
|
||||||
|
language.print(name);
|
||||||
|
println!("{}", ROW);
|
||||||
|
|
||||||
|
for stat in &language.stats {
|
||||||
|
println!("{}", stat);
|
||||||
|
}
|
||||||
|
println!("{}", ROW);
|
||||||
|
} else if output_option == None {
|
||||||
|
language.print(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = tx.send(());
|
||||||
|
let _ = child.join();
|
||||||
|
|
||||||
|
for &(_, ref language) in &languages {
|
||||||
|
if !language.is_empty() {
|
||||||
|
total += language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(format) = output_option {
|
||||||
|
match &*format {
|
||||||
|
"cbor" => {
|
||||||
|
let cbor: Vec<_> = languages.to_cbor().unwrap();
|
||||||
|
|
||||||
|
for byte in cbor {
|
||||||
|
print!("{:02x}", byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"json" => print!("{}", languages.to_json().unwrap()),
|
||||||
|
// "toml" => print!("{}", {
|
||||||
|
// let encoder = toml::Encoder::new();
|
||||||
|
// lang_map.encode(&mut encoder).unwrap();
|
||||||
|
// encoder.toml
|
||||||
|
// }),
|
||||||
|
"yaml" => print!("{}", languages.to_yaml().unwrap()),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
} else if let Some(sort_category) = sort_option {
|
||||||
|
|
||||||
|
for &mut (_, ref mut language) in &mut languages {
|
||||||
|
match &*sort_category {
|
||||||
|
BLANKS => language.sort_by(BLANKS),
|
||||||
|
COMMENTS => language.sort_by(COMMENTS),
|
||||||
|
CODE => language.sort_by(CODE),
|
||||||
|
FILES => {}
|
||||||
|
TOTAL => language.sort_by(TOTAL),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let languages: Vec<_> = languages.into_iter().collect();
|
||||||
|
|
||||||
|
match &*sort_category {
|
||||||
|
BLANKS => languages.sort_by(|a, b| b.1.blanks.cmp(&a.1.blanks)),
|
||||||
|
COMMENTS => languages.sort_by(|a, b| b.1.comments.cmp(&a.1.comments)),
|
||||||
|
CODE => languages.sort_by(|a, b| b.1.code.cmp(&a.1.code)),
|
||||||
|
FILES => languages.sort_by(|a, b| b.1.files.len().cmp(&a.1.files.len())),
|
||||||
|
TOTAL => languages.sort_by(|a, b| b.1.lines.cmp(&a.1.lines)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for (name, language) in languages {
|
||||||
|
if !language.is_empty() {
|
||||||
|
if !files_option {
|
||||||
|
language.print(name);
|
||||||
|
} else {
|
||||||
|
language.print(name);
|
||||||
|
println!("{}", ROW);
|
||||||
|
for file in &language.stats {
|
||||||
|
println!("{}", file);
|
||||||
|
}
|
||||||
|
println!("{}", ROW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if output_option == None {
|
||||||
|
if !files_option {
|
||||||
|
println!("{}", ROW);
|
||||||
|
}
|
||||||
|
total.print(__Total);
|
||||||
|
println!("{}", ROW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
226
src/main.rs.in
226
src/main.rs.in
|
@ -1,226 +0,0 @@
|
||||||
// Copyright (c) 2015 Aaron Power
|
|
||||||
// Use of this source code is governed by the MIT/APACHE2.0 license that can be
|
|
||||||
// found in the LICENCE-{APACHE, MIT} file.
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
extern crate tokei;
|
|
||||||
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
use std::sync::mpsc::channel;
|
|
||||||
|
|
||||||
use clap::App;
|
|
||||||
use rayon::prelude::*;
|
|
||||||
|
|
||||||
use consts::*;
|
|
||||||
use fsutil::*;
|
|
||||||
use language::Language;
|
|
||||||
use language_name::LanguageName;
|
|
||||||
use language_name::LanguageName::*;
|
|
||||||
use stats::Stats;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Get options at the beginning, so the program doesn't have to make any extra calls to get the
|
|
||||||
// information, and there isn't any magic strings.
|
|
||||||
let yaml = load_yaml!("../cli.yml");
|
|
||||||
let matches = App::from_yaml(yaml).get_matches();
|
|
||||||
let files_option = matches.is_present(FILES);
|
|
||||||
let input_option = matches.value_of("file_input");
|
|
||||||
let output_option = matches.value_of("output");
|
|
||||||
let language_option = matches.is_present("languages");
|
|
||||||
let sort_option = matches.value_of("sort");
|
|
||||||
let paths = matches.values_of("input").unwrap();
|
|
||||||
let ignored_directories = {
|
|
||||||
let mut ignored_directories = vec![".git"];
|
|
||||||
if let Some(user_ignored) = matches.values_of("exclude") {
|
|
||||||
for ignored in user_ignored {
|
|
||||||
ignored_directories.push(ignored);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ignored_directories
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if language_option {
|
|
||||||
for key in languages.keys() {
|
|
||||||
println!("{:<25}", key);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(input) = input_option {
|
|
||||||
let deserded_map = match File::open(input) {
|
|
||||||
Ok(mut file) => {
|
|
||||||
let contents = {
|
|
||||||
let mut contents = String::new();
|
|
||||||
file.read_to_string(&mut contents).unwrap();
|
|
||||||
contents
|
|
||||||
};
|
|
||||||
|
|
||||||
convert_input(contents)
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
if input == "stdin" {
|
|
||||||
let mut stdin = std::io::stdin();
|
|
||||||
let mut buffer = String::new();
|
|
||||||
|
|
||||||
let _ = stdin.read_to_string(&mut buffer);
|
|
||||||
convert_input(buffer)
|
|
||||||
} else {
|
|
||||||
convert_input(String::from(input))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(deserded_map) = deserded_map {
|
|
||||||
for (name, input_language) in deserded_map {
|
|
||||||
if let Some(language) = languages.get_mut(&LanguageName::from(name)) {
|
|
||||||
*language += input_language;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("Input provided wasn't a path, or valid CBOR, JSON, or YAML.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if output_option == None {
|
|
||||||
println!("{}", ROW);
|
|
||||||
println!(" {:<12} {:>12} {:>12} {:>12} {:>12} {:>12}",
|
|
||||||
"Language",
|
|
||||||
"Files",
|
|
||||||
"Lines",
|
|
||||||
"Code",
|
|
||||||
"Comments",
|
|
||||||
"Blanks");
|
|
||||||
println!("{}", ROW);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut total = Language::new_blank();
|
|
||||||
|
|
||||||
let print_animation = output_option == None;
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let child = thread::spawn(move || {
|
|
||||||
loop {
|
|
||||||
if let Ok(_) = rx.try_recv() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if print_animation {
|
|
||||||
print!("\x1B[?25l");
|
|
||||||
print!(" Counting files. \r");
|
|
||||||
thread::sleep(Duration::from_millis(4));
|
|
||||||
print!(" Counting files..\r");
|
|
||||||
thread::sleep(Duration::from_millis(4));
|
|
||||||
print!(" Counting files...\r");
|
|
||||||
thread::sleep(Duration::from_millis(4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// if output_option == None {
|
|
||||||
// print!("{}", CLEAR);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if !language.is_empty() {
|
|
||||||
// if sort_option == None && output_option == None {
|
|
||||||
// if files_option {
|
|
||||||
// language.print(name);
|
|
||||||
// println!("{}", ROW);
|
|
||||||
|
|
||||||
// for stat in &language.stats {
|
|
||||||
// println!("{}", stat);
|
|
||||||
// }
|
|
||||||
// println!("{}", ROW);
|
|
||||||
// } else if output_option == None {
|
|
||||||
// language.print(name);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
let _ = tx.send(());
|
|
||||||
let _ = child.join();
|
|
||||||
|
|
||||||
for &(_, ref language) in &languages {
|
|
||||||
if !language.is_empty() {
|
|
||||||
total += language;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(format) = output_option {
|
|
||||||
|
|
||||||
let mut lang_map = BTreeMap::new();
|
|
||||||
|
|
||||||
for (key, value) in languages {
|
|
||||||
if !value.is_empty() {
|
|
||||||
lang_map.insert(format!("{:?}", key), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match &*format {
|
|
||||||
"cbor" => {
|
|
||||||
let cbor: Vec<_> = serde_cbor::to_vec(&lang_map).unwrap();
|
|
||||||
|
|
||||||
for byte in cbor {
|
|
||||||
print!("{:02x}", byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"json" => print!("{}", serde_json::to_string(&lang_map).unwrap()),
|
|
||||||
"toml" => print!("{}", {
|
|
||||||
let encoder = toml::Encoder::new();
|
|
||||||
lang_map.encode(&mut encoder).unwrap();
|
|
||||||
encoder.toml
|
|
||||||
}),
|
|
||||||
"yaml" => print!("{}", serde_yaml::to_string(&lang_map).unwrap()),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
} else if let Some(sort_category) = sort_option {
|
|
||||||
|
|
||||||
for &mut (_, ref mut language) in &mut languages {
|
|
||||||
match &*sort_category {
|
|
||||||
BLANKS => language.sort_by(BLANKS),
|
|
||||||
COMMENTS => language.sort_by(COMMENTS),
|
|
||||||
CODE => language.sort_by(CODE),
|
|
||||||
FILES => {}
|
|
||||||
TOTAL => language.sort_by(TOTAL),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match &*sort_category {
|
|
||||||
BLANKS => languages.sort_by(|a, b| b.1.blanks.cmp(&a.1.blanks)),
|
|
||||||
COMMENTS => languages.sort_by(|a, b| b.1.comments.cmp(&a.1.comments)),
|
|
||||||
CODE => languages.sort_by(|a, b| b.1.code.cmp(&a.1.code)),
|
|
||||||
FILES => languages.sort_by(|a, b| b.1.files.len().cmp(&a.1.files.len())),
|
|
||||||
TOTAL => languages.sort_by(|a, b| b.1.lines.cmp(&a.1.lines)),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
for (name, language) in languages {
|
|
||||||
if !language.is_empty() {
|
|
||||||
if !files_option {
|
|
||||||
language.print(name);
|
|
||||||
} else {
|
|
||||||
language.print(name);
|
|
||||||
println!("{}", ROW);
|
|
||||||
for file in &language.stats {
|
|
||||||
println!("{}", file);
|
|
||||||
}
|
|
||||||
println!("{}", ROW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if output_option == None {
|
|
||||||
if !files_option {
|
|
||||||
println!("{}", ROW);
|
|
||||||
}
|
|
||||||
total.print(__Total);
|
|
||||||
println!("{}", ROW);
|
|
||||||
print!("\x1B[?25h\r");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,4 @@
|
||||||
pub const CLEAR: &'static str = " \r";
|
|
||||||
pub const ROW: &'static str = "-------------------------------------------------------------------------------";
|
|
||||||
pub const BLANKS: &'static str = "blanks";
|
pub const BLANKS: &'static str = "blanks";
|
||||||
pub const COMMENTS: &'static str = "comments";
|
pub const COMMENTS: &'static str = "comments";
|
||||||
pub const CODE: &'static str = "code";
|
pub const CODE: &'static str = "code";
|
||||||
pub const FILES: &'static str = "files";
|
|
||||||
pub const TOTAL: &'static str = "total";
|
pub const TOTAL: &'static str = "total";
|
||||||
|
|
|
@ -2,16 +2,13 @@
|
||||||
// Use of this source code is governed by the APACHE2.0/MIT licence that can be
|
// Use of this source code is governed by the APACHE2.0/MIT licence that can be
|
||||||
// found in the LICENCE-{APACHE/MIT} file.
|
// found in the LICENCE-{APACHE/MIT} file.
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
use rustc_serialize::hex::FromHex;
|
|
||||||
use serde_cbor;
|
|
||||||
use serde_json;
|
|
||||||
use serde_yaml;
|
|
||||||
use walkdir::{WalkDir, WalkDirIterator};
|
use walkdir::{WalkDir, WalkDirIterator};
|
||||||
|
|
||||||
use language::{Language, LanguageName};
|
use language::{Language, LanguageName};
|
||||||
|
@ -52,17 +49,26 @@ pub fn has_trailing_comments(line: &str,
|
||||||
in_comments != 0
|
in_comments != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_files<'a, I: Iterator<Item = &'a str>>(paths: I,
|
pub fn get_all_files<'a, I>(mut paths: Cow<'a, I>,
|
||||||
ignored_directories: Vec<&str>,
|
mut ignored_directories: Cow<'a, I>,
|
||||||
languages: &mut BTreeMap<LanguageName,
|
languages: &mut BTreeMap<LanguageName, Language>)
|
||||||
Language>) {
|
where I: Iterator<Item = &'a str> + Clone + 'a
|
||||||
|
{
|
||||||
|
let paths = paths.to_mut();
|
||||||
|
let ignored_directories = ignored_directories.to_mut();
|
||||||
for path in paths {
|
for path in paths {
|
||||||
// A small metadata check to check if the file actually exists, this is used over calling
|
// A small metadata check to check if the file actually exists, this is used over calling
|
||||||
// File::open because we're going to be passing the path to either glob() or WalkDir::new()
|
// File::open because we're going to be passing the path to either glob() or WalkDir::new()
|
||||||
if let Err(_) = Path::new(path).metadata() {
|
if let Err(_) = Path::new(path).metadata() {
|
||||||
if let Ok(paths) = glob(path) {
|
if let Ok(paths) = glob(path) {
|
||||||
for path in paths {
|
'path: for path in paths {
|
||||||
let path = rs_or_cont!(path);
|
let path = rs_or_cont!(path);
|
||||||
|
|
||||||
|
for ig in ignored_directories.clone() {
|
||||||
|
if opt_or_cont!(path.to_str()).contains(ig) {
|
||||||
|
continue 'path;
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut language = if opt_or_cont!(path.to_str()).contains("Makefile") {
|
let mut language = if opt_or_cont!(path.to_str()).contains("Makefile") {
|
||||||
languages.get_mut(&Makefile).unwrap()
|
languages.get_mut(&Makefile).unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,7 +80,7 @@ pub fn get_all_files<'a, I: Iterator<Item = &'a str>>(paths: I,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let walker = WalkDir::new(path).into_iter().filter_entry(|entry| {
|
let walker = WalkDir::new(path).into_iter().filter_entry(|entry| {
|
||||||
for ig in ignored_directories.to_owned() {
|
for ig in ignored_directories.clone() {
|
||||||
if entry.path().to_str().unwrap().contains(&*ig) {
|
if entry.path().to_str().unwrap().contains(&*ig) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -143,23 +149,7 @@ pub fn get_filetype_from_shebang<P: AsRef<Path>>(file: P) -> Option<&'static str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This originally too a &[u8], but the u8 didn't directly correspond with the hexadecimal u8, so
|
#[cfg(test)]
|
||||||
/// it had to be changed to a String, and add the rustc_serialize dependency.
|
|
||||||
pub fn convert_input(contents: String) -> Option<BTreeMap<String, Language>> {
|
|
||||||
if contents.is_empty() {
|
|
||||||
None
|
|
||||||
} else if let Ok(result) = serde_json::from_str(&*contents) {
|
|
||||||
Some(result)
|
|
||||||
} else if let Ok(result) = serde_yaml::from_str(&*contents) {
|
|
||||||
Some(result)
|
|
||||||
} else if let Ok(result) = serde_cbor::from_slice(&*contents.from_hex().unwrap()) {
|
|
||||||
Some(result)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code, unused_imports)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
mod consts;
|
|
||||||
mod fs;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
pub mod consts;
|
||||||
|
pub mod fs;
|
||||||
|
|
||||||
pub use consts::*;
|
pub use self::consts::*;
|
||||||
pub use fs::*;
|
pub use self::fs::*;
|
||||||
|
|
Loading…
Reference in a new issue