perf: Lazy load files from directory (#335)
Changes context to use `once_cell` to lazily evaluate directory listing on first use.
This commit is contained in:
parent
8f03c14582
commit
7e891f17c1
|
@ -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(¤t_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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use ansi_term::Color;
|
||||
use git2::Repository;
|
||||
use std::io;
|
||||
use std::process::Command;
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use ansi_term::Color;
|
||||
use git2::Repository;
|
||||
use std::fs::{self, File};
|
||||
use std::io;
|
||||
use std::process::Command;
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
use ansi_term::Color;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::common::{self, TestCommand};
|
||||
|
||||
|
|
Loading…
Reference in New Issue