mirror of
https://github.com/Llewellynvdm/starship.git
synced 2025-01-13 18:33:01 +00:00
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,
|
pub current_dir: PathBuf,
|
||||||
|
|
||||||
/// A vector containing the full paths of all the files in `current_dir`.
|
/// 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.
|
/// The map of arguments that were passed when starship was called.
|
||||||
pub arguments: ArgMatches<'a>,
|
pub arguments: ArgMatches<'a>,
|
||||||
@ -52,22 +52,11 @@ impl<'a> Context<'a> {
|
|||||||
// TODO: Currently gets the physical directory. Get the logical directory.
|
// TODO: Currently gets the physical directory. Get the logical directory.
|
||||||
let current_dir = Context::expand_tilde(dir.into());
|
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 {
|
Context {
|
||||||
config,
|
config,
|
||||||
arguments,
|
arguments,
|
||||||
current_dir,
|
current_dir,
|
||||||
dir_files,
|
dir_files: OnceCell::new(),
|
||||||
repo: 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
|
// returns a new ScanDir struct with reference to current dir_files of context
|
||||||
// see ScanDir for methods
|
// see ScanDir for methods
|
||||||
pub fn new_scan_dir(&'a self) -> ScanDir<'a> {
|
pub fn try_begin_scan(&'a self) -> Option<ScanDir<'a>> {
|
||||||
ScanDir {
|
Some(ScanDir {
|
||||||
dir_files: self.dir_files.as_ref(),
|
dir_files: self.get_dir_files().ok()?,
|
||||||
files: &[],
|
files: &[],
|
||||||
folders: &[],
|
folders: &[],
|
||||||
extensions: &[],
|
extensions: &[],
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Will lazily get repo root and branch when a module requests it.
|
/// Will lazily get repo root and branch when a module requests it.
|
||||||
pub fn get_repo(&self) -> Result<&Repo, std::io::Error> {
|
pub fn get_repo(&self) -> Result<&Repo, std::io::Error> {
|
||||||
let repo = self
|
self.repo
|
||||||
.repo
|
|
||||||
.get_or_try_init(|| -> Result<Repo, std::io::Error> {
|
.get_or_try_init(|| -> Result<Repo, std::io::Error> {
|
||||||
let repository = Repository::discover(&self.current_dir).ok();
|
let repository = Repository::discover(&self.current_dir).ok();
|
||||||
let branch = repository
|
let branch = repository
|
||||||
@ -128,9 +116,19 @@ impl<'a> Context<'a> {
|
|||||||
root,
|
root,
|
||||||
state,
|
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
|
// A struct of Criteria which will be used to verify current PathBuf is
|
||||||
// of X language, criteria can be set via the builder pattern
|
// of X language, criteria can be set via the builder pattern
|
||||||
pub struct ScanDir<'a> {
|
pub struct ScanDir<'a> {
|
||||||
dir_files: &'a Vec<PathBuf>, // Replace with reference
|
dir_files: &'a Vec<PathBuf>,
|
||||||
files: &'a [&'a str],
|
files: &'a [&'a str],
|
||||||
folders: &'a [&'a str],
|
folders: &'a [&'a str],
|
||||||
extensions: &'a [&'a str],
|
extensions: &'a [&'a str],
|
||||||
@ -174,7 +172,7 @@ impl<'a> ScanDir<'a> {
|
|||||||
|
|
||||||
/// based on the current Pathbuf check to see
|
/// based on the current Pathbuf check to see
|
||||||
/// if any of this criteria match or exist and returning a boolean
|
/// 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| {
|
self.dir_files.iter().any(|path| {
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
path_has_name(path, self.folders)
|
path_has_name(path, self.folders)
|
||||||
@ -261,7 +259,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_criteria_scan_fails() {
|
fn test_criteria_scan_fails() {
|
||||||
let mut failing_criteria = ScanDir {
|
let failing_criteria = ScanDir {
|
||||||
dir_files: &vec![PathBuf::new()],
|
dir_files: &vec![PathBuf::new()],
|
||||||
files: &["package.json"],
|
files: &["package.json"],
|
||||||
extensions: &["js"],
|
extensions: &["js"],
|
||||||
@ -269,9 +267,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// fails if buffer does not match any criteria
|
// 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")],
|
dir_files: &vec![PathBuf::from("/package.js/dog.go")],
|
||||||
files: &["package.json"],
|
files: &["package.json"],
|
||||||
extensions: &["js"],
|
extensions: &["js"],
|
||||||
@ -279,18 +277,18 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// fails when passed a pathbuf dir matches extension path
|
// 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]
|
#[test]
|
||||||
fn test_criteria_scan_passes() {
|
fn test_criteria_scan_passes() {
|
||||||
let mut passing_criteria = ScanDir {
|
let passing_criteria = ScanDir {
|
||||||
dir_files: &vec![PathBuf::from("package.json")],
|
dir_files: &vec![PathBuf::from("package.json")],
|
||||||
files: &["package.json"],
|
files: &["package.json"],
|
||||||
extensions: &["js"],
|
extensions: &["js"],
|
||||||
folders: &["node_modules"],
|
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>,
|
config: Option<&'a toml::value::Table>,
|
||||||
|
|
||||||
/// The module's name, to be used in configuration and logging.
|
/// 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.
|
/// The styling to be inherited by all segments contained within this module.
|
||||||
style: Style,
|
style: Style,
|
||||||
@ -55,7 +55,7 @@ impl<'a> Module<'a> {
|
|||||||
pub fn new(name: &str, config: Option<&'a toml::value::Table>) -> Module<'a> {
|
pub fn new(name: &str, config: Option<&'a toml::value::Table>) -> Module<'a> {
|
||||||
Module {
|
Module {
|
||||||
config,
|
config,
|
||||||
name: name.to_string(),
|
_name: name.to_string(),
|
||||||
style: Style::default(),
|
style: Style::default(),
|
||||||
prefix: Affix::default_prefix(name),
|
prefix: Affix::default_prefix(name),
|
||||||
segments: Vec::new(),
|
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.
|
/// Module affixes are to be used for the prefix or suffix of a module.
|
||||||
pub struct Affix {
|
pub struct Affix {
|
||||||
/// The affix's name, to be used in configuration and logging.
|
/// The affix's name, to be used in configuration and logging.
|
||||||
name: String,
|
_name: String,
|
||||||
|
|
||||||
/// The affix's style.
|
/// The affix's style.
|
||||||
style: Style,
|
style: Style,
|
||||||
@ -216,7 +216,7 @@ pub struct Affix {
|
|||||||
impl Affix {
|
impl Affix {
|
||||||
pub fn default_prefix(name: &str) -> Self {
|
pub fn default_prefix(name: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: format!("{}_prefix", name),
|
_name: format!("{}_prefix", name),
|
||||||
style: Style::default(),
|
style: Style::default(),
|
||||||
value: "via ".to_string(),
|
value: "via ".to_string(),
|
||||||
}
|
}
|
||||||
@ -224,7 +224,7 @@ impl Affix {
|
|||||||
|
|
||||||
pub fn default_suffix(name: &str) -> Self {
|
pub fn default_suffix(name: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: format!("{}_suffix", name),
|
_name: format!("{}_suffix", name),
|
||||||
style: Style::default(),
|
style: Style::default(),
|
||||||
value: " ".to_string(),
|
value: " ".to_string(),
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,11 @@ use super::{Context, Module};
|
|||||||
/// - Current directory contains a file with the `.go` extension
|
/// - Current directory contains a file with the `.go` extension
|
||||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||||
let is_go_project = context
|
let is_go_project = context
|
||||||
.new_scan_dir()
|
.try_begin_scan()?
|
||||||
.set_files(&["go.mod", "go.sum", "glide.yaml", "Gopkg.yml", "Gopkg.lock"])
|
.set_files(&["go.mod", "go.sum", "glide.yaml", "Gopkg.yml", "Gopkg.lock"])
|
||||||
.set_extensions(&["go"])
|
.set_extensions(&["go"])
|
||||||
.set_folders(&["Godeps"])
|
.set_folders(&["Godeps"])
|
||||||
.scan();
|
.is_match();
|
||||||
|
|
||||||
if !is_go_project {
|
if !is_go_project {
|
||||||
return None;
|
return None;
|
||||||
|
@ -11,11 +11,11 @@ use super::{Context, Module};
|
|||||||
/// - Current directory contains a `node_modules` directory
|
/// - Current directory contains a `node_modules` directory
|
||||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||||
let is_js_project = context
|
let is_js_project = context
|
||||||
.new_scan_dir()
|
.try_begin_scan()?
|
||||||
.set_files(&["package.json"])
|
.set_files(&["package.json"])
|
||||||
.set_extensions(&["js"])
|
.set_extensions(&["js"])
|
||||||
.set_folders(&["node_modules"])
|
.set_folders(&["node_modules"])
|
||||||
.scan();
|
.is_match();
|
||||||
|
|
||||||
if !is_js_project {
|
if !is_js_project {
|
||||||
return None;
|
return None;
|
||||||
|
@ -16,7 +16,7 @@ use super::{Context, Module};
|
|||||||
/// - Current directory contains a `Pipfile` file
|
/// - Current directory contains a `Pipfile` file
|
||||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||||
let is_py_project = context
|
let is_py_project = context
|
||||||
.new_scan_dir()
|
.try_begin_scan()?
|
||||||
.set_files(&[
|
.set_files(&[
|
||||||
"requirements.txt",
|
"requirements.txt",
|
||||||
".python-version",
|
".python-version",
|
||||||
@ -24,7 +24,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||||||
"Pipfile",
|
"Pipfile",
|
||||||
])
|
])
|
||||||
.set_extensions(&["py"])
|
.set_extensions(&["py"])
|
||||||
.scan();
|
.is_match();
|
||||||
|
|
||||||
if !is_py_project {
|
if !is_py_project {
|
||||||
return None;
|
return None;
|
||||||
|
@ -10,10 +10,10 @@ use super::{Context, Module};
|
|||||||
/// - Current directory contains a `Gemfile` file
|
/// - Current directory contains a `Gemfile` file
|
||||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||||
let is_rb_project = context
|
let is_rb_project = context
|
||||||
.new_scan_dir()
|
.try_begin_scan()?
|
||||||
.set_files(&["Gemfile"])
|
.set_files(&["Gemfile"])
|
||||||
.set_extensions(&["rb"])
|
.set_extensions(&["rb"])
|
||||||
.scan();
|
.is_match();
|
||||||
|
|
||||||
if !is_rb_project {
|
if !is_rb_project {
|
||||||
return None;
|
return None;
|
||||||
|
@ -10,10 +10,10 @@ use super::{Context, Module};
|
|||||||
/// - Current directory contains a `Cargo.toml` file
|
/// - Current directory contains a `Cargo.toml` file
|
||||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||||
let is_rs_project = context
|
let is_rs_project = context
|
||||||
.new_scan_dir()
|
.try_begin_scan()?
|
||||||
.set_files(&["Cargo.toml"])
|
.set_files(&["Cargo.toml"])
|
||||||
.set_extensions(&["rs"])
|
.set_extensions(&["rs"])
|
||||||
.scan();
|
.is_match();
|
||||||
|
|
||||||
if !is_rs_project {
|
if !is_rs_project {
|
||||||
return None;
|
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
|
/// 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)
|
/// transparent, which makes it easy to test (unlike anything involving the actual time)
|
||||||
fn format_time(time_format: &str, localtime: DateTime<Local>) -> String {
|
fn format_time(time_format: &str, local_time: DateTime<Local>) -> String {
|
||||||
localtime.format(time_format).to_string()
|
local_time.format(time_format).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Because we cannot make acceptance tests for the time module, these unit
|
/* 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).
|
/// (e.g. The version that software is running).
|
||||||
pub struct Segment {
|
pub struct Segment {
|
||||||
/// The segment's name, to be used in configuration and logging.
|
/// 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.
|
/// The segment's style. If None, will inherit the style of the module containing it.
|
||||||
style: Option<Style>,
|
style: Option<Style>,
|
||||||
@ -19,7 +19,7 @@ impl Segment {
|
|||||||
/// Creates a new segment with default fields.
|
/// Creates a new segment with default fields.
|
||||||
pub fn new(name: &str) -> Self {
|
pub fn new(name: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
_name: name.to_string(),
|
||||||
style: None,
|
style: None,
|
||||||
value: "".to_string(),
|
value: "".to_string(),
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use ansi_term::Color;
|
use ansi_term::Color;
|
||||||
use git2::Repository;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use ansi_term::Color;
|
use ansi_term::Color;
|
||||||
use git2::Repository;
|
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
use ansi_term::Color;
|
|
||||||
use std::fs;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
use crate::common::{self, TestCommand};
|
use crate::common::{self, TestCommand};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user