extern crate glob;
use self::glob::glob;
use colored::*;
use error::*;
use regex::{Regex, RegexSet};
use std::fs;
use std::fs::Metadata;
use std::path::PathBuf;
use std::process::exit;
use std::result::Result;
use types::*;
use utils::*;
#[cfg(not(target_os = "windows"))]
use std::os::unix::fs::PermissionsExt;
pub fn glob_exists(s: &str) -> bool {
glob(s).unwrap().filter_map(Result::ok).count() != 0
}
pub fn is_project_dir(p: &str, name: &str) -> bool {
lazy_static! {
static ref REGEX_PROJECT_DIR: Regex =
Regex::new(r"_minted|((\.stack-work|build|gen|cbits|ats-deps|\.atspkg|target|\.reco-work|\.cabal-sandbox|dist|\.criterion|dist-newstyle.*|target|\.egg-info|elm-stuff|\.pulp-cache|\.psc-package|output|bower_components|node_modules|__pycache__|\.liquid)$)")
.unwrap();
}
if REGEX_PROJECT_DIR.is_match(name) {
let mut parent_path = PathBuf::from(p);
let mut parent_string = p.to_owned();
match name {
".stack-work" => {
let mut hpack = parent_path.clone();
parent_path.push("../cabal.project");
hpack.push("package.yaml");
parent_string.push_str("/../*.cabal");
parent_path.exists() || hpack.exists() || glob_exists(&parent_string)
}
"nimcache" => {
parent_string.push_str("/../*.nim");
glob_exists(&parent_string)
}
"target" => {
let mut dhall = parent_path.clone();
dhall.push("../atspkg.dhall");
let mut shake = parent_path.clone();
shake.push("../shake.hs");
let mut elba = parent_path.clone();
elba.push("../elba.toml");
parent_path.push("../Cargo.toml");
parent_path.exists() || dhall.exists() || shake.exists() || elba.exists()
}
".atspkg" | "ats-deps" | "cbits" | "gen" => {
parent_path.push("../atspkg.dhall");
parent_path.exists()
}
".criterion" => {
parent_path.push("../Cargo.toml");
parent_path.exists()
}
".liquid" => {
parent_string.push_str("/../*.hs");
glob_exists(&parent_string)
}
".reco-work" => {
parent_path.push("../main.go");
parent_path.exists()
}
"elm-stuff" => {
let mut package_path = PathBuf::from(p);
package_path.push("../elm-package.json");
package_path.exists()
}
".pulp-cache" | "output" | ".psc-package" => {
let mut package_path = PathBuf::from(p);
package_path.push("../psc-package.json");
package_path.exists()
}
"build" | "dist" | ".cabal-sandbox" | "dist-newstyle" | "dist-newstyle-meta" => {
let mut cabal_project = parent_path.clone();
let mut parent_string_blod = parent_string.clone();
parent_path.push("../setup.py");
parent_string.push_str("/../*.cabal");
cabal_project.push("../cabal.project");
parent_string_blod.push_str("/../*.blod");
parent_path.exists()
|| glob_exists(&parent_string)
|| cabal_project.exists()
|| glob_exists(&parent_string_blod)
}
"bower_components" => {
let mut package_path = PathBuf::from(p);
package_path.push("../bower.json");
package_path.exists()
}
"__pycache__" => true,
"node_modules" => true,
_ => {
let mut parent_path_latex = parent_path.clone();
parent_path.push("../setup.py");
parent_path_latex.push("../*.tex");
(parent_path.exists() && str::ends_with(name, ".egg-info"))
|| (glob_exists(&parent_path_latex.to_string_lossy())
&& str::starts_with(name, "_minted"))
}
}
} else {
false
}
}
pub fn is_artifact(
path_str: &str,
full_path: &str,
metadata: &Metadata,
vimtags: bool,
gitignore: &Option<RegexSet>,
) -> bool {
lazy_static! {
static ref REGEX_GITIGNORE: Regex =
Regex::new(r"\.(stats|conf|h|c|out|cache.*|dat|pc|info|ll|js)$").unwrap();
}
{
lazy_static! {
static ref REGEX: Regex =
Regex::new(r"\.(a|i|ii|la|lo|o|keter|bc|dyn_o|d|rlib|crate|hi|hc|chi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|bbl|blg|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|hspec-failures|pyc|vo|agdai|beam|mod|go\.(v|teak|xmldef|rewrittenast|rewrittengo|simplego|tree-(bind|eval|finish|parse))|p_hi|p_o|prof|hide-cache|ghc\.environment\..*\d.\d.\d|(t|p|m)ix|synctex\.gz|hl|hp|sandbox\.config|exe|eventlog|ipa|ttc|chs\.h|chi)$")
.unwrap();
}
if REGEX.is_match(path_str) || (path_str == "tags" && vimtags) {
true
} else if let Some(ref x) = *gitignore {
if metadata.permissions().mode() == 0o755 || REGEX_GITIGNORE.is_match(path_str) {
x.is_match(full_path)
} else {
false
}
} else {
path_str == "flxg_stats.txt"
}
}
}
pub fn read_size(
in_paths: &PathBuf,
excludes: Option<&Regex>,
maybe_gitignore: &Option<RegexSet>,
vimtags: bool,
artifacts_only: bool,
) -> FileSize {
let mut size = FileSize::new(0);
let gitignore = if artifacts_only {
mk_ignores(in_paths, maybe_gitignore)
} else {
None
};
if let Ok(paths) = fs::read_dir(in_paths) {
for p in paths {
let val = match p {
Ok(x) => x,
_ => {
panic!("{}", Internal::IoError);
}
};
let path = val.path();
let (path_string, bool_loop): (&str, bool) = if let Some(x) = path.as_path().to_str() {
let bool_loop = match excludes {
Some(ex) => !ex.is_match(x),
_ => true,
};
(x, bool_loop)
} else {
eprintln!(
"{}: skipping invalid unicode filepath at {:?}",
"Warning".yellow(),
path
);
("", false)
};
if bool_loop {
let path_type = val.file_type().unwrap();
if path_type.is_file() {
if let Ok(metadata) = val.metadata() {
if !artifacts_only || {
is_artifact(
val.file_name().to_str().unwrap(),
path_string,
&metadata,
vimtags,
&gitignore,
)
} {
let file_size = FileSize::new(metadata.len());
size.add(file_size);
}
}
}
else if path_type.is_dir() {
let dir_size = if artifacts_only
&& is_project_dir(path_string, val.file_name().to_str().unwrap())
{
read_size(&path, excludes, &gitignore, vimtags, false)
} else {
read_size(&path, excludes, &gitignore, vimtags, artifacts_only)
};
size.add(dir_size);
}
}
}
}
else if !in_paths.exists() {
eprintln!(
"{}: path '{}' does not exist, or you do not have permission to enter.",
"Error".red(),
&in_paths.display()
);
}
else if !in_paths.is_dir() {
eprintln!(
"{}: {} is not a directory.",
"Error".red(),
&in_paths.display()
);
exit(0x0001);
}
else {
eprintln!(
"{}: permission denied for directory: {}",
"Warning".yellow(),
&in_paths.display()
);
}
size
}
pub fn read_all(
in_paths: &PathBuf,
depth: u8,
max_depth: Option<u8>,
excludes: Option<&Regex>,
maybe_gitignore: &Option<RegexSet>,
vimtags: bool,
artifacts_only: bool,
) -> FileTree {
let mut tree = FileTree::new();
let gitignore = if artifacts_only {
mk_ignores(in_paths, maybe_gitignore)
} else {
None
};
if let Ok(paths) = fs::read_dir(in_paths) {
for p in paths {
let val = match p {
Ok(x) => x,
_ => {
eprintln!("{}: {:?}.", "Error".red(), p);
exit(0x0001)
}
};
let path = val.path();
let (path_string, bool_loop): (&str, bool) = if let Some(x) = path.as_path().to_str() {
let bool_loop = match excludes {
Some(ex) => !ex.is_match(x),
_ => true,
};
(x, bool_loop)
} else {
eprintln!(
"{}: skipping invalid unicode filepath at {:?}",
"Warning".yellow(),
path
);
("", false)
};
if bool_loop {
let path_type = val.file_type().unwrap();
if path_type.is_file() {
if let Ok(metadata) = val.metadata() {
if !artifacts_only || {
is_artifact(
val.file_name().to_str().unwrap(),
path_string,
&metadata,
vimtags,
&gitignore,
)
} {
let file_size = FileSize::new(metadata.len());
tree.push(path_string.to_string(), file_size, None, depth + 1, false);
}
}
}
else if path_type.is_dir() {
if let Some(d) = max_depth {
if depth + 1 >= d && !artifacts_only {
let dir_size =
{ read_size(&path, excludes, &gitignore, vimtags, artifacts_only) };
tree.push(path_string.to_string(), dir_size, None, depth + 1, true);
} else if artifacts_only
&& is_project_dir(path_string, val.file_name().to_str().unwrap())
{
let dir_size =
{ read_size(&path, excludes, &gitignore, vimtags, false) };
tree.push(path_string.to_string(), dir_size, None, depth + 1, true);
} else {
let mut subtree = read_all(
&path,
depth + 1,
max_depth,
excludes,
&gitignore,
vimtags,
artifacts_only,
);
let dir_size = subtree.file_size;
tree.push(
path_string.to_string(),
dir_size,
Some(&mut subtree),
depth + 1,
true,
);
}
} else if artifacts_only
&& is_project_dir(path_string, val.file_name().to_str().unwrap())
{
let dir_size = { read_size(&path, excludes, &gitignore, vimtags, false) };
tree.push(path_string.to_string(), dir_size, None, depth + 1, true);
} else {
let mut subtree = read_all(
&path,
depth + 1,
max_depth,
excludes,
&gitignore,
vimtags,
artifacts_only,
);
let dir_size = subtree.file_size;
tree.push(
path_string.to_string(),
dir_size,
Some(&mut subtree),
depth + 1,
true,
);
}
}
}
}
}
else if !in_paths.exists() {
eprintln!(
"{}: path '{}' does not exist, or you do not have permission to enter.",
"Error".red(),
&in_paths.display()
);
}
else if !in_paths.is_dir() {
if artifacts_only {
eprintln!(
"{}: {} is not a directory; not searching for artifacts",
"Warning".yellow(),
&in_paths.display()
);
}
if let Ok(l) = in_paths.metadata() {
let size = l.len();
let to_formatted = format!("{}", FileSize::new(size));
println!("{}\t {}", &to_formatted.green(), in_paths.display());
} else {
panic!("{}", Internal::IoError);
}
}
else {
eprintln!(
"{}: permission denied for directory: {}",
"Warning".yellow(),
&in_paths.display()
);
}
tree
}
pub fn read_no_excludes(
in_paths: &PathBuf,
_: Option<&Regex>,
_: &Option<RegexSet>,
_: bool,
) -> FileSize {
let mut size = FileSize::new(0);
if let Ok(paths) = fs::read_dir(in_paths) {
for p in paths {
let val = match p {
Ok(x) => x,
_ => {
panic!("{}", Internal::IoError);
}
};
let path_type = val.file_type().unwrap();
if path_type.is_file() {
if let Ok(metadata) = val.metadata() {
let file_size = FileSize::new(metadata.len());
size.add(file_size);
}
}
else if path_type.is_dir() {
let dir_size = {
let path = val.path();
read_no_excludes(&path, None, &None, false)
};
size.add(dir_size);
}
}
}
else if !in_paths.exists() {
eprintln!(
"{}: path '{}' does not exist, or you do not have permission to enter.",
"Error".red(),
&in_paths.display()
);
}
else if !in_paths.is_dir() {
eprintln!(
"{}: {} is not a directory.",
"Error".red(),
&in_paths.display()
);
exit(0x0001);
}
else {
eprintln!(
"{}: permission denied for directory: {}",
"Warning".yellow(),
&in_paths.display()
);
}
size
}
pub fn read_all_fast(in_paths: &PathBuf, depth: u8, max_depth: Option<u8>) -> FileTree {
let mut tree = FileTree::new();
if let Ok(paths) = fs::read_dir(in_paths) {
for p in paths {
let val = match p {
Ok(x) => x,
_ => {
eprintln!("{}: unexpected failure on {:?} failed.", "Error".red(), p);
exit(0x0001)
}
};
let path_type = val.file_type().unwrap();
if path_type.is_file() {
if let Ok(metadata) = val.metadata() {
{
let path = val.path();
let path_string: &str = if let Some(x) = path.as_path().to_str() {
x
} else {
eprintln!(
"{}: skipping invalid unicode filepath at {:?}",
"Warning".yellow(),
path
);
""
};
let file_size = FileSize::new(metadata.len());
tree.push(path_string.to_string(), file_size, None, depth + 1, false);
}
}
}
else if path_type.is_dir() {
if let Some(d) = max_depth {
if depth + 1 >= d {
let path = val.path();
let path_string: &str = if let Some(x) = path.as_path().to_str() {
x
} else {
eprintln!(
"{}: skipping invalid unicode filepath at {:?}",
"Warning".yellow(),
path
);
""
};
let dir_size = { read_no_excludes(&path, None, &None, false) };
tree.push(path_string.to_string(), dir_size, None, depth + 1, true);
} else {
let path = val.path();
let path_string: &str = if let Some(x) = path.as_path().to_str() {
x
} else {
eprintln!(
"{}: skipping invalid unicode filepath at {:?}",
"Warning".yellow(),
path
);
""
};
let mut subtree = read_all_fast(&path, depth + 1, max_depth);
let dir_size = subtree.file_size;
tree.push(
path_string.to_string(),
dir_size,
Some(&mut subtree),
depth + 1,
true,
);
}
} else {
let path = val.path();
let path_string: &str = if let Some(x) = path.as_path().to_str() {
x
} else {
eprintln!(
"{}: skipping invalid unicode filepath at {:?}",
"Warning".yellow(),
path
);
""
};
let mut subtree = read_all_fast(&path, depth + 1, max_depth);
let dir_size = subtree.file_size;
tree.push(
path_string.to_string(),
dir_size,
Some(&mut subtree),
depth + 1,
true,
);
}
}
}
}
else if !in_paths.exists() {
eprintln!(
"{}: path '{}' does not exist, or you do not have permission to enter.",
"Error".red(),
&in_paths.display()
);
}
else if !in_paths.is_dir() {
if let Ok(l) = in_paths.metadata() {
let size = l.len();
let to_formatted = format!("{}", FileSize::new(size));
println!("{}\t {}", &to_formatted.green(), in_paths.display());
} else {
panic!("{}", Internal::IoError);
}
}
else {
eprintln!(
"{}: permission denied for directory: {}",
"Warning".yellow(),
&in_paths.display()
);
}
tree
}