Skip to content

Commit

Permalink
Add support for env_var.VAR in format
Browse files Browse the repository at this point in the history
  • Loading branch information
segevfiner committed Jan 1, 2022
1 parent 4deaa02 commit 6c58463
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 55 deletions.
15 changes: 8 additions & 7 deletions docs/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1066,13 +1066,14 @@ default = "unknown user"

### Options

| Option | Default | Description |
| ---------- | ------------------------------ | ---------------------------------------------------------------------------- |
| `symbol` | `""` | The symbol used before displaying the variable value. |
| `variable` | | The environment variable to be displayed. |
| `default` | | The default value to be displayed when the selected variable is not defined. |
| `format` | `"with [$env_value]($style) "` | The format for the module. |
| `disabled` | `false` | Disables the `env_var` module. |
| Option | Default | Description |
| ------------- | ------------------------------ | ---------------------------------------------------------------------------- |
| `symbol` | `""` | The symbol used before displaying the variable value. |
| `variable` | | The environment variable to be displayed. |
| `default` | | The default value to be displayed when the selected variable is not defined. |
| `format` | `"with [$env_value]($style) "` | The format for the module. |
| `description` | `"<env_var module>"` | The description of the module that is shown when running `starship explain`. | |
| `disabled` | `false` | Disables the `env_var` module. | |

### Variables

Expand Down
31 changes: 29 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,13 +343,40 @@ impl StarshipConfig {
module_config
}

/// Get the subset of the table for a env_var module by its name
pub fn get_env_var_module_config(&self, module_name: &str) -> Option<&Value> {
let config_path = if !module_name.is_empty() {
vec!["env_var", module_name]
} else {
vec!["env_var"]
};
let module_config = self.get_config(&config_path);
if module_config.is_some() {
log::debug!(
"env_var config found for \"{}\": {:?}",
&module_name,
&module_config
);
}
module_config
}

/// Get the table of all the registered custom modules, if any
pub fn get_custom_modules(&self) -> Option<&toml::value::Table> {
self.get_config(&["custom"])?.as_table()
}
/// Get the table of all the registered env_var modules, if any
pub fn get_env_var_modules(&self) -> Option<&toml::value::Table> {
self.get_config(&["env_var"])?.as_table()
pub fn get_env_var_modules(&self) -> Option<toml::value::Table> {
let config_table = self.get_config(&["env_var"])?.as_table()?;

// Old configuration is present in starship configuration
if config_table.iter().any(|(_, config)| !config.is_table()) {
let mut wrapper_config_table = toml::value::Table::new();
wrapper_config_table.insert("".to_string(), toml::Value::Table(config_table.clone()));
return Some(wrapper_config_table);
}

Some(config_table.clone())
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/configs/env_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct EnvVarConfig<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<&'a str>,
pub format: &'a str,
pub description: &'a str,
pub disabled: bool,
}

Expand All @@ -23,6 +24,7 @@ impl<'a> Default for EnvVarConfig<'a> {
variable: None,
default: None,
format: "with [$env_value]($style) ",
description: "<env_var module>",
disabled: false,
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,15 @@ impl<'a> Context<'a> {
Some(disabled == Some(true))
}

/// Return whether the specified env_var module has a `disabled` option set to true.
/// If it doesn't exist, `None` is returned.
pub fn is_env_var_module_disabled_in_config(&self, name: &str) -> Option<bool> {
let config = self.config.get_env_var_module_config(name)?;
let disabled = Some(config).and_then(|table| table.as_table()?.get("disabled")?.as_bool());

Some(disabled == Some(true))
}

// returns a new ScanDir struct with reference to current dir_files of context
// see ScanDir for methods
pub fn try_begin_scan(&'a self) -> Option<ScanDir<'a>> {
Expand Down
1 change: 0 additions & 1 deletion src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ pub const ALL_MODULES: &[&str] = &[
"dotnet",
"elixir",
"elm",
"env_var",
"erlang",
"fill",
"gcloud",
Expand Down
72 changes: 30 additions & 42 deletions src/modules/env_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,25 @@ use super::{Context, Module};
use crate::config::RootModuleConfig;
use crate::configs::env_var::EnvVarConfig;
use crate::formatter::StringFormatter;
use crate::segment::Segment;

/// Creates env_var_module displayer which displays all configured environmental variables
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let config_table = context.config.get_env_var_modules()?;
let mut env_modules = config_table
.iter()
.filter(|(_, config)| config.is_table())
.filter_map(|(variable, _)| env_var_module(vec!["env_var", variable], context))
.collect::<Vec<Module>>();
// Old configuration is present in starship configuration
if config_table.iter().any(|(_, config)| !config.is_table()) {
if let Some(fallback_env_var_module) = env_var_module(vec!["env_var"], context) {
env_modules.push(fallback_env_var_module);
}
}
Some(env_var_displayer(env_modules, context))
}

/// A utility module to display multiple env_variable modules
fn env_var_displayer<'a>(modules: Vec<Module>, context: &'a Context) -> Module<'a> {
let mut module = context.new_module("env_var_displayer");

let module_segments = modules
.into_iter()
.flat_map(|module| module.segments)
.collect::<Vec<Segment>>();
module.set_segments(module_segments);
module
}

/// Creates a module with the value of the chosen environment variable
///
/// Will display the environment variable's value if all of the following criteria are met:
/// - env_var.disabled is absent or false
/// - env_var.variable is defined
/// - a variable named as the value of env_var.variable is defined
fn env_var_module<'a>(module_config_path: Vec<&str>, context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module(&module_config_path.join("."));
let config_value = context.config.get_config(&module_config_path);
let config = EnvVarConfig::load(config_value.expect(
pub fn module<'a>(name: &str, context: &'a Context) -> Option<Module<'a>> {
let toml_config = context.config.get_env_var_module_config(name).expect(
"modules::env_var::module should only be called after ensuring that the module exists",
));
);
let config: EnvVarConfig = EnvVarConfig::load(toml_config);

if config.disabled {
return None;
};

let variable_name = get_variable_name(module_config_path, &config);
let mut module = Module::new(
&format!("env_var.{}", name),
config.description,
Some(toml_config),
);

let variable_name = get_variable_name(vec!["env_var", name], &config);

let env_value = get_env_value(context, variable_name?, config.default)?;
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
Expand All @@ -74,7 +44,7 @@ fn env_var_module<'a>(module_config_path: Vec<&str>, context: &'a Context) -> Op
module.set_segments(match parsed {
Ok(segments) => segments,
Err(error) => {
log::warn!("Error in module `env_var`:\n{}", error);
log::warn!("Error in module `env_var.{}`:\n{}", name, error);
return None;
}
});
Expand Down Expand Up @@ -253,6 +223,24 @@ mod test {
assert_eq!(expected, actual);
}

#[test]
fn display_single_with_multiple_defined() {
let actual = ModuleRenderer::new("env_var.TEST_VAR")
.config(toml::toml! {
[env_var.TEST_VAR]
[env_var.TEST_VAR2]
})
.env("TEST_VAR", TEST_VAR_VALUE)
.env("TEST_VAR2", TEST_VAR_VALUE)
.collect();
let expected = Some(format!(
"with {} ",
style().paint(TEST_VAR_VALUE),
));

assert_eq!(expected, actual);
}

fn style() -> Style {
// default style
Color::Black.bold().dimmed()
Expand Down
3 changes: 1 addition & 2 deletions src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod docker_context;
mod dotnet;
mod elixir;
mod elm;
mod env_var;
pub(crate) mod env_var;
mod erlang;
mod fill;
mod gcloud;
Expand Down Expand Up @@ -101,7 +101,6 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
"elixir" => elixir::module(context),
"elm" => elm::module(context),
"erlang" => erlang::module(context),
"env_var" => env_var::module(context),
"fill" => fill::module(context),
"gcloud" => gcloud::module(context),
"git_branch" => git_branch::module(context),
Expand Down
68 changes: 67 additions & 1 deletion src/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,13 @@ pub fn module(module_name: &str, args: ArgMatches) {
}

pub fn get_module(module_name: &str, context: Context) -> Option<String> {
modules::handle(module_name, &context).map(|m| m.to_string())
Some(
handle_module(module_name, &context, &BTreeSet::new())
.iter()
.map(|m| m.to_string())
.collect::<Vec<String>>()
.join(""),
)
}

pub fn timings(args: ArgMatches) {
Expand Down Expand Up @@ -313,13 +319,20 @@ fn handle_module<'a>(
module_list: &BTreeSet<String>,
) -> Vec<Module<'a>> {
struct DebugCustomModules<'tmp>(&'tmp toml::value::Table);
struct DebugEnvVarModules<'tmp>(&'tmp toml::value::Table);

impl Debug for DebugCustomModules<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.0.keys()).finish()
}
}

impl Debug for DebugEnvVarModules<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.0.keys()).finish()
}
}

let mut modules: Vec<Module> = Vec::new();

if ALL_MODULES.contains(&module) {
Expand Down Expand Up @@ -356,6 +369,37 @@ fn handle_module<'a>(
),
},
}
} else if module == "env_var" {
// Write out all env_var modules, except for those that are explicitly set
if let Some(env_var_modules) = context.config.get_env_var_modules() {
let env_var_modules = env_var_modules
.iter()
.filter_map(|(env_var_module, config)| {
if should_add_implicit_env_var_module(env_var_module, config, module_list) {
modules::env_var::module(env_var_module, context)
} else {
None
}
});
modules.extend(env_var_modules);
}
} else if let Some(module) = module.strip_prefix("env_var.") {
// Write out a custom module if it isn't disabled (and it exists...)
match context.is_env_var_module_disabled_in_config(module) {
Some(true) => (), // Module is disabled, we don't add it to the prompt
Some(false) => modules.extend(modules::env_var::module(module, context)),
None => match context.config.get_env_var_modules() {
Some(modules) => log::debug!(
"top level format contains env_var module \"{}\", but no configuration was provided. Configuration for the following modules were provided: {:?}",
module,
DebugEnvVarModules(&modules),
),
None => log::debug!(
"top level format contains env_var module \"{}\", but no configuration was provided.",
module,
),
},
}
} else {
log::debug!(
"Expected top level format to contain value from {:?}. Instead received {}",
Expand Down Expand Up @@ -389,6 +433,28 @@ fn should_add_implicit_custom_module(
.unwrap_or(false)
}

fn should_add_implicit_env_var_module(
env_var_module: &str,
config: &toml::Value,
module_list: &BTreeSet<String>,
) -> bool {
let explicit_module_name = format!("env_var.{}", env_var_module);
let is_explicitly_specified = module_list.contains(&explicit_module_name);

if is_explicitly_specified {
// The module is already specified explicitly, so we skip it
return false;
}

let false_value = toml::Value::Boolean(false);

!config
.get("disabled")
.unwrap_or(&false_value)
.as_bool()
.unwrap_or(false)
}

pub fn format_duration(duration: &Duration) -> String {
let milis = duration.as_millis();
if milis == 0 {
Expand Down

0 comments on commit 6c58463

Please sign in to comment.