first attempt at moving stuff around

This commit is contained in:
Aaronepower 2016-06-01 15:08:36 +01:00
parent 5ecad644f4
commit 76be54b67d
13 changed files with 547 additions and 476 deletions

38
Cargo.lock generated
View File

@ -9,10 +9,10 @@ dependencies = [
"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_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_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)",
"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]]
name = "aster"
version = "0.16.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
@ -133,20 +133,20 @@ dependencies = [
[[package]]
name = "quasi"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "quasi_codegen"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aster 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.32.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.33.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]]
@ -188,14 +188,14 @@ dependencies = [
[[package]]
name = "serde_codegen"
version = "0.7.5"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aster 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_codegen 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.32.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.11.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.33.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]]
@ -223,15 +223,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syntex"
version = "0.32.0"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "syntex_syntax"
version = "0.32.0"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -22,8 +22,8 @@ path = "src/main.rs"
# For building serde in stable.
[build-dependencies]
serde_codegen = "0.7.5"
syntex = "0.32.0"
serde_codegen = "0.7.7"
syntex = "0.33.0"
# Dependencies, and why they are used.
# - Clap: For CLI argument parsing.

150
src/language/language.rs Normal file
View 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);
}
}

View File

@ -1,7 +1,7 @@
use std::fmt;
use std::path::Path;
use utils::fs::*;
use utils::*;
use self::LanguageName::*;
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]

View File

@ -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)]
pub struct Languages {
@ -8,34 +24,65 @@ pub struct Languages {
impl Languages {
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
pub fn from_previous(previous: String) -> Result<Self, LanguageError> {
let mut _self = Self::new();
let map: Result<BTreeMap<LanguageName, Language>, LanguageError> = {
if previous.is_empty() {
Err(SerdeEmpty)
} else if let Ok(result) = serde_json::from_str(&*previous) {
Ok(result)
} else if let Ok(result) = serde_yaml::from_str(&*previous) {
Ok(result)
} else if let Ok(result) = serde_cbor::from_slice(&*previous.from_hex().unwrap()) {
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)
where I: Iterator<Item = &str>,
P: Into<Cow<'a, I>>
pub fn add_previous(&mut self, previous: String) -> Result<(), LanguageError> {
let previous_languages = match Self::from_previous(previous) {
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() {
return;
}
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();
for file in files {
@ -46,12 +93,12 @@ impl Languages {
let contents = {
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
};
let lines = contents.lines();
stats.lines += lines.size_hint().0;
if language.is_blank() {
stats.code += lines.count();
@ -59,8 +106,11 @@ impl Languages {
}
'line: for line in lines {
// 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.
stats.lines += 1;
// 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 {
line
} else {
@ -74,7 +124,10 @@ impl Languages {
for &(multi_line, multi_line_end) in &language.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;
is_in_comments = true;
if language.nested {
@ -86,7 +139,8 @@ impl Languages {
if is_in_comments {
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 {
comment_depth -= 1;
if comment_depth == 0 {
@ -110,12 +164,17 @@ impl Languages {
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 {
use super::LanguageName::*;
let map = btreemap! {
ActionScript => Language::new_c(),
Assembly => Language::new_single(vec![";"]),
@ -194,4 +253,48 @@ impl Languages {
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,
}

View File

@ -2,157 +2,10 @@
// Use of this source code is governed by the MIT/APACHE2.0 license that can be
// found in the LICENCE-{APACHE - MIT} file.
mod language_name;
pub mod language;
pub mod languages;
pub mod language_name;
pub use language_name::*;
use std::path::PathBuf;
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);
}
}
pub use self::languages::Languages;
pub use self::language::LanguageStatistics;
pub use self::language_name::*;

View File

@ -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"));

View File

@ -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]
mod utils;
mod language;
mod stats;
pub mod language;
pub mod stats;
pub use language::Languages;
pub use language::*;

View File

@ -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);
}
}

View File

@ -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");
}
}

View File

@ -1,7 +1,4 @@
pub const CLEAR: &'static str = " \r";
pub const ROW: &'static str = "-------------------------------------------------------------------------------";
pub const BLANKS: &'static str = "blanks";
pub const COMMENTS: &'static str = "comments";
pub const CODE: &'static str = "code";
pub const FILES: &'static str = "files";
pub const TOTAL: &'static str = "total";

View File

@ -2,16 +2,13 @@
// Use of this source code is governed by the APACHE2.0/MIT licence that can be
// found in the LICENCE-{APACHE/MIT} file.
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::path::Path;
use glob::glob;
use rustc_serialize::hex::FromHex;
use serde_cbor;
use serde_json;
use serde_yaml;
use walkdir::{WalkDir, WalkDirIterator};
use language::{Language, LanguageName};
@ -52,17 +49,26 @@ pub fn has_trailing_comments(line: &str,
in_comments != 0
}
pub fn get_all_files<'a, I: Iterator<Item = &'a str>>(paths: I,
ignored_directories: Vec<&str>,
languages: &mut BTreeMap<LanguageName,
Language>) {
pub fn get_all_files<'a, I>(mut paths: Cow<'a, I>,
mut ignored_directories: Cow<'a, I>,
languages: &mut BTreeMap<LanguageName, 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 {
// 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()
if let Err(_) = Path::new(path).metadata() {
if let Ok(paths) = glob(path) {
for path in paths {
'path: for path in paths {
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") {
languages.get_mut(&Makefile).unwrap()
} else {
@ -74,7 +80,7 @@ pub fn get_all_files<'a, I: Iterator<Item = &'a str>>(paths: I,
}
} else {
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) {
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
/// 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)]
#[cfg(test)]
mod tests {
use super::*;

View File

@ -1,7 +1,7 @@
mod consts;
mod fs;
#[macro_use]
mod macros;
pub mod consts;
pub mod fs;
pub use consts::*;
pub use fs::*;
pub use self::consts::*;
pub use self::fs::*;