#[macro_use] extern crate clap; use std::fs::File; use std::path::Path; use std::io::*; use std::path::PathBuf; use clap::{App, Arg}; mod config; fn omit_line(line: String, in_comment_section: &mut bool, comment_tokens: &config::CommentTokens) -> bool { let s = line.trim(); let begin_comment_section = s.starts_with(comment_tokens.begin_comment_block.as_str()); let end_comment_section = s.starts_with(comment_tokens.end_comment_block.as_str()); if begin_comment_section { *in_comment_section = true; return true; } else if *in_comment_section && !end_comment_section { return true; } else if *in_comment_section && end_comment_section { *in_comment_section = false; return true; } s.starts_with(comment_tokens.single_line.as_str()) || s.is_empty() } fn get_config<'a>(config: &'a Vec, extension: &str) -> Option<&'a config::CommentTokens> { for conf in config.iter() { if conf.file_ending == extension { return Some(conf); } } None } fn count_lines(filename: &str, config: &mut Vec) -> Result{ let mut sum = 0; let mut in_comment_section = false; // do we have config for this type of file? let extension = match Path::new(filename).extension() { Some(ext) => ext.to_str().unwrap(), None => return Err(Error::new(std::io::ErrorKind::Other, "could not get extension")), }; let comment_tokens = match get_config(&config, extension) { Some(conf) => conf, None => return Err(Error::new(std::io::ErrorKind::NotFound, "config not found")), }; let bufread = BufReader::new(try!(File::open(filename))); for line in bufread.lines() { if omit_line(try!(line), &mut in_comment_section, &comment_tokens) { continue; } sum += 1; } Ok(sum) } fn count_files_for_dir(dir: PathBuf, mut sum: &mut i32, mut config: &mut Vec) { match dir.read_dir() { Ok(iter) => { for entry in iter { match entry { Ok(i) => { if i.path().is_dir() { count_files_for_dir(i.path(), &mut sum, &mut config); } else { count_lines_for_file(i.path().to_str().unwrap(), &mut sum, &mut config); } }, Err(e) => println!("error: {}", e), } } }, Err(e) => println!("error: {}", e), } } fn count_lines_for_file(filename: &str, sum: &mut i32, mut config: &mut Vec) { match count_lines(filename, &mut config) { Ok(i) => { println!("{}\t: {}", i, filename); *sum += i; }, Err(e) => println!("error for file {}: {}", filename, e), } } fn main() { let m = App::new("loc").about("Count lines of code. Without parameter read recursive all files from current directory and count lines of code\ for each") .version(crate_version!()) .author("Benedict Börger ") .arg(Arg::with_name("file") .help("files to count lines of code") .short("f") .long("file") .takes_value(true) .multiple(true)) .get_matches(); let mut config = match config::read_config() { Err(e) => panic!("error reading config file(s): {}", e), Ok(i) => i, }; let mut sum = 0; if let Some(files) = m.values_of("file") { for file in files { count_lines_for_file(file, &mut sum, &mut config) } } else { match std::env::current_dir() { Ok(i) => count_files_for_dir(i, &mut sum, &mut config), Err(e) => panic!("could not determine current dir: {}", e), } } println!("{}\t: total", sum); }