Skip to content
This repository has been archived by the owner on Nov 14, 2023. It is now read-only.

Commit

Permalink
fix(Symlinks): adds ability to optionally follow symlinks while counting
Browse files Browse the repository at this point in the history
Closes #6
Closes #7
  • Loading branch information
kbknapp committed Aug 25, 2015
1 parent 551c79c commit d265980
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 76 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 24 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::env;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use clap::ArgMatches;

Expand All @@ -21,9 +21,10 @@ pub struct Config<'a> {
pub thousands: Option<char>,
pub utf8_rule: Utf8Rule,
pub usafe: bool,
pub exclude: Vec<&'a str>,
pub exclude: Vec<PathBuf>,
pub exts: Option<Vec<&'a str>>,
pub to_count: Vec<PathBuf>
pub to_count: Vec<PathBuf>,
pub follow_links: bool
}

impl<'a> Config<'a> {
Expand All @@ -40,7 +41,24 @@ impl<'a> Config<'a> {
thousands: m.value_of("sep").map(|s| s.chars().nth(0).unwrap() ),
usafe: m.is_present("unsafe-statistics"),
utf8_rule: value_t!(m.value_of("rule"), Utf8Rule).unwrap_or(Utf8Rule::Strict),
exclude: m.values_of("paths").unwrap_or(vec![".git"]),
exclude: if let Some(v) = m.values_of("paths") {
debugln!("There are some");
let mut ret = vec![];
for p in v {
let pb = Path::new(p);
if pb.is_relative() {
ret.push(cli_try!(env::current_dir()).join(p));
} else {
ret.push(pb.to_path_buf());
}
}
debugln!("found files or dirs: {:?}", ret);
ret.push(cli_try!(env::current_dir()).join(".git"));
ret
} else {
debugln!("There aren't any, adding .git");
vec!(cli_try!(env::current_dir()).join(".git"))
},
to_count: if let Some(v) = m.values_of("to_count") {
debugln!("There are some");
let mut ret = vec![];
Expand All @@ -53,7 +71,8 @@ impl<'a> Config<'a> {
debugln!("There aren't any, using cwd");
vec![cli_try!(env::current_dir())]
},
exts: m.values_of("exts")
exts: m.values_of("exts"),
follow_links: m.is_present("follow-symlinks")
})
}
}
67 changes: 32 additions & 35 deletions src/count/counts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,46 +41,43 @@ impl<'c> Counts<'c> {
debugln!("executing; fill_from; cfg={:?}", self.cfg);
for path in &self.cfg.to_count {
debugln!("iter; path={:?};", path);
if let Some(f) = path.to_str() {
let files = fsutil::get_all_files(f, &self.cfg.exclude);
let mut files = vec![];
fsutil::get_all_files(&mut files, &path, &self.cfg.exclude, self.cfg.follow_links);

for file in files {
debugln!("iter; file={:?};", file);
let extension = match Path::new(&file).extension() {
Some(result) => {
if let Some(ref exts) = self.cfg.exts {
if !exts.contains(&result.to_str().unwrap_or("")) { continue }
}
result.to_str().unwrap()
},
None => continue,
};

debugln!("found extension: {:?}", extension);
if let Some(pos_lang) = Language::from_ext(extension) {
debugln!("Extension is valid");
let mut found = false;
debugln!("Searching for previous entries of that type");
for l in self.counts.iter_mut() {
if l.lang.extension() == extension {
debugln!("Found");
found = true;
l.add_file(PathBuf::from(&file));
break;
}
for file in files {
debugln!("iter; file={:?};", file);
let extension = match Path::new(&file).extension() {
Some(result) => {
if let Some(ref exts) = self.cfg.exts {
if !exts.contains(&result.to_str().unwrap_or("")) { continue }
}
if !found {
debugln!("Not found, creating new");
let mut c = Count::new(pos_lang, self.cfg.thousands);
c.add_file(PathBuf::from(&file));
self.counts.push(c);
result.to_str().unwrap()
},
None => continue,
};

debugln!("found extension: {:?}", extension);
if let Some(pos_lang) = Language::from_ext(extension) {
debugln!("Extension is valid");
let mut found = false;
debugln!("Searching for previous entries of that type");
for l in self.counts.iter_mut() {
if l.lang.extension() == extension {
debugln!("Found");
found = true;
l.add_file(PathBuf::from(&file));
break;
}
} else {
debugln!("extension wasn't valid");
}
if !found {
debugln!("Not found, creating new");
let mut c = Count::new(pos_lang, self.cfg.thousands);
c.add_file(PathBuf::from(&file));
self.counts.push(c);
}
} else {
debugln!("extension wasn't valid");
}
} else {
debugln!("path couldn't be converted to a str");
}
}
}
Expand Down
66 changes: 33 additions & 33 deletions src/fsutil.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,56 @@
use std::fs;
use std::fs::metadata;
use std::io::Result;
use std::path::PathBuf;

use glob;

pub fn get_all_files<'a>(path: &'a str, exclude: &Vec<&'a str>) -> Vec<PathBuf> {
pub fn get_all_files<'a>(v: &mut Vec<PathBuf>, path: &PathBuf, exclude: &Vec<PathBuf>, follow_links: bool) {
debugln!("executing; get_all_files; path={:?}; exclude={:?};", path, exclude);
let mut files = vec![];
if exclude.contains(path) {
return
}

debugln!("Getting metadata");
if let Ok(result) = metadata(&path) {
if let Ok(result) = get_metadata(&path, follow_links) {
debugln!("Found");
if result.is_dir() {
debugln!("It's a dir");
let dir = fs::read_dir(&path).unwrap();
'file: for entry in dir {
for entry in dir {
let entry = entry.unwrap();
let file_path = entry.path();
let file_str = file_path.to_str().expect("file_path isn't a valid str");
let file_string = file_str.to_owned();
let path_metadata = metadata(&file_string).unwrap();

for ignored in exclude {
debugln!("iter; ignored={:?}", ignored);
if file_str.contains(ignored) {
debugln!("iter; ignored={:?}", ignored);
continue 'file;
}
}
if path_metadata.is_dir() {
for file in get_all_files(&*file_string, &exclude) {
files.push(file);
}
} else if path_metadata.is_file() {
files.push(PathBuf::from(file_str));
}
get_all_files(v, &file_path.to_path_buf(), &exclude, follow_links);
}
} else {
debugln!("It's a file");
if !exclude.contains(&path) {
debugln!("It's not excluded");
files.push(PathBuf::from(path));
} else {
debugln!("It's excluded");
}
v.push(path.clone());
}
} else {
for path_buf in glob::glob(&path).ok().expect("failed to get files from glob") {
let file_path = path_buf.unwrap();
files.push(file_path);
for path_buf in glob::glob(path.to_str().unwrap_or("")).ok().expect("failed to get files from glob") {
if let Ok(file_path) = path_buf {
if let Ok(result) = get_metadata(&file_path, follow_links) {
if result.is_dir() {
debugln!("It's a dir");
let dir = fs::read_dir(&path).unwrap();
for entry in dir {
let entry = entry.unwrap();
let file_path = entry.path();
get_all_files(v, &file_path.to_path_buf(), &exclude, follow_links);
}
} else {
debugln!("It's a file");
v.push(path.clone());
}
}
}
}
}
}

files
fn get_metadata(path: &PathBuf, follow_links: bool) -> Result<fs::Metadata> {
if follow_links {
fs::metadata(path)
} else {
fs::symlink_metadata(path)
}
}
12 changes: 10 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,20 @@ fn main() {
-l, --language [exts]... 'Only count these languges (by source code extension){n}\
(i.e. \'-l js py cpp\')'
-v, --verbose 'Print verbose output'
-S, --follow-symlinks 'Follows symlinks and counts source files it finds{n}(Defaults to false when omitted)'
[to_count]... 'The files or directories (including children) to count{n}\
(defaults to current working directory when omitted)'")
.arg(Arg::from_usage("-s, --separator [sep] 'Set the thousands separator for pretty printing'")
.validator(single_char))
.arg(Arg::from_usage("--utf8-rule [rule] 'Sets the UTF-8 parsing rule (Defaults to \'strict\'){n}'")
.possible_values(&UTF8_RULES)))
.possible_values(&UTF8_RULES))
.after_help("When using '--exclude <path>' the path given can either be relative to the current \n\
directory, or absolute. When '<path>' is a file, it must be relative to the current \n\
directory or it will not be found. Example, if the current directory has a child \n\
directory named 'target' with a child fild 'test.rs' and you use `--exclude target/test.rs' \n\
\n\
Globs are also supported. For example, to eclude 'test.rs' files from all child directories \n\
of the current directory you could do '--exclude */test.rs'."))
.get_matches();

if let Some(m) = m.subcommand_matches("count") {
Expand All @@ -68,7 +76,7 @@ fn main() {

fn execute(cfg: Config) -> CliResult<()> {
debugln!("executing; cmd=execute;");
verboseln!(cfg, "{}: {}", Format::Warning("Excluding"), cfg.exclude.connect(", "));
verboseln!(cfg, "{}: {:?}", Format::Warning("Excluding"), cfg.exclude);
verbose!(cfg, "{}",
if cfg.exts.is_some() {
format!("{} including files with extension: {}\n", Format::Warning("Only"), cfg.exts.as_ref().unwrap().connect(", "))
Expand Down

0 comments on commit d265980

Please sign in to comment.