From 76be54b67dee90de193c7950fac8ad70db7618df Mon Sep 17 00:00:00 2001 From: Aaronepower Date: Wed, 1 Jun 2016 15:08:36 +0100 Subject: [PATCH] first attempt at moving stuff around --- Cargo.lock | 38 +++--- Cargo.toml | 4 +- src/language/language.rs | 150 ++++++++++++++++++++++ src/language/language_name.rs | 2 +- src/language/languages.rs | 153 +++++++++++++++++++---- src/language/mod.rs | 159 +----------------------- src/lib.rs | 11 ++ src/lib.rs.in | 18 +-- src/main.rs | 207 ++++++++++++++++++++++++++++++- src/main.rs.in | 226 ---------------------------------- src/utils/consts.rs | 3 - src/utils/fs.rs | 44 +++---- src/utils/mod.rs | 8 +- 13 files changed, 547 insertions(+), 476 deletions(-) create mode 100644 src/language/language.rs delete mode 100644 src/main.rs.in diff --git a/Cargo.lock b/Cargo.lock index c844a62..97a4f79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", diff --git a/Cargo.toml b/Cargo.toml index dc68f60..abc08bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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. diff --git a/src/language/language.rs b/src/language/language.rs new file mode 100644 index 0000000..15abd8b --- /dev/null +++ b/src/language/language.rs @@ -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, + pub stats: Vec, + 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 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); + } +} diff --git a/src/language/language_name.rs b/src/language/language_name.rs index fa642a8..cbaa5d9 100644 --- a/src/language/language_name.rs +++ b/src/language/language_name.rs @@ -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)] diff --git a/src/language/languages.rs b/src/language/languages.rs index 70720d2..bb9fb94 100644 --- a/src/language/languages.rs +++ b/src/language/languages.rs @@ -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> { - 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 { + let mut _self = Self::new(); + let map: Result, 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(paths: P, ignored: P) - where I: Iterator, - P: Into> + 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 + Clone, + C: Into> { - 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, serde_cbor::Error> { + serde_cbor::to_vec(&self.inner) + } + pub fn to_json(&self) -> Result { + serde_json::to_string(&self.inner) + } + pub fn to_yaml(&self) -> Result { + serde_yaml::to_string(&self.inner) + } +} + + +impl IntoIterator for Languages { + type Item = as IntoIterator>::Item; + type IntoIter = 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, } diff --git a/src/language/mod.rs b/src/language/mod.rs index f0a2467..61e69e0 100644 --- a/src/language/mod.rs +++ b/src/language/mod.rs @@ -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, - pub stats: Vec, - 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 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::*; diff --git a/src/lib.rs b/src/lib.rs index ef39e31..b50096d 100644 --- a/src/lib.rs +++ b/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")); diff --git a/src/lib.rs.in b/src/lib.rs.in index 2868570..224fc87 100644 --- a/src/lib.rs.in +++ b/src/lib.rs.in @@ -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; \ No newline at end of file +pub use language::*; diff --git a/src/main.rs b/src/main.rs index 4752f8f..b20f802 100644 --- a/src/main.rs +++ b/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); + } +} diff --git a/src/main.rs.in b/src/main.rs.in deleted file mode 100644 index 2374921..0000000 --- a/src/main.rs.in +++ /dev/null @@ -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"); - } -} diff --git a/src/utils/consts.rs b/src/utils/consts.rs index 07147f0..2d27e61 100644 --- a/src/utils/consts.rs +++ b/src/utils/consts.rs @@ -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"; diff --git a/src/utils/fs.rs b/src/utils/fs.rs index a93a19f..f7021ed 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -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>(paths: I, - ignored_directories: Vec<&str>, - languages: &mut BTreeMap) { +pub fn get_all_files<'a, I>(mut paths: Cow<'a, I>, + mut ignored_directories: Cow<'a, I>, + languages: &mut BTreeMap) + where I: Iterator + 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>(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>(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> { - 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::*; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 8c21c96..d9d3c2f 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -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::*;