Implement directory scanner (#34)

This commit is contained in:
Tim Mulqueen 2019-05-12 13:37:23 -04:00 committed by Matan Kushner
parent d3ce00c516
commit 5fd715e7c3
5 changed files with 184 additions and 86 deletions

View file

@ -1,6 +1,7 @@
use clap::ArgMatches;
use git2::Repository;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::path::PathBuf;
@ -56,4 +57,138 @@ impl<'a> Context<'a> {
}
dir
}
// returns a new ScanDir struct with reference to current dir_files of context
// see ScanDir for methods
pub fn new_scan_dir(&'a self) -> ScanDir<'a> {
ScanDir {
dir_files: self.dir_files.as_ref(),
files: &[],
folders: &[],
extensions: &[],
}
}
}
// A struct of Criteria which will be used to verify current PathBuf is
// of X language, criteria can be set via the builder pattern
pub struct ScanDir<'a> {
dir_files: &'a Vec<PathBuf>, // Replace with reference
files: &'a [&'a str],
folders: &'a [&'a str],
extensions: &'a [&'a str],
}
impl<'a> ScanDir<'a> {
pub fn set_files(mut self, files: &'a [&'a str]) -> Self {
self.files = files;
self
}
pub fn set_extensions(mut self, extensions: &'a [&'a str]) -> Self {
self.extensions = extensions;
self
}
pub fn set_folders(mut self, folders: &'a [&'a str]) -> Self {
self.folders = folders;
self
}
/// based on the current Pathbuf check to see
/// if any of this criteria match or exist and returning a boolean
pub fn scan(&mut self) -> bool {
self.dir_files.iter().any(|path| {
path_has_name(&path, &self.folders)
|| path_has_name(&path, &self.files)
|| has_extension(&path, &self.extensions)
})
}
}
/// checks to see if the pathbuf matches a file or folder name
pub fn path_has_name<'a>(dir_entry: &PathBuf, names: &'a [&'a str]) -> bool {
let found_file_or_folder_name = names.into_iter().find(|file_or_folder_name| {
dir_entry
.file_name()
.and_then(OsStr::to_str)
.unwrap_or_default()
== **file_or_folder_name
});
match found_file_or_folder_name {
Some(name) => !name.is_empty(),
None => false,
}
}
/// checks if pathbuf matches the extension provided
pub fn has_extension<'a>(dir_entry: &PathBuf, extensions: &'a [&'a str]) -> bool {
let found_ext = extensions.into_iter().find(|ext| {
dir_entry
.extension()
.and_then(OsStr::to_str)
.unwrap_or_default()
== **ext
});
match found_ext {
Some(extension) => !extension.is_empty(),
None => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_has_name() {
let mut buf = PathBuf::from("/");
let files = vec!["package.json"];
assert_eq!(path_has_name(&buf, &files), false);
buf.set_file_name("some-file.js");
assert_eq!(path_has_name(&buf, &files), false);
buf.set_file_name("package.json");
assert_eq!(path_has_name(&buf, &files), true);
}
#[test]
fn test_has_extension() {
let mut buf = PathBuf::from("/");
let extensions = vec!["js"];
assert_eq!(has_extension(&buf, &extensions), false);
buf.set_file_name("some-file.rs");
assert_eq!(has_extension(&buf, &extensions), false);
buf.set_file_name("some-file.js");
assert_eq!(has_extension(&buf, &extensions), true)
}
#[test]
fn test_criteria_scan() {
let mut failing_criteria = ScanDir {
dir_files: &vec![PathBuf::new()],
files: &["package.json"],
extensions: &["js"],
folders: &["node_modules"],
};
// fails if buffer does not match any criteria
assert_eq!(failing_criteria.scan(), false);
let mut passing_criteria = ScanDir {
dir_files: &vec![PathBuf::from("package.json")],
files: &["package.json"],
extensions: &["js"],
folders: &["node_modules"],
};
assert_eq!(passing_criteria.scan(), true);
}
}

View file

@ -4,59 +4,44 @@ use std::process::Command;
use super::{Context, Module};
/// Creates a segment with the current Python version
/// Creates a segment with the current Go version
///
/// Will display the Python version if any of the following criteria are met:
/// - Current directory contains a `.go` file
/// Will display the Go version if any of the following criteria are met:
/// - Current directory contains a `go.mod` file
/// - Current directory contains a `go.sum` file
/// - Current directory contains a `Godeps` directory
/// - Current directory contains a `glide.yaml` file
/// - Current directory contains a `Gopkg.yml` file
/// - Current directory contains a `Gopkg.lock` file
/// - Current directory contains a `.go` file
/// - Current directory contains a `Godeps` directory
pub fn segment(context: &Context) -> Option<Module> {
let is_go_project = context.dir_files.iter().any(has_go_files);
let is_go_project = context
.new_scan_dir()
.set_files(&["go.mod", "go.sum", "glide.yaml", "Gopkg.yml", "Gopkg.lock"])
.set_extensions(&["go"])
.set_folders(&["Godeps"])
.scan();
if !is_go_project {
return None;
}
const GO_CHAR: &str = "🐹 ";
let module_color = Color::Cyan.bold();
match get_go_version() {
Some(go_version) => {
const GO_CHAR: &str = "🐹 ";
let module_color = Color::Cyan.bold();
let mut module = Module::new("go");
module.set_style(module_color);
let mut module = Module::new("go");
module.set_style(module_color);
let go_version = get_go_version()?;
let formatted_version = format_go_version(go_version)?;
module.new_segment("symbol", GO_CHAR);
module.new_segment("version", formatted_version);
let formatted_version = format_go_version(go_version)?;
module.new_segment("symbol", GO_CHAR);
module.new_segment("version", formatted_version);
Some(module)
}
fn has_go_files(dir_entry: &PathBuf) -> bool {
let is_go_mod =
|d: &PathBuf| -> bool { d.is_file() && d.file_name().unwrap_or_default() == "go.mod" };
let is_go_sum =
|d: &PathBuf| -> bool { d.is_file() && d.file_name().unwrap_or_default() == "go.sum" };
let is_godeps =
|d: &PathBuf| -> bool { d.is_dir() && d.file_name().unwrap_or_default() == "Godeps" };
let is_glide_yaml =
|d: &PathBuf| -> bool { d.is_file() && d.file_name().unwrap_or_default() == "glide.yaml" };
let is_go_file =
|d: &PathBuf| -> bool { d.is_file() && d.extension().unwrap_or_default() == "go" };
let is_gopkg_yml =
|d: &PathBuf| -> bool { d.is_file() && d.file_name().unwrap_or_default() == "Gopkg.yml" };
let is_gopkg_lock =
|d: &PathBuf| -> bool { d.is_file() && d.file_name().unwrap_or_default() == "Gopkg.lock" };
is_go_mod(&dir_entry)
|| is_go_sum(&dir_entry)
|| is_godeps(&dir_entry)
|| is_glide_yaml(&dir_entry)
|| is_go_file(&dir_entry)
|| is_gopkg_yml(&dir_entry)
|| is_gopkg_lock(&dir_entry)
Some(module)
}
None => None,
}
}
fn get_go_version() -> Option<String> {

View file

@ -1,5 +1,4 @@
use ansi_term::Color;
use std::path::PathBuf;
use std::process::Command;
use super::{Context, Module};
@ -11,7 +10,13 @@ use super::{Context, Module};
/// - Current directory contains a `package.json` file
/// - Current directory contains a `node_modules` directory
pub fn segment(context: &Context) -> Option<Module> {
let is_js_project = context.dir_files.iter().any(has_js_files);
let is_js_project = context
.new_scan_dir()
.set_files(&["package.json"])
.set_extensions(&["js"])
.set_folders(&["node_modules"])
.scan();
if !is_js_project {
return None;
}
@ -34,18 +39,6 @@ pub fn segment(context: &Context) -> Option<Module> {
}
}
fn has_js_files(dir_entry: &PathBuf) -> bool {
let is_js_file =
|d: &PathBuf| -> bool { d.is_file() && d.extension().unwrap_or_default() == "js" };
let is_node_modules =
|d: &PathBuf| -> bool { d.is_dir() && d.file_name().unwrap_or_default() == "node_modules" };
let is_package_json = |d: &PathBuf| -> bool {
d.is_file() && d.file_name().unwrap_or_default() == "package.json"
};
is_js_file(&dir_entry) || is_node_modules(&dir_entry) || is_package_json(&dir_entry)
}
fn get_node_version() -> Option<String> {
match Command::new("node").arg("--version").output() {
Ok(output) => Some(String::from_utf8(output.stdout).unwrap()),

View file

@ -1,5 +1,4 @@
use ansi_term::Color;
use std::path::PathBuf;
use std::process::Command;
use super::{Context, Module};
@ -12,7 +11,17 @@ use super::{Context, Module};
/// - Current directory contains a `requirements.txt` file
/// - Current directory contains a `pyproject.toml` file
pub fn segment(context: &Context) -> Option<Module> {
let is_py_project = context.dir_files.iter().any(has_py_files);
let is_py_project = context
.new_scan_dir()
.set_files(&[
"requirements.txt",
".python-version",
"pyproject.toml",
"pyproject.toml",
])
.set_extensions(&["py"])
.scan();
if !is_py_project {
return None;
}
@ -35,25 +44,6 @@ pub fn segment(context: &Context) -> Option<Module> {
}
}
fn has_py_files(dir_entry: &PathBuf) -> bool {
let is_py_file =
|d: &PathBuf| -> bool { d.is_file() && d.extension().unwrap_or_default() == "py" };
let is_python_version = |d: &PathBuf| -> bool {
d.is_file() && d.file_name().unwrap_or_default() == ".python-version"
};
let is_requirements_txt = |d: &PathBuf| -> bool {
d.is_file() && d.file_name().unwrap_or_default() == "requirements.txt"
};
let is_py_project = |d: &PathBuf| -> bool {
d.is_file() && d.file_name().unwrap_or_default() == "pyproject.toml"
};
is_py_file(&dir_entry)
|| is_python_version(&dir_entry)
|| is_requirements_txt(&dir_entry)
|| is_py_project(&dir_entry)
}
fn get_python_version() -> Option<String> {
match Command::new("python").arg("--version").output() {
Ok(output) => Some(String::from_utf8(output.stdout).unwrap()),

View file

@ -1,5 +1,4 @@
use ansi_term::Color;
use std::path::PathBuf;
use std::process::Command;
use super::{Context, Module};
@ -7,10 +6,15 @@ use super::{Context, Module};
/// Creates a segment with the current Rust version
///
/// Will display the Rust version if any of the following criteria are met:
/// - Current directory contains a `.rs` file
/// - Current directory contains a file with a `.rs` extension
/// - Current directory contains a `Cargo.toml` file
pub fn segment(context: &Context) -> Option<Module> {
let is_rs_project = context.dir_files.iter().any(has_rs_files);
let is_rs_project = context
.new_scan_dir()
.set_files(&["Cargo.toml"])
.set_extensions(&["rs"])
.scan();
if !is_rs_project {
return None;
}
@ -33,15 +37,6 @@ pub fn segment(context: &Context) -> Option<Module> {
}
}
fn has_rs_files(dir_entry: &PathBuf) -> bool {
let is_rs_file =
|d: &PathBuf| -> bool { d.is_file() && d.extension().unwrap_or_default() == "rs" };
let is_cargo_toml =
|d: &PathBuf| -> bool { d.is_file() && d.file_name().unwrap_or_default() == "Cargo.toml" };
is_rs_file(&dir_entry) || is_cargo_toml(&dir_entry)
}
fn get_rust_version() -> Option<String> {
match Command::new("rustc").arg("-V").output() {
Ok(output) => Some(String::from_utf8(output.stdout).unwrap()),