1
0
mirror of https://github.com/Llewellynvdm/starship.git synced 2024-10-31 18:52:29 +00:00

feat: allow changing default command timeout (#2283)

* feat: allow changing default command timeout

* fix clippy

* add doc to exec_cmd in Context

* update docs in CONTRIBUTING.md

* Fix comment in CONTRIBUTING.md

Co-authored-by: Thomas O'Donnell <andytom@users.noreply.github.com>

Co-authored-by: Thomas O'Donnell <andytom@users.noreply.github.com>
This commit is contained in:
David Knaack 2021-02-11 21:34:47 +01:00 committed by GitHub
parent 04d1332f9c
commit eccbda8328
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 112 additions and 97 deletions

View File

@ -37,7 +37,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::php::PhpConfig; use crate::configs::php::PhpConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
@ -51,20 +50,19 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
## External commands ## External commands
To run a external command (e.g. to get the version of a tool) and to allow for mocking use the `utils::exec_cmd` function. Here's a quick example: To run a external command (e.g. to get the version of a tool) and to allow for mocking use the `context.exec_cmd` function. Here's a quick example:
```rust ```rust
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use crate::configs::php::PhpConfig; use crate::configs::php::PhpConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
// Here `my_env_var` will be either the stdout of the called command or the function // Here `output` will be either the stdout of the called command or the function
// will exit if the called program was not installed or could not be run. // will exit if the called program was not installed or could not be run.
let output = utils::exec_cmd("my_command", &["first_arg", "second_arg"])?.stdout; let output = context.exec_cmd("my_command", &["first_arg", "second_arg"])?.stdout;
// Then you can happily use the output // Then you can happily use the output
} }

View File

@ -3,6 +3,7 @@ use crate::utils::exec_cmd;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Duration;
#[cfg(feature = "http")] #[cfg(feature = "http")]
const GIT_IO_BASE_URL: &str = "https://git.io/"; const GIT_IO_BASE_URL: &str = "https://git.io/";
@ -152,7 +153,7 @@ fn get_shell_info() -> ShellInfo {
let shell = shell.unwrap(); let shell = shell.unwrap();
let version = exec_cmd(&shell, &["--version"]) let version = exec_cmd(&shell, &["--version"], Duration::from_millis(500))
.map(|output| output.stdout.trim().to_string()) .map(|output| output.stdout.trim().to_string())
.unwrap_or_else(|| UNKNOWN_VERSION.to_string()); .unwrap_or_else(|| UNKNOWN_VERSION.to_string());

View File

@ -6,6 +6,7 @@ use starship_module_config_derive::ModuleConfig;
pub struct StarshipRootConfig<'a> { pub struct StarshipRootConfig<'a> {
pub format: &'a str, pub format: &'a str,
pub scan_timeout: u64, pub scan_timeout: u64,
pub command_timeout: u64,
pub add_newline: bool, pub add_newline: bool,
} }
@ -78,6 +79,7 @@ impl<'a> RootModuleConfig<'a> for StarshipRootConfig<'a> {
StarshipRootConfig { StarshipRootConfig {
format: "$all", format: "$all",
scan_timeout: 30, scan_timeout: 30,
command_timeout: 500,
add_newline: true, add_newline: true,
} }
} }

View File

@ -1,5 +1,6 @@
use crate::config::StarshipConfig; use crate::config::StarshipConfig;
use crate::module::Module; use crate::module::Module;
use crate::utils::{exec_cmd, CommandOutput};
use crate::modules; use crate::modules;
use clap::ArgMatches; use clap::ArgMatches;
@ -43,6 +44,9 @@ pub struct Context<'a> {
/// A HashMap of environment variable mocks /// A HashMap of environment variable mocks
pub env: HashMap<&'a str, String>, pub env: HashMap<&'a str, String>,
/// Timeout for the execution of commands
cmd_timeout: Duration,
} }
impl<'a> Context<'a> { impl<'a> Context<'a> {
@ -100,6 +104,8 @@ impl<'a> Context<'a> {
let current_dir = current_dir.canonicalize().unwrap_or(current_dir); let current_dir = current_dir.canonicalize().unwrap_or(current_dir);
let logical_dir = logical_path; let logical_dir = logical_path;
let cmd_timeout = Duration::from_millis(config.get_root_config().command_timeout);
Context { Context {
config, config,
properties, properties,
@ -109,6 +115,7 @@ impl<'a> Context<'a> {
repo: OnceCell::new(), repo: OnceCell::new(),
shell, shell,
env: HashMap::new(), env: HashMap::new(),
cmd_timeout,
} }
} }
@ -237,6 +244,11 @@ impl<'a> Context<'a> {
pub fn get_cmd_duration(&self) -> Option<u128> { pub fn get_cmd_duration(&self) -> Option<u128> {
self.properties.get("cmd_duration")?.parse::<u128>().ok() self.properties.get("cmd_duration")?.parse::<u128>().ok()
} }
/// Execute a command and return the output on stdout and stderr if successful
pub fn exec_cmd(&self, cmd: &str, args: &[&str]) -> Option<CommandOutput> {
exec_cmd(cmd, args, self.cmd_timeout)
}
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::cmake::CMakeConfig; use crate::configs::cmake::CMakeConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current CMake version /// Creates a module with the current CMake version
/// ///
@ -31,7 +30,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
_ => None, _ => None,
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => utils::exec_cmd("cmake", &["--version"]) "version" => context
.exec_cmd("cmake", &["--version"])
.map(|output| format_cmake_version(&output.stdout)) .map(|output| format_cmake_version(&output.stdout))
.flatten() .flatten()
.map(Ok), .map(Ok),

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::crystal::CrystalConfig; use crate::configs::crystal::CrystalConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Crystal version /// Creates a module with the current Crystal version
/// ///
@ -35,7 +34,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => format_crystal_version( "version" => format_crystal_version(
utils::exec_cmd("crystal", &["--version"])?.stdout.as_str(), context.exec_cmd("crystal", &["--version"])?.stdout.as_str(),
) )
.map(Ok), .map(Ok),
_ => None, _ => None,

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::dart::DartConfig; use crate::configs::dart::DartConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Dart version /// Creates a module with the current Dart version
/// ///
@ -37,7 +36,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let dart_version = utils::exec_cmd("dart", &["--version"])?.stderr; let dart_version = context.exec_cmd("dart", &["--version"])?.stderr;
parse_dart_version(&dart_version).map(Ok) parse_dart_version(&dart_version).map(Ok)
} }
_ => None, _ => None,

View File

@ -66,9 +66,14 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
"version" => { "version" => {
let version = if enable_heuristic { let version = if enable_heuristic {
let repo_root = context.get_repo().ok().and_then(|r| r.root.as_deref()); let repo_root = context.get_repo().ok().and_then(|r| r.root.as_deref());
estimate_dotnet_version(&dotnet_files, &context.current_dir, repo_root) estimate_dotnet_version(
context,
&dotnet_files,
&context.current_dir,
repo_root,
)
} else { } else {
get_version_from_cli() get_version_from_cli(context)
}; };
version.map(|v| Ok(v.0)) version.map(|v| Ok(v.0))
} }
@ -136,6 +141,7 @@ fn get_tfm_from_project_file(path: &Path) -> Option<String> {
} }
fn estimate_dotnet_version( fn estimate_dotnet_version(
context: &Context,
files: &[DotNetFile], files: &[DotNetFile],
current_dir: &Path, current_dir: &Path,
repo_root: Option<&Path>, repo_root: Option<&Path>,
@ -150,17 +156,18 @@ fn estimate_dotnet_version(
match relevant_file.file_type { match relevant_file.file_type {
FileType::GlobalJson => get_pinned_sdk_version_from_file(relevant_file.path.as_path()) FileType::GlobalJson => get_pinned_sdk_version_from_file(relevant_file.path.as_path())
.or_else(get_latest_sdk_from_cli), .or_else(|| get_latest_sdk_from_cli(context)),
FileType::SolutionFile => { FileType::SolutionFile => {
// With this heuristic, we'll assume that a "global.json" won't // With this heuristic, we'll assume that a "global.json" won't
// be found in any directory above the solution file. // be found in any directory above the solution file.
get_latest_sdk_from_cli() get_latest_sdk_from_cli(context)
} }
_ => { _ => {
// If we see a dotnet project, we'll check a small number of neighboring // If we see a dotnet project, we'll check a small number of neighboring
// directories to see if we can find a global.json. Otherwise, assume the // directories to see if we can find a global.json. Otherwise, assume the
// latest SDK is in use. // latest SDK is in use.
try_find_nearby_global_json(current_dir, repo_root).or_else(get_latest_sdk_from_cli) try_find_nearby_global_json(current_dir, repo_root)
.or_else(|| get_latest_sdk_from_cli(context))
} }
} }
} }
@ -288,13 +295,13 @@ fn map_str_to_lower(value: Option<&OsStr>) -> Option<String> {
Some(value?.to_str()?.to_ascii_lowercase()) Some(value?.to_str()?.to_ascii_lowercase())
} }
fn get_version_from_cli() -> Option<Version> { fn get_version_from_cli(context: &Context) -> Option<Version> {
let version_output = utils::exec_cmd("dotnet", &["--version"])?; let version_output = context.exec_cmd("dotnet", &["--version"])?;
Some(Version(format!("v{}", version_output.stdout.trim()))) Some(Version(format!("v{}", version_output.stdout.trim())))
} }
fn get_latest_sdk_from_cli() -> Option<Version> { fn get_latest_sdk_from_cli(context: &Context) -> Option<Version> {
match utils::exec_cmd("dotnet", &["--list-sdks"]) { match context.exec_cmd("dotnet", &["--list-sdks"]) {
Some(sdks_output) => { Some(sdks_output) => {
fn parse_failed<T>() -> Option<T> { fn parse_failed<T>() -> Option<T> {
log::warn!("Unable to parse the output from `dotnet --list-sdks`."); log::warn!("Unable to parse the output from `dotnet --list-sdks`.");
@ -325,7 +332,7 @@ fn get_latest_sdk_from_cli() -> Option<Version> {
"Received a non-success exit code from `dotnet --list-sdks`. \ "Received a non-success exit code from `dotnet --list-sdks`. \
Falling back to `dotnet --version`.", Falling back to `dotnet --version`.",
); );
get_version_from_cli() get_version_from_cli(context)
} }
} }
} }

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::elixir::ElixirConfig; use crate::configs::elixir::ElixirConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
@ -23,7 +22,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
return None; return None;
} }
let versions = Lazy::new(get_elixir_version); let versions = Lazy::new(|| get_elixir_version(context));
let mut module = context.new_module("elixir"); let mut module = context.new_module("elixir");
let config = ElixirConfig::try_load(module.config); let config = ElixirConfig::try_load(module.config);
@ -64,8 +63,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
fn get_elixir_version() -> Option<(String, String)> { fn get_elixir_version(context: &Context) -> Option<(String, String)> {
let output = utils::exec_cmd("elixir", &["--version"])?.stdout; let output = context.exec_cmd("elixir", &["--version"])?.stdout;
parse_elixir_version(&output) parse_elixir_version(&output)
} }

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::elm::ElmConfig; use crate::configs::elm::ElmConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Elm version /// Creates a module with the current Elm version
/// ///
@ -39,7 +38,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let elm_version = utils::exec_cmd("elm", &["--version"])?.stdout; let elm_version = context.exec_cmd("elm", &["--version"])?.stdout;
let module_version = Some(format!("v{}", elm_version.trim()))?; let module_version = Some(format!("v{}", elm_version.trim()))?;
Some(Ok(module_version)) Some(Ok(module_version))
} }

View File

@ -32,7 +32,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
_ => None, _ => None,
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => get_erlang_version().map(Ok), "version" => get_erlang_version(context).map(Ok),
_ => None, _ => None,
}) })
.parse(None) .parse(None)
@ -49,10 +49,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
fn get_erlang_version() -> Option<String> { fn get_erlang_version(context: &Context) -> Option<String> {
use crate::utils; Some(context.exec_cmd(
Some(utils::exec_cmd(
"erl", "erl",
&[ &[
"-noshell", "-noshell",

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::go::GoConfig; use crate::configs::go::GoConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Go version /// Creates a module with the current Go version
/// ///
@ -48,7 +47,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
format_go_version(&utils::exec_cmd("go", &["version"])?.stdout.as_str()).map(Ok) format_go_version(&context.exec_cmd("go", &["version"])?.stdout.as_str())
.map(Ok)
} }
_ => None, _ => None,
}) })

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::helm::HelmConfig; use crate::configs::helm::HelmConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Helm version /// Creates a module with the current Helm version
/// ///
@ -33,7 +32,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => format_helm_version( "version" => format_helm_version(
&utils::exec_cmd("helm", &["version", "--short", "--client"])? &context
.exec_cmd("helm", &["version", "--short", "--client"])?
.stdout .stdout
.as_str(), .as_str(),
) )

View File

@ -3,8 +3,6 @@ use crate::formatter::StringFormatter;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use crate::utils;
use regex::Regex; use regex::Regex;
const JAVA_VERSION_PATTERN: &str = "(?P<version>[\\d\\.]+)[^\\s]*\\s(?:built|from)"; const JAVA_VERSION_PATTERN: &str = "(?P<version>[\\d\\.]+)[^\\s]*\\s(?:built|from)";
@ -72,7 +70,7 @@ fn get_java_version(context: &Context) -> Option<String> {
None => String::from("java"), None => String::from("java"),
}; };
let output = utils::exec_cmd(&java_command.as_str(), &["-Xinternalversion"])?; let output = context.exec_cmd(&java_command.as_str(), &["-Xinternalversion"])?;
let java_version = if output.stdout.is_empty() { let java_version = if output.stdout.is_empty() {
output.stderr output.stderr
} else { } else {

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::julia::JuliaConfig; use crate::configs::julia::JuliaConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Julia version /// Creates a module with the current Julia version
/// ///
@ -34,10 +33,10 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
_ => None, _ => None,
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => format_julia_version(
format_julia_version(&utils::exec_cmd("julia", &["--version"])?.stdout.as_str()) &context.exec_cmd("julia", &["--version"])?.stdout.as_str(),
.map(Ok) )
} .map(Ok),
_ => None, _ => None,
}) })
.parse(None) .parse(None)

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::kotlin::KotlinConfig; use crate::configs::kotlin::KotlinConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
use regex::Regex; use regex::Regex;
const KOTLIN_VERSION_PATTERN: &str = "(?P<version>[\\d\\.]+[\\d\\.]+[\\d\\.]+)"; const KOTLIN_VERSION_PATTERN: &str = "(?P<version>[\\d\\.]+[\\d\\.]+[\\d\\.]+)";
@ -35,8 +34,10 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let kotlin_version = let kotlin_version = format_kotlin_version(&get_kotlin_version(
format_kotlin_version(&get_kotlin_version(&config.kotlin_binary)?)?; context,
&config.kotlin_binary,
)?)?;
Some(Ok(kotlin_version)) Some(Ok(kotlin_version))
} }
_ => None, _ => None,
@ -55,8 +56,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
fn get_kotlin_version(kotlin_binary: &str) -> Option<String> { fn get_kotlin_version(context: &Context, kotlin_binary: &str) -> Option<String> {
match utils::exec_cmd(kotlin_binary, &["-version"]) { match context.exec_cmd(kotlin_binary, &["-version"]) {
Some(output) => { Some(output) => {
if output.stdout.is_empty() { if output.stdout.is_empty() {
Some(output.stderr) Some(output.stderr)

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::lua::LuaConfig; use crate::configs::lua::LuaConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
use regex::Regex; use regex::Regex;
const LUA_VERSION_PATERN: &str = "(?P<version>[\\d\\.]+[a-z\\-]*[1-9]*)[^\\s]*"; const LUA_VERSION_PATERN: &str = "(?P<version>[\\d\\.]+[a-z\\-]*[1-9]*)[^\\s]*";
@ -39,7 +38,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let lua_version = format_lua_version(&get_lua_version(&config.lua_binary)?)?; let lua_version =
format_lua_version(&get_lua_version(context, &config.lua_binary)?)?;
Some(Ok(lua_version)) Some(Ok(lua_version))
} }
_ => None, _ => None,
@ -58,8 +58,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
fn get_lua_version(lua_binary: &str) -> Option<String> { fn get_lua_version(context: &Context, lua_binary: &str) -> Option<String> {
match utils::exec_cmd(lua_binary, &["-v"]) { match context.exec_cmd(lua_binary, &["-v"]) {
Some(output) => { Some(output) => {
if output.stdout.is_empty() { if output.stdout.is_empty() {
Some(output.stderr) Some(output.stderr)

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::nim::NimConfig; use crate::configs::nim::NimConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Nim version /// Creates a module with the current Nim version
/// ///
@ -34,7 +33,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
_ => None, _ => None,
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => utils::exec_cmd("nim", &["--version"]) "version" => context
.exec_cmd("nim", &["--version"])
.map(|command_output| command_output.stdout) .map(|command_output| command_output.stdout)
.and_then(|nim_version_output| { .and_then(|nim_version_output| {
Some(format!("v{}", parse_nim_version(&nim_version_output)?)) Some(format!("v{}", parse_nim_version(&nim_version_output)?))

View File

@ -38,8 +38,11 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("nodejs"); let mut module = context.new_module("nodejs");
let config = NodejsConfig::try_load(module.config); let config = NodejsConfig::try_load(module.config);
let nodejs_version = let nodejs_version = Lazy::new(|| {
Lazy::new(|| utils::exec_cmd("node", &["--version"]).map(|cmd| cmd.stdout)); context
.exec_cmd("node", &["--version"])
.map(|cmd| cmd.stdout)
});
let parsed = StringFormatter::new(config.format).and_then(|formatter| { let parsed = StringFormatter::new(config.format).and_then(|formatter| {
formatter formatter
.map_meta(|var, _| match var { .map_meta(|var, _| match var {

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::ocaml::OCamlConfig; use crate::configs::ocaml::OCamlConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current OCaml version /// Creates a module with the current OCaml version
/// ///
@ -46,9 +45,9 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
.is_match(); .is_match();
let ocaml_version = if is_esy_project { let ocaml_version = if is_esy_project {
utils::exec_cmd("esy", &["ocaml", "-vnum"])?.stdout context.exec_cmd("esy", &["ocaml", "-vnum"])?.stdout
} else { } else {
utils::exec_cmd("ocaml", &["-vnum"])?.stdout context.exec_cmd("ocaml", &["-vnum"])?.stdout
}; };
Some(Ok(format!("v{}", &ocaml_version.trim()))) Some(Ok(format!("v{}", &ocaml_version.trim())))
} }

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::perl::PerlConfig; use crate::configs::perl::PerlConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current perl version /// Creates a module with the current perl version
/// ///
@ -44,8 +43,9 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let perl_version = let perl_version = context
utils::exec_cmd("perl", &["-e", "printf q#%vd#,$^V;"])?.stdout; .exec_cmd("perl", &["-e", "printf q#%vd#,$^V;"])?
.stdout;
Some(Ok(format!("v{}", perl_version))) Some(Ok(format!("v{}", perl_version)))
} }
_ => None, _ => None,

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::php::PhpConfig; use crate::configs::php::PhpConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current PHP version /// Creates a module with the current PHP version
/// ///
@ -35,7 +34,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let php_cmd_output = utils::exec_cmd( let php_cmd_output = context.exec_cmd(
"php", "php",
&[ &[
"-nr", "-nr",

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::purescript::PureScriptConfig; use crate::configs::purescript::PureScriptConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current PureScript version /// Creates a module with the current PureScript version
/// ///
@ -35,7 +34,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let purs_version = utils::exec_cmd("purs", &["--version"])?.stdout; let purs_version = context.exec_cmd("purs", &["--version"])?.stdout;
Some(Ok(format!("v{}", purs_version.trim()))) Some(Ok(format!("v{}", purs_version.trim())))
} }
_ => None, _ => None,

View File

@ -4,7 +4,6 @@ use std::path::Path;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use crate::configs::python::PythonConfig; use crate::configs::python::PythonConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Python version and, if active, virtual environment. /// Creates a module with the current Python version and, if active, virtual environment.
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
@ -42,7 +41,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let version = get_python_version(&config)?; let version = get_python_version(context, &config)?;
Some(Ok(version.trim().to_string())) Some(Ok(version.trim().to_string()))
} }
"virtualenv" => { "virtualenv" => {
@ -66,12 +65,12 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module) Some(module)
} }
fn get_python_version(config: &PythonConfig) -> Option<String> { fn get_python_version(context: &Context, config: &PythonConfig) -> Option<String> {
if config.pyenv_version_name { if config.pyenv_version_name {
return Some(utils::exec_cmd("pyenv", &["version-name"])?.stdout); return Some(context.exec_cmd("pyenv", &["version-name"])?.stdout);
}; };
let version = config.python_binary.0.iter().find_map(|binary| { let version = config.python_binary.0.iter().find_map(|binary| {
match utils::exec_cmd(binary, &["--version"]) { match context.exec_cmd(binary, &["--version"]) {
Some(output) => { Some(output) => {
if output.stdout.is_empty() { if output.stdout.is_empty() {
Some(output.stderr) Some(output.stderr)

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::ruby::RubyConfig; use crate::configs::ruby::RubyConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Ruby version /// Creates a module with the current Ruby version
/// ///
@ -34,7 +33,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
format_ruby_version(&utils::exec_cmd("ruby", &["-v"])?.stdout.as_str()).map(Ok) format_ruby_version(&context.exec_cmd("ruby", &["-v"])?.stdout.as_str()).map(Ok)
} }
_ => None, _ => None,
}) })

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::swift::SwiftConfig; use crate::configs::swift::SwiftConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Swift version /// Creates a module with the current Swift version
/// ///
@ -34,7 +33,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let swift_version = utils::exec_cmd("swift", &["--version"])?.stdout; let swift_version = context.exec_cmd("swift", &["--version"])?.stdout;
parse_swift_version(&swift_version).map(Ok) parse_swift_version(&swift_version).map(Ok)
} }
_ => None, _ => None,

View File

@ -38,7 +38,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => format_terraform_version( "version" => format_terraform_version(
&utils::exec_cmd("terraform", &["version"])?.stdout.as_str(), &context.exec_cmd("terraform", &["version"])?.stdout.as_str(),
) )
.map(Ok), .map(Ok),
"workspace" => get_terraform_workspace(context).map(Ok), "workspace" => get_terraform_workspace(context).map(Ok),

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::vagrant::VagrantConfig; use crate::configs::vagrant::VagrantConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Vagrant version /// Creates a module with the current Vagrant version
/// ///
@ -32,7 +31,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => format_vagrant_version( "version" => format_vagrant_version(
&utils::exec_cmd("vagrant", &["--version"])?.stdout.as_str(), &context.exec_cmd("vagrant", &["--version"])?.stdout.as_str(),
) )
.map(Ok), .map(Ok),
_ => None, _ => None,

View File

@ -2,7 +2,6 @@ use super::{Context, Module, RootModuleConfig};
use crate::configs::zig::ZigConfig; use crate::configs::zig::ZigConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
use crate::utils;
/// Creates a module with the current Zig version /// Creates a module with the current Zig version
/// ///
@ -33,7 +32,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}) })
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let zig_version_output = utils::exec_cmd("zig", &["version"])?.stdout; let zig_version_output = context.exec_cmd("zig", &["version"])?.stdout;
let zig_version = format!("v{}", zig_version_output.trim()); let zig_version = format!("v{}", zig_version_output.trim());
Some(Ok(zig_version)) Some(Ok(zig_version))
} }

View File

@ -30,12 +30,12 @@ impl PartialEq for CommandOutput {
/// 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
#[cfg(not(test))] #[cfg(not(test))]
pub fn exec_cmd(cmd: &str, args: &[&str]) -> Option<CommandOutput> { pub fn exec_cmd(cmd: &str, args: &[&str], time_limit: Duration) -> Option<CommandOutput> {
internal_exec_cmd(&cmd, &args) internal_exec_cmd(&cmd, &args, time_limit)
} }
#[cfg(test)] #[cfg(test)]
pub fn exec_cmd(cmd: &str, args: &[&str]) -> Option<CommandOutput> { pub fn exec_cmd(cmd: &str, args: &[&str], time_limit: Duration) -> Option<CommandOutput> {
let command = match args.len() { let command = match args.len() {
0 => String::from(cmd), 0 => String::from(cmd),
_ => format!("{} {}", cmd, args.join(" ")), _ => format!("{} {}", cmd, args.join(" ")),
@ -202,7 +202,7 @@ CMake suite maintained and supported by Kitware (kitware.com/cmake).\n",
stderr: String::default(), stderr: String::default(),
}), }),
// If we don't have a mocked command fall back to executing the command // If we don't have a mocked command fall back to executing the command
_ => internal_exec_cmd(&cmd, &args), _ => internal_exec_cmd(&cmd, &args, time_limit),
} }
} }
@ -255,7 +255,7 @@ pub fn wrap_seq_for_shell(
final_string final_string
} }
fn internal_exec_cmd(cmd: &str, args: &[&str]) -> Option<CommandOutput> { fn internal_exec_cmd(cmd: &str, args: &[&str], time_limit: Duration) -> Option<CommandOutput> {
log::trace!("Executing command {:?} with args {:?}", cmd, args); log::trace!("Executing command {:?} with args {:?}", cmd, args);
let full_path = match which::which(cmd) { let full_path = match which::which(cmd) {
@ -269,8 +269,6 @@ fn internal_exec_cmd(cmd: &str, args: &[&str]) -> Option<CommandOutput> {
} }
}; };
let time_limit = Duration::from_millis(500);
let start = Instant::now(); let start = Instant::now();
let process = match Command::new(full_path) let process = match Command::new(full_path)
@ -310,7 +308,8 @@ fn internal_exec_cmd(cmd: &str, args: &[&str]) -> Option<CommandOutput> {
}) })
} }
Ok(None) => { Ok(None) => {
log::warn!("Executing command {:?} timed out", cmd); log::warn!("Executing command {:?} timed out.", cmd);
log::warn!("You can set command_timeout in your config to a higher value to allow longer-running commands to keep executing.");
None None
} }
Err(error) => { Err(error) => {
@ -326,7 +325,7 @@ mod tests {
#[test] #[test]
fn exec_mocked_command() { fn exec_mocked_command() {
let result = exec_cmd("dummy_command", &[]); let result = exec_cmd("dummy_command", &[], Duration::from_millis(500));
let expected = Some(CommandOutput { let expected = Some(CommandOutput {
stdout: String::from("stdout ok!\n"), stdout: String::from("stdout ok!\n"),
stderr: String::from("stderr ok!\n"), stderr: String::from("stderr ok!\n"),
@ -341,7 +340,7 @@ mod tests {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn exec_no_output() { fn exec_no_output() {
let result = internal_exec_cmd("true", &[]); let result = internal_exec_cmd("true", &[], Duration::from_millis(500));
let expected = Some(CommandOutput { let expected = Some(CommandOutput {
stdout: String::from(""), stdout: String::from(""),
stderr: String::from(""), stderr: String::from(""),
@ -353,7 +352,8 @@ mod tests {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn exec_with_output_stdout() { fn exec_with_output_stdout() {
let result = internal_exec_cmd("/bin/sh", &["-c", "echo hello"]); let result =
internal_exec_cmd("/bin/sh", &["-c", "echo hello"], Duration::from_millis(500));
let expected = Some(CommandOutput { let expected = Some(CommandOutput {
stdout: String::from("hello\n"), stdout: String::from("hello\n"),
stderr: String::from(""), stderr: String::from(""),
@ -365,7 +365,11 @@ mod tests {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn exec_with_output_stderr() { fn exec_with_output_stderr() {
let result = internal_exec_cmd("/bin/sh", &["-c", "echo hello >&2"]); let result = internal_exec_cmd(
"/bin/sh",
&["-c", "echo hello >&2"],
Duration::from_millis(500),
);
let expected = Some(CommandOutput { let expected = Some(CommandOutput {
stdout: String::from(""), stdout: String::from(""),
stderr: String::from("hello\n"), stderr: String::from("hello\n"),
@ -377,7 +381,11 @@ mod tests {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn exec_with_output_both() { fn exec_with_output_both() {
let result = internal_exec_cmd("/bin/sh", &["-c", "echo hello; echo world >&2"]); let result = internal_exec_cmd(
"/bin/sh",
&["-c", "echo hello; echo world >&2"],
Duration::from_millis(500),
);
let expected = Some(CommandOutput { let expected = Some(CommandOutput {
stdout: String::from("hello\n"), stdout: String::from("hello\n"),
stderr: String::from("world\n"), stderr: String::from("world\n"),
@ -389,7 +397,7 @@ mod tests {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn exec_with_non_zero_exit_code() { fn exec_with_non_zero_exit_code() {
let result = internal_exec_cmd("false", &[]); let result = internal_exec_cmd("false", &[], Duration::from_millis(500));
let expected = None; let expected = None;
assert_eq!(result, expected) assert_eq!(result, expected)
@ -398,7 +406,7 @@ mod tests {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn exec_slow_command() { fn exec_slow_command() {
let result = internal_exec_cmd("sleep", &["500"]); let result = internal_exec_cmd("sleep", &["500"], Duration::from_millis(500));
let expected = None; let expected = None;
assert_eq!(result, expected) assert_eq!(result, expected)