summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: a19e43db6edeb16a5806c8a25916f1ce2da22ddf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#[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<config::CommentTokens>, 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<config::CommentTokens>) -> Result<i32>{
	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<config::CommentTokens>) {
	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<config::CommentTokens>) {
	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 <benedict@0xb8000.de>")
		.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);
}