summaryrefslogtreecommitdiffstats
path: root/src/context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/context.rs')
-rw-r--r--src/context.rs73
1 files changed, 63 insertions, 10 deletions
diff --git a/src/context.rs b/src/context.rs
index 4a00bba6b..3fa8b0b59 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -7,6 +7,7 @@ use clap::ArgMatches;
use dirs_next::home_dir;
use git2::{ErrorCode::UnbornBranch, Repository, RepositoryState};
use once_cell::sync::OnceCell;
+use starship_cache::Cache;
use std::collections::{HashMap, HashSet};
use std::env;
use std::ffi::OsString;
@@ -44,17 +45,21 @@ pub struct Context<'a> {
/// A HashMap of environment variable mocks
#[cfg(test)]
- pub env: HashMap<&'a str, String>,
+ pub env_mocks: HashMap<&'a str, String>,
/// A HashMap of command mocks
#[cfg(test)]
- pub cmd: HashMap<&'a str, Option<CommandOutput>>,
+ pub cmd_mocks: HashMap<&'a str, Option<CommandOutput>>,
+ /// A placeholder for a mock battery provider
#[cfg(feature = "battery")]
pub battery_info_provider: &'a (dyn crate::modules::BatteryInfoProvider + Send + Sync),
/// Timeout for the execution of commands
cmd_timeout: Duration,
+
+ /// An instance of Starship's binary output cache
+ cmd_cache: Cache
}
impl<'a> Context<'a> {
@@ -113,6 +118,15 @@ impl<'a> Context<'a> {
let cmd_timeout = Duration::from_millis(config.get_root_config().command_timeout);
+ let cache_dir = env::var_os("STARSHIP_CACHE")
+ .map(PathBuf::from)
+ .unwrap_or_else(|| {
+ dirs_next::home_dir()
+ .expect("Unable to find home directory")
+ .join(".cache/starship")
+ });
+ let cmd_cache = Cache::create_or_parse(cache_dir).unwrap();
+
Context {
config,
properties,
@@ -122,12 +136,13 @@ impl<'a> Context<'a> {
repo: OnceCell::new(),
shell,
#[cfg(test)]
- env: HashMap::new(),
+ env_mocks: HashMap::new(),
#[cfg(test)]
- cmd: HashMap::new(),
+ cmd_mocks: HashMap::new(),
#[cfg(feature = "battery")]
battery_info_provider: &crate::modules::BatteryInfoProviderImpl,
cmd_timeout,
+ cmd_cache
}
}
@@ -143,7 +158,7 @@ impl<'a> Context<'a> {
// Retrives a environment variable from the os or from a table if in testing mode
#[cfg(test)]
pub fn get_env<K: AsRef<str>>(&self, key: K) -> Option<String> {
- self.env.get(key.as_ref()).map(|val| val.to_string())
+ self.env_mocks.get(key.as_ref()).map(|val| val.to_string())
}
#[cfg(not(test))]
@@ -155,7 +170,7 @@ impl<'a> Context<'a> {
// Retrives a environment variable from the os or from a table if in testing mode (os version)
#[cfg(test)]
pub fn get_env_os<K: AsRef<str>>(&self, key: K) -> Option<OsString> {
- self.env.get(key.as_ref()).map(OsString::from)
+ self.env_mocks.get(key.as_ref()).map(OsString::from)
}
#[cfg(not(test))]
@@ -264,20 +279,58 @@ impl<'a> Context<'a> {
self.properties.get("cmd_duration")?.parse::<u128>().ok()
}
- /// Execute a command and return the output on stdout and stderr if successful
+ /// Execute a command and return the output on stdout and stderr if successful,
+ /// while caching the output and respecting the user's timeout configuration.
+ ///
+ /// This method automatically caches successful commands to short-circuit
+ /// future calls. For a non-caching alternative, use [`Self::uncached_exec_cmd()`].
#[inline]
- pub fn exec_cmd(&self, cmd: &str, args: &[&str]) -> Option<CommandOutput> {
+ pub fn exec_cmd(&mut self, cmd: &str, args: &[&str]) -> Option<CommandOutput> {
#[cfg(test)]
{
- let command = match args.len() {
+ let full_command = match args.len() {
0 => cmd.to_owned(),
_ => format!("{} {}", cmd, args.join(" ")),
};
- if let Some(output) = self.cmd.get(command.as_str()) {
+ if let Some(output) = self.cmd_mocks.get(full_command.as_str()) {
return output.clone();
}
}
+
+ log::trace!("Executing command {:?} with args {:?}", cmd, args);
+
+ let full_path = match which::which(cmd) {
+ Ok(full_path) => {
+ log::trace!("Using {:?} as {:?}", full_path, cmd);
+ full_path
+ }
+ Err(error) => {
+ log::trace!("Unable to find {:?} in PATH, {:?}", cmd, error);
+ return None;
+ }
+ };
+ let full_command = format!("{} {}", cmd, args.join(" "));
+
+ if let Some(output) = self.cmd_cache.get(&full_path, &full_command) {
+ log::info!("Retreived {:?} from cache: {:?}", full_command, output);
+ let output = CommandOutput::from(output);
+ return Some(output);
+ };
+
exec_cmd(cmd, args, self.cmd_timeout)
+ .map(|output| {
+ self.cmd_cache.set(&full_path, &full_command, &output);
+ output
+ })
+ }
+
+ /// Execute a command and return the output on stdout and stderr if successful,
+ /// while respecting the user's timeout configuration.
+ ///
+ /// This method specifically doesn't cache its results. For an alternative that
+ /// caches for use by successive calls, use [`Self::exec_cmd()`].
+ pub fn uncached_exec_cmd(&self, cmd: &str, args: &[&str]) -> Option<CommandOutput> {
+ todo!()
}
}