diff options
author | Aaron Power <Aaronepower@users.noreply.github.com> | 2015-10-11 21:50:55 +0100 |
---|---|---|
committer | Aaron Power <Aaronepower@users.noreply.github.com> | 2015-10-11 21:50:55 +0100 |
commit | c06871b56faa0c3cb4d515dce979b455b256b3ea (patch) | |
tree | a42ab4855f42fd32b37e65dc57f42144b2b82813 | |
parent | 1481e27bd2cfda165be3f1c1f6c4c7252ec00327 (diff) | |
parent | ee4712be1d9a201eb88d5bc9100e0130cb85cbe4 (diff) |
Merge pull request #17 from Aaronepower/RefCell1.2.0
Added support for multiple file extensions
-rw-r--r-- | src/language.rs | 201 | ||||
-rw-r--r-- | src/main.rs | 464 |
2 files changed, 381 insertions, 284 deletions
diff --git a/src/language.rs b/src/language.rs index 70b3441..9c3fedb 100644 --- a/src/language.rs +++ b/src/language.rs @@ -4,118 +4,119 @@ use std::fmt; +#[derive(Debug)] pub struct Language<'a> { - pub name: &'a str, - pub line_comment: &'a str, - pub multi_line: &'a str, - pub multi_line_end: &'a str, - pub files: Vec<String>, - pub code: usize, - pub comments: usize, - pub blanks: usize, - pub lines: usize, - pub total: usize, - pub size: usize, + pub name: &'a str, + pub line_comment: &'a str, + pub multi_line: &'a str, + pub multi_line_end: &'a str, + pub files: Vec<String>, + pub code: usize, + pub comments: usize, + pub blanks: usize, + pub lines: usize, + pub total: usize, + pub printed: bool, } impl<'a> Language<'a> { - pub fn new<'b>(name: &'a str, - line_comment: &'a str, - multi_line: &'a str, - multi_line_end: &'a str) -> Language<'a> { + pub fn new<'b>(name: &'a str, + line_comment: &'a str, + multi_line: &'a str, + multi_line_end: &'a str) -> Language<'a> { - Language { - name: name, - line_comment: line_comment, - multi_line: multi_line, - multi_line_end: multi_line_end, - files: Vec::new(), - code: 0, - comments: 0, - blanks: 0, - lines: 0, - total: 0, - size: 0, - } - } + Language { + name: name, + line_comment: line_comment, + multi_line: multi_line, + multi_line_end: multi_line_end, + files: Vec::new(), + code: 0, + comments: 0, + blanks: 0, + lines: 0, + total: 0, + printed: false, + } + } - pub fn new_c(name: &'a str) -> Language<'a> { - Language { - name: name, - line_comment: "//", - multi_line: "/*", - multi_line_end: "*/", - files: Vec::new(), - code: 0, - comments: 0, - blanks: 0, - lines: 0, - total: 0, - size: 0, - } - } + pub fn new_c(name: &'a str) -> Language<'a> { + Language { + name: name, + line_comment: "//", + multi_line: "/*", + multi_line_end: "*/", + files: Vec::new(), + code: 0, + comments: 0, + blanks: 0, + lines: 0, + total: 0, + printed: false, + } + } - pub fn new_html(name: &'a str) -> Language<'a> { - Language { - name: name, - line_comment: "<!--", - multi_line: "<!--", - multi_line_end: "-->", - files: Vec::new(), - code: 0, - comments: 0, - blanks: 0, - lines: 0, - total: 0, - size: 0, - } - } + pub fn new_html(name: &'a str) -> Language<'a> { + Language { + name: name, + line_comment: "<!--", + multi_line: "<!--", + multi_line_end: "-->", + files: Vec::new(), + code: 0, + comments: 0, + blanks: 0, + lines: 0, + total: 0, + printed: false, + } + } - pub fn new_blank(name: &'a str) -> Language<'a> { - Language { - name: name, - line_comment: "", - multi_line: "", - multi_line_end: "", - files: Vec::new(), - code: 0, - comments: 0, - blanks: 0, - lines: 0, - total: 0, - size: 0, - } - } + pub fn new_blank(name: &'a str) -> Language<'a> { + Language { + name: name, + line_comment: "", + multi_line: "", + multi_line_end: "", + files: Vec::new(), + code: 0, + comments: 0, + blanks: 0, + lines: 0, + total: 0, + printed: false, + } + } - pub fn new_single(name: &'a str, line_comment: &'a str) -> Language<'a> { - Language { - name: name, - line_comment: line_comment, - multi_line: "", - multi_line_end: "", - files: Vec::new(), - code: 0, - comments: 0, - blanks: 0, - lines: 0, - total: 0, - size: 0, - } - } + pub fn new_single(name: &'a str, line_comment: &'a str) -> Language<'a> { + Language { + name: name, + line_comment: line_comment, + multi_line: "", + multi_line_end: "", + files: Vec::new(), + code: 0, + comments: 0, + blanks: 0, + lines: 0, + total: 0, + printed: false, + } + } - pub fn is_empty(&self) -> bool { - self.code == 0 && self.comments == 0 && self.blanks == 0 && self.lines == 0 - } + pub fn is_empty(&self) -> bool { + self.code == 0 && self.comments == 0 && self.blanks == 0 && self.lines == 0 + } } impl<'a> fmt::Display for Language<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let total = if self.total == 0 { - self.files.len() - } else { - self.total - }; - write!(f," {: <15} {: >15} {:>15} {:>15} {:>15} {:>15}", self.name, total, self.lines, self.blanks, self.comments, self.code) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let total = if self.total == 0 { + self.files.len() + } else { + self.total + }; + write!(f," {: <15} {: >15} {:>15} {:>15} {:>15} {:>15}", self.name, total, self.lines, self.blanks, self.comments, self.code) + } } diff --git a/src/main.rs b/src/main.rs index 9f71121..1ddd852 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,8 @@ pub mod macros; pub mod language; pub mod fsutil; +use std::rc::Rc; +use std::cell::RefCell; use std::io::Read; use std::path::Path; use std::fs::File; @@ -23,188 +25,282 @@ use fsutil::{get_all_files, contains_comments}; static ROW: &'static str = "--------------------------------------------------------------------------------------------------"; fn main() { - let yaml = load_yaml!("../cli.yml"); - let matches = App::from_yaml(yaml).get_matches(); - - let mut languages: BTreeMap<&str, Language> = BTreeMap::new(); - languages.insert("as" , Language::new_c("ActionScript")); - languages.insert("c" , Language::new_c("C")); - languages.insert("cs" , Language::new_c("C#")); - languages.insert("clj" , Language::new_single("Clojure", ";,#,#_")); - languages.insert("coffee" , Language::new("CoffeeScript", "#", "###", "###")); - languages.insert("cfm" , Language::new("ColdFusion", "<!---", "<!---", "--->")); - languages.insert("cfc" , Language::new_c("ColdFusion CFScript")); - languages.insert("cpp" , Language::new_c("C++")); - languages.insert("css" , Language::new_c("CSS")); - languages.insert("d" , Language::new_c("D")); - languages.insert("dart" , Language::new_c("Dart")); - languages.insert("el" , Language::new("LISP", ";", "#|", "|#")); - languages.insert("f" , Language::new_single("FORTRAN Legacy", "c,C,!,*")); - languages.insert("f90" , Language::new_single("FORTRAN Modern", "!")); - languages.insert("go" , Language::new_c("Go")); - languages.insert("h" , Language::new_c("C Header")); - languages.insert("hs" , Language::new_single("Haskell", "--")); - languages.insert("hpp" , Language::new_c("C++ Header")); - languages.insert("html" , Language::new_html("HTML")); - languages.insert("java" , Language::new_c("Java")); - languages.insert("js" , Language::new_c("JavaScript")); - languages.insert("jl" , Language::new("Julia", "#", "#=", "=#")); - languages.insert("json" , Language::new_blank("JSON")); - languages.insert("jsx" , Language::new_c("JSX")); - languages.insert("less" , Language::new_c("LESS")); - languages.insert("m" , Language::new_c("Objective-C")); - languages.insert("md" , Language::new_blank("Markdown")); - languages.insert("mm" , Language::new_c("Objective-C++")); - languages.insert("php" , Language::new("PHP", "#,//","/*","*/")); - languages.insert("pas" , Language::new("Pascal", "//,(*","{","}")); - languages.insert("pl" , Language::new("Perl", "#","=","=cut")); - languages.insert("py" , Language::new("Python", "#","'''","'''")); - languages.insert("rs" , Language::new("Rust", "//,///,//!", "/*", "*/")); - languages.insert("r" , Language::new("R", "#","","")); - languages.insert("rb" , Language::new("Ruby", "#","=begin","=end")); - languages.insert("rhtml" , Language::new_html("Ruby HTML")); - languages.insert("sass" , Language::new_c("Sass")); - languages.insert("sh" , Language::new_single("BASH", "#")); - languages.insert("sql" , Language::new("SQL", "--", "/*", "*/")); - languages.insert("swift" , Language::new_c("Swift")); - languages.insert("toml" , Language::new_single("TOML", "#")); - languages.insert("ts" , Language::new_c("TypeScript")); - languages.insert("xml" , Language::new_html("XML")); - languages.insert("yml" , Language::new_single("YAML", "#")); - - if matches.is_present("languages") { - for (ext, language) in languages { - println!("{:<25} ({})", language.name, ext); - } - return; - } - - - let paths = matches.values_of("input").unwrap(); - - let mut ignored_directories: Vec<String> = Vec::new(); - - if let Some(user_ignored) = matches.value_of("exclude") { - for ignored in user_ignored.split(",") { - ignored_directories.push(ignored.to_owned()); - } - } - - let mut sort = String::new(); - if let Some(sort_by) = matches.value_of("sort") { - match &*sort_by.to_lowercase() { - "files" | "total" | "blanks" | "comments" | "code" => sort.push_str(&*sort_by.to_lowercase()), - _ => println!("--sort must be any of the following files, total, blanks, comments, code"), - } - } - let sort_empty = sort.is_empty(); - - println!("{}", ROW); - println!(" {:<15} {:>15} {:>15} {:>15} {:>15} {:>15}", - "Language", "Files", "Total", "Blanks", "Comments", "Code"); - println!("{}", ROW); - for path in paths { - let files = get_all_files(path.to_owned(), &ignored_directories); - - for file in files { - let extension = unwrap_opt_cont!(unwrap_opt_cont!(Path::new(&file).extension()).to_str()); - - let lowercase: &str = &extension.to_lowercase(); - - let mut language = unwrap_opt_cont!(languages.get_mut(lowercase)); - language.files.push(file.to_owned()); - } - } - - let mut total = Language::new_blank("Total"); - - for (_, language) in &mut languages { - - for file in language.files.iter() { - - let mut contents = String::new(); - let is_fortran = language.name.contains("FORTRAN"); - let _ = unwrap_rs_cont!(unwrap_rs_cont!(File::open(&file)).read_to_string(&mut contents)); - - let mut is_in_comments = false; - 'line: for line in contents.lines() { - let line = if is_fortran {line} else {line.trim()}; - language.lines += 1; - - if line.trim().is_empty() { - language.blanks += 1; - continue; - } - if !language.multi_line.is_empty() { - if line.starts_with(language.multi_line) { - is_in_comments = true; - } else if contains_comments(line, language.multi_line) { - language.code += 1; - is_in_comments = true; - } - } - - if is_in_comments { - if line.contains(language.multi_line_end) { - is_in_comments = false; - } - language.comments += 1; - continue; - } - if !language.line_comment.is_empty() { - let single_comments = language.line_comment.split(","); - for single in single_comments { - if line.starts_with(single) { - language.comments += 1; - continue 'line; - } - } - } - language.code += 1; - } - } - - - if !language.is_empty() && sort_empty { - println!("{}", language); - } - - total.total += language.files.len(); - total.lines += language.lines; - total.comments += language.comments; - total.blanks += language.blanks; - total.code += language.code; - } - - if !sort_empty { - let mut unsorted_vec:Vec<(&&str, &Language)> = languages.iter().collect(); - match &*sort { - "files" => { - unsorted_vec.sort_by(|a, b| b.1.files.len().cmp(&a.1.files.len())) - }, - "total" => { - unsorted_vec.sort_by(|a, b| b.1.lines.cmp(&a.1.lines)) - }, - "blanks" => { - unsorted_vec.sort_by(|a, b| b.1.blanks.cmp(&a.1.blanks)) - }, - "comments" => { - unsorted_vec.sort_by(|a, b| b.1.comments.cmp(&a.1.comments)) - }, - "code" => { - unsorted_vec.sort_by(|a, b| b.1.code.cmp(&a.1.code)) - }, - _ => unreachable!(), - }; - - for (_, language) in unsorted_vec { - if !language.is_empty() { - println!("{}", language); - } - } - } - - println!("{}", ROW); - println!("{}", total); - println!("{}", ROW); + let yaml = load_yaml!("../cli.yml"); + let matches = App::from_yaml(yaml).get_matches(); + + let action_script = Rc::new(RefCell::new(Language::new_c("ActionScript"))); + let bash = Rc::new(RefCell::new(Language::new_single("BASH", "#"))); + let batch = Rc::new(RefCell::new(Language::new_single("Batch", "REM"))); + let c = Rc::new(RefCell::new(Language::new_c("C"))); + let c_header = Rc::new(RefCell::new(Language::new_c("C Header"))); + let c_sharp = Rc::new(RefCell::new(Language::new_c("C#"))); + let clojure = Rc::new(RefCell::new(Language::new_single("Clojure", ";,#,#_"))); + let coffee_script = Rc::new(RefCell::new(Language::new("CoffeeScript", "#", "###", "###"))); + let cold_fusion = Rc::new(RefCell::new(Language::new("ColdFusion", "<!---", "<!---", "--->"))); + let cf_script = Rc::new(RefCell::new(Language::new_c("ColdFusion CFScript"))); + let cpp = Rc::new(RefCell::new(Language::new_c("C++"))); + let cpp_header = Rc::new(RefCell::new(Language::new_c("C++ Header"))); + let css = Rc::new(RefCell::new(Language::new_c("CSS"))); + let d = Rc::new(RefCell::new(Language::new_c("D"))); + let dart = Rc::new(RefCell::new(Language::new_c("Dart"))); + let lisp = Rc::new(RefCell::new(Language::new("LISP", ";", "#|", "|#"))); + let fortran_legacy = Rc::new(RefCell::new(Language::new_single("FORTRAN Legacy", "c,C,!,*"))); + let fortran_modern = Rc::new(RefCell::new(Language::new_single("FORTRAN Modern", "!"))); + let go = Rc::new(RefCell::new(Language::new_c("Go"))); + let haskell = Rc::new(RefCell::new(Language::new_single("Haskell", "--"))); + let html = Rc::new(RefCell::new(Language::new_html("HTML"))); + let java = Rc::new(RefCell::new(Language::new_c("Java"))); + let java_script = Rc::new(RefCell::new(Language::new_c("JavaScript"))); + let julia = Rc::new(RefCell::new(Language::new("Julia", "#", "#=", "=#"))); + let json = Rc::new(RefCell::new(Language::new_blank("JSON"))); + let jsx = Rc::new(RefCell::new(Language::new_c("JSX"))); + let less = Rc::new(RefCell::new(Language::new_c("LESS"))); + let markdown = Rc::new(RefCell::new(Language::new_blank("Markdown"))); + let objective_c = Rc::new(RefCell::new(Language::new_c("Objective-C"))); + let objective_cpp = Rc::new(RefCell::new(Language::new_c("Objective-C++"))); + let php = Rc::new(RefCell::new(Language::new("PHP", "#,//","/*","*/"))); + let pascal = Rc::new(RefCell::new(Language::new("Pascal", "//,(*","{","}"))); + let perl = Rc::new(RefCell::new(Language::new("Perl", "#","=","=cut"))); + let python = Rc::new(RefCell::new(Language::new("Python", "#","'''","'''"))); + let r = Rc::new(RefCell::new(Language::new("R", "#","",""))); + let ruby = Rc::new(RefCell::new(Language::new("Ruby", "#","=begin","=end"))); + let ruby_html = Rc::new(RefCell::new(Language::new_html("Ruby HTML"))); + let rust = Rc::new(RefCell::new(Language::new("Rust", "//,///,//!", "/*", "*/"))); + let sass = Rc::new(RefCell::new(Language::new_c("Sass"))); + let sql = Rc::new(RefCell::new(Language::new("SQL", "--", "/*", "*/"))); + let swift = Rc::new(RefCell::new(Language::new_c("Swift"))); + let toml = Rc::new(RefCell::new(Language::new_single("TOML", "#"))); + let type_script = Rc::new(RefCell::new(Language::new_c("TypeScript"))); + let xml = Rc::new(RefCell::new(Language::new_html("XML"))); + let yaml = Rc::new(RefCell::new(Language::new_single("YAML", "#"))); + + let mut languages: BTreeMap<&str, &Rc<RefCell<Language>>> = BTreeMap::new(); + languages.insert("as" , &action_script); + languages.insert("bat" , &batch); + languages.insert("btm" , &batch); + languages.insert("cmd" , &batch); + languages.insert("bash" , &bash); + languages.insert("sh" , &bash); + languages.insert("c" , &c); + languages.insert("ec" , &c); + languages.insert("pgc" , &c); + languages.insert("cs" , &c_sharp); + languages.insert("clj" , &clojure); + languages.insert("coffee" , &coffee_script); + languages.insert("cfm" , &cold_fusion); + languages.insert("cfc" , &cf_script); + languages.insert("cc" , &cpp); + languages.insert("cpp" , &cpp); + languages.insert("cxx" , &cpp); + languages.insert("pcc" , &cpp); + languages.insert("c++" , &cpp); + languages.insert("css" , &css); + languages.insert("d" , &d); + languages.insert("dart" , &dart); + languages.insert("el" , &lisp); + languages.insert("lisp" , &lisp); + languages.insert("lsp" , &lisp); + languages.insert("sc" , &lisp); + languages.insert("f" , &fortran_legacy); + languages.insert("f77" , &fortran_legacy); + languages.insert("for" , &fortran_legacy); + languages.insert("ftn" , &fortran_legacy); + languages.insert("pfo" , &fortran_legacy); + languages.insert("f90" , &fortran_modern); + languages.insert("f95" , &fortran_modern); + languages.insert("go" , &go); + languages.insert("h" , &c_header); + languages.insert("hs" , &haskell); + languages.insert("hpp" , &cpp_header); + languages.insert("hh" , &cpp_header); + languages.insert("html" , &html); + languages.insert("java" , &java); + languages.insert("js" , &java_script); + languages.insert("jl" , &julia); + languages.insert("json" , &json); + languages.insert("jsx" , &jsx); + languages.insert("less" , &less); + languages.insert("m" , &objective_c); + languages.insert("md" , &markdown); + languages.insert("mm" , &objective_cpp); + languages.insert("php" , &php); + languages.insert("pas" , &pascal); + languages.insert("pl" , &perl); + languages.insert("py" , &python); + languages.insert("r" , &r); + languages.insert("rake" , &ruby); + languages.insert("rb" , &ruby); + languages.insert("rhtml" , &ruby_html); + languages.insert("rs" , &rust); + languages.insert("sass" , &sass); + languages.insert("scss" , &sass); + languages.insert("sql" , &sql); + languages.insert("swift" , &swift); + languages.insert("toml" , &toml); + languages.insert("ts" , &type_script); + languages.insert("xml" , &xml); + languages.insert("yaml" , &yaml); + languages.insert("yml" , &yaml); + + if matches.is_present("languages") { + for (_, language) in languages.iter() { + let ref language = language.borrow(); + println!("{:<25}", language.name); + } + } + + let paths = matches.values_of("input").unwrap(); + + let mut ignored_directories: Vec<String> = Vec::new(); + + if let Some(user_ignored) = matches.value_of("exclude") { + for ignored in user_ignored.split(",") { + ignored_directories.push(ignored.to_owned()); + } + } + + let mut sort = String::new(); + if let Some(sort_by) = matches.value_of("sort") { + match &*sort_by.to_lowercase() { + "files" | "total" | "blanks" | "comments" | "code" => sort.push_str(&*sort_by.to_lowercase()), + _ => println!("--sort must be any of the following files, total, blanks, comments, code"), + } + } + let sort_empty = sort.is_empty(); + + println!("{}", ROW); + println!(" {:<15} {:>15} {:>15} {:>15} {:>15} {:>15}", + "Language", "Files", "Total", "Blanks", "Comments", "Code"); + println!("{}", ROW); + for path in paths { + let files = get_all_files(path.to_owned(), &ignored_directories); + for file in files { + let extension = unwrap_opt_cont!(unwrap_opt_cont!(Path::new(&file).extension()).to_str()); + let lowercase: &str = &extension.to_lowercase(); + let language = unwrap_opt_cont!(languages.get_mut(lowercase)); + language.borrow_mut().files.push(file.to_owned()); + } + } + + let mut total = Language::new_blank("Total"); + for (_, language) in &mut languages { + if language.borrow().printed { + continue; + } + let files = language.borrow_mut().files.clone(); + for file in files { + + let mut contents = String::new(); + let is_fortran = language.borrow().name.contains("FORTRAN"); + let _ = unwrap_rs_cont!(unwrap_rs_cont!(File::open(&file)).read_to_string(&mut contents)); + + let mut is_in_comments = false; + let lines = contents.lines(); + + 'line: for line in lines { + let line = if is_fortran {line} else {line.trim()}; + language.borrow_mut().lines += 1; + + if line.trim().is_empty() { + language.borrow_mut().blanks += 1; + continue; + } + + if !language.borrow().multi_line.is_empty() { + let multi_line = language.borrow().multi_line; + if line.starts_with(multi_line) { + is_in_comments = true; + } else if contains_comments(line, multi_line) { + language.borrow_mut().code += 1; + is_in_comments = true; + } + } + + + if is_in_comments { + if line.contains(language.borrow().multi_line_end) { + is_in_comments = false; + } + + language.borrow_mut().comments += 1; + continue; + } + let single_comments = language.borrow().line_comment.split(","); + for single in single_comments { + if line.starts_with(single) { + language.borrow_mut().comments += 1; + continue 'line; + } + } + language.borrow_mut().code += 1; + } + } + if !language.borrow().is_empty() { + language.borrow_mut().printed = true; + if sort_empty { + println!("{}", *language.borrow()); + } + } + let language = language.borrow(); + + total.total += language.files.len(); + total.lines += language.lines; + total.comments += language.comments; + total.blanks += language.blanks; + total.code += language.code; + } + + if !sort_empty { + let mut unsorted_vec:Vec<(&&str, &&Rc<RefCell<Language>>)> = languages.iter().collect(); + match &*sort { + "files" => { + unsorted_vec.sort_by(|a, b| { + let ref a = *a.1.borrow(); + let ref b = *b.1.borrow(); + b.files.len().cmp(&a.files.len()) + }) + }, + "total" => { + unsorted_vec.sort_by(|a, b| { + let ref a = *a.1.borrow(); + let ref b = *b.1.borrow(); + b.lines.cmp(&a.lines) + }) + }, + "blanks" => { + unsorted_vec.sort_by(|a, b| { + let ref a = *a.1.borrow(); + let ref b = *b.1.borrow(); + b.blanks.cmp(&a.blanks) + }) + }, + "comments" => { + unsorted_vec.sort_by(|a, b| { + let ref a = *a.1.borrow(); + let ref b = *b.1.borrow(); + b.comments.cmp(&a.comments) + }) + }, + "code" => { + unsorted_vec.sort_by(|a, b| { + let ref a = *a.1.borrow(); + let ref b = *b.1.borrow(); + b.code.cmp(&a.code) + }) + }, + _ => unreachable!(), + }; + + for (_, language) in unsorted_vec { + + if !language.borrow().is_empty() && language.borrow().printed { + language.borrow_mut().printed = false; + println!("{}", *language.borrow()); + } + } + } + + println!("{}", ROW); + println!("{}", total); + println!("{}", ROW); } |