summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNick Young <nick@nickwb.net>2019-09-15 00:23:53 +1000
committerKevin Song <chipbuster@users.noreply.github.com>2019-09-14 09:23:53 -0500
commit7e891f17c1c6f59f7a8be18e83927271f307dd3d (patch)
tree1ef229d198f7f0183f8f6ae4467b14fd8dc89f66 /src
parent8f03c14582ad61bf4f95a6b69642d8052002d03d (diff)
perf: Lazy load files from directory (#335)
Changes context to use `once_cell` to lazily evaluate directory listing on first use.
Diffstat (limited to 'src')
-rw-r--r--src/context.rs56
-rw-r--r--src/module.rs10
-rw-r--r--src/modules/golang.rs4
-rw-r--r--src/modules/nodejs.rs4
-rw-r--r--src/modules/python.rs4
-rw-r--r--src/modules/ruby.rs4
-rw-r--r--src/modules/rust.rs4
-rw-r--r--src/modules/time.rs4
-rw-r--r--src/segment.rs4
9 files changed, 46 insertions, 48 deletions
diff --git a/src/context.rs b/src/context.rs
index 836a18dbb..3687eb74c 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -20,7 +20,7 @@ pub struct Context<'a> {
pub current_dir: PathBuf,
/// A vector containing the full paths of all the files in `current_dir`.
- pub dir_files: Vec<PathBuf>,
+ dir_files: OnceCell<Vec<PathBuf>>,
/// The map of arguments that were passed when starship was called.
pub arguments: ArgMatches<'a>,
@@ -52,22 +52,11 @@ impl<'a> Context<'a> {
// TODO: Currently gets the physical directory. Get the logical directory.
let current_dir = Context::expand_tilde(dir.into());
- let dir_files = fs::read_dir(&current_dir)
- .unwrap_or_else(|_| {
- panic!(
- "Unable to read current directory: {}",
- current_dir.to_string_lossy()
- )
- })
- .filter_map(Result::ok)
- .map(|entry| entry.path())
- .collect::<Vec<PathBuf>>();
-
Context {
config,
arguments,
current_dir,
- dir_files,
+ dir_files: OnceCell::new(),
repo: OnceCell::new(),
}
}
@@ -100,19 +89,18 @@ impl<'a> Context<'a> {
// 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(),
+ pub fn try_begin_scan(&'a self) -> Option<ScanDir<'a>> {
+ Some(ScanDir {
+ dir_files: self.get_dir_files().ok()?,
files: &[],
folders: &[],
extensions: &[],
- }
+ })
}
/// Will lazily get repo root and branch when a module requests it.
pub fn get_repo(&self) -> Result<&Repo, std::io::Error> {
- let repo = self
- .repo
+ self.repo
.get_or_try_init(|| -> Result<Repo, std::io::Error> {
let repository = Repository::discover(&self.current_dir).ok();
let branch = repository
@@ -128,9 +116,19 @@ impl<'a> Context<'a> {
root,
state,
})
- })?;
+ })
+ }
- Ok(repo)
+ pub fn get_dir_files(&self) -> Result<&Vec<PathBuf>, std::io::Error> {
+ self.dir_files
+ .get_or_try_init(|| -> Result<Vec<PathBuf>, std::io::Error> {
+ let dir_files = fs::read_dir(&self.current_dir)?
+ .filter_map(Result::ok)
+ .map(|entry| entry.path())
+ .collect::<Vec<PathBuf>>();
+
+ Ok(dir_files)
+ })
}
}
@@ -150,7 +148,7 @@ pub struct Repo {
// 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
+ dir_files: &'a Vec<PathBuf>,
files: &'a [&'a str],
folders: &'a [&'a str],
extensions: &'a [&'a str],
@@ -174,7 +172,7 @@ impl<'a> ScanDir<'a> {
/// 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 {
+ pub fn is_match(&self) -> bool {
self.dir_files.iter().any(|path| {
if path.is_dir() {
path_has_name(path, self.folders)
@@ -261,7 +259,7 @@ mod tests {
#[test]
fn test_criteria_scan_fails() {
- let mut failing_criteria = ScanDir {
+ let failing_criteria = ScanDir {
dir_files: &vec![PathBuf::new()],
files: &["package.json"],
extensions: &["js"],
@@ -269,9 +267,9 @@ mod tests {
};
// fails if buffer does not match any criteria
- assert_eq!(failing_criteria.scan(), false);
+ assert_eq!(failing_criteria.is_match(), false);
- let mut failing_dir_criteria = ScanDir {
+ let failing_dir_criteria = ScanDir {
dir_files: &vec![PathBuf::from("/package.js/dog.go")],
files: &["package.json"],
extensions: &["js"],
@@ -279,18 +277,18 @@ mod tests {
};
// fails when passed a pathbuf dir matches extension path
- assert_eq!(failing_dir_criteria.scan(), false);
+ assert_eq!(failing_dir_criteria.is_match(), false);
}
#[test]
fn test_criteria_scan_passes() {
- let mut passing_criteria = ScanDir {
+ let passing_criteria = ScanDir {
dir_files: &vec![PathBuf::from("package.json")],
files: &["package.json"],
extensions: &["js"],
folders: &["node_modules"],
};
- assert_eq!(passing_criteria.scan(), true);
+ assert_eq!(passing_criteria.is_match(), true);
}
}
diff --git a/src/module.rs b/src/module.rs
index c2f4580e1..dde073fb2 100644
--- a/src/module.rs
+++ b/src/module.rs
@@ -35,7 +35,7 @@ pub struct Module<'a> {
config: Option<&'a toml::value::Table>,
/// The module's name, to be used in configuration and logging.
- name: String,
+ _name: String,
/// The styling to be inherited by all segments contained within this module.
style: Style,
@@ -55,7 +55,7 @@ impl<'a> Module<'a> {
pub fn new(name: &str, config: Option<&'a toml::value::Table>) -> Module<'a> {
Module {
config,
- name: name.to_string(),
+ _name: name.to_string(),
style: Style::default(),
prefix: Affix::default_prefix(name),
segments: Vec::new(),
@@ -204,7 +204,7 @@ fn ansi_strings_modified(ansi_strings: Vec<ANSIString>, shell: String) -> Vec<AN
/// Module affixes are to be used for the prefix or suffix of a module.
pub struct Affix {
/// The affix's name, to be used in configuration and logging.
- name: String,
+ _name: String,
/// The affix's style.
style: Style,
@@ -216,7 +216,7 @@ pub struct Affix {
impl Affix {
pub fn default_prefix(name: &str) -> Self {
Self {
- name: format!("{}_prefix", name),
+ _name: format!("{}_prefix", name),
style: Style::default(),
value: "via ".to_string(),
}
@@ -224,7 +224,7 @@ impl Affix {
pub fn default_suffix(name: &str) -> Self {
Self {
- name: format!("{}_suffix", name),
+ _name: format!("{}_suffix", name),
style: Style::default(),
value: " ".to_string(),
}
diff --git a/src/modules/golang.rs b/src/modules/golang.rs
index 4676f9e6d..9bbf0c16b 100644
--- a/src/modules/golang.rs
+++ b/src/modules/golang.rs
@@ -15,11 +15,11 @@ use super::{Context, Module};
/// - Current directory contains a file with the `.go` extension
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let is_go_project = context
- .new_scan_dir()
+ .try_begin_scan()?
.set_files(&["go.mod", "go.sum", "glide.yaml", "Gopkg.yml", "Gopkg.lock"])
.set_extensions(&["go"])
.set_folders(&["Godeps"])
- .scan();
+ .is_match();
if !is_go_project {
return None;
diff --git a/src/modules/nodejs.rs b/src/modules/nodejs.rs
index 180e143ff..b30f2f531 100644
--- a/src/modules/nodejs.rs
+++ b/src/modules/nodejs.rs
@@ -11,11 +11,11 @@ use super::{Context, Module};
/// - Current directory contains a `node_modules` directory
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let is_js_project = context
- .new_scan_dir()
+ .try_begin_scan()?
.set_files(&["package.json"])
.set_extensions(&["js"])
.set_folders(&["node_modules"])
- .scan();
+ .is_match();
if !is_js_project {
return None;
diff --git a/src/modules/python.rs b/src/modules/python.rs
index 487d033d5..84ca0e7e8 100644
--- a/src/modules/python.rs
+++ b/src/modules/python.rs
@@ -16,7 +16,7 @@ use super::{Context, Module};
/// - Current directory contains a `Pipfile` file
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let is_py_project = context
- .new_scan_dir()
+ .try_begin_scan()?
.set_files(&[
"requirements.txt",
".python-version",
@@ -24,7 +24,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
"Pipfile",
])
.set_extensions(&["py"])
- .scan();
+ .is_match();
if !is_py_project {
return None;
diff --git a/src/modules/ruby.rs b/src/modules/ruby.rs
index 7e689234d..0faa2ccf8 100644
--- a/src/modules/ruby.rs
+++ b/src/modules/ruby.rs
@@ -10,10 +10,10 @@ use super::{Context, Module};
/// - Current directory contains a `Gemfile` file
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let is_rb_project = context
- .new_scan_dir()
+ .try_begin_scan()?
.set_files(&["Gemfile"])
.set_extensions(&["rb"])
- .scan();
+ .is_match();
if !is_rb_project {
return None;
diff --git a/src/modules/rust.rs b/src/modules/rust.rs
index 26ee377c9..2008a539d 100644
--- a/src/modules/rust.rs
+++ b/src/modules/rust.rs
@@ -10,10 +10,10 @@ use super::{Context, Module};
/// - Current directory contains a `Cargo.toml` file
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let is_rs_project = context
- .new_scan_dir()
+ .try_begin_scan()?
.set_files(&["Cargo.toml"])
.set_extensions(&["rs"])
- .scan();
+ .is_match();
if !is_rs_project {
return None;
diff --git a/src/modules/time.rs b/src/modules/time.rs
index 0450e86fe..61a68c724 100644
--- a/src/modules/time.rs
+++ b/src/modules/time.rs
@@ -41,8 +41,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
/// Format a given time into the given string. This function should be referentially
/// transparent, which makes it easy to test (unlike anything involving the actual time)
-fn format_time(time_format: &str, localtime: DateTime<Local>) -> String {
- localtime.format(time_format).to_string()
+fn format_time(time_format: &str, local_time: DateTime<Local>) -> String {
+ local_time.format(time_format).to_string()
}
/* Because we cannot make acceptance tests for the time module, these unit
diff --git a/src/segment.rs b/src/segment.rs
index 8419ec31e..ebae80d51 100644
--- a/src/segment.rs
+++ b/src/segment.rs
@@ -6,7 +6,7 @@ use std::fmt;
/// (e.g. The version that software is running).
pub struct Segment {
/// The segment's name, to be used in configuration and logging.
- name: String,
+ _name: String,
/// The segment's style. If None, will inherit the style of the module containing it.
style: Option<Style>,
@@ -19,7 +19,7 @@ impl Segment {
/// Creates a new segment with default fields.
pub fn new(name: &str) -> Self {
Self {
- name: name.to_string(),
+ _name: name.to_string(),
style: None,
value: "".to_string(),
}