From b8a812b93207da992ff8ae9f8b955bfa1252072b Mon Sep 17 00:00:00 2001 From: Mick Hohmann Date: Wed, 20 Mar 2024 22:47:32 +0100 Subject: [PATCH] feat(username): add detect_env_vars as option (#5833) * Added the option "detect_env_vars" to the `username` module with the same functionality as in the `hostname` module. * Fixed logic error and added test to catch it * build(deps): update dependency vitepress to ^1.0.0-rc.45 * build(deps): update rust crate shadow-rs to 0.27.1 * Added the option "detect_env_vars" to the `username` module with the same functionality as in the `hostname` module. * Fixed logic error and added test to catch it * Removed unused gix import * Removed unused gix import, again * Removed unused gix import. Next try --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/config-schema.json | 8 ++++ docs/config/README.md | 29 ++++++++++--- src/configs/username.rs | 2 + src/modules/username.rs | 88 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 117 insertions(+), 10 deletions(-) diff --git a/.github/config-schema.json b/.github/config-schema.json index e2bade18..f293dd26 100644 --- a/.github/config-schema.json +++ b/.github/config-schema.json @@ -1717,6 +1717,7 @@ }, "username": { "default": { + "detect_env_vars": [], "disabled": false, "format": "[$user]($style) in ", "show_always": false, @@ -5878,6 +5879,13 @@ "UsernameConfig": { "type": "object", "properties": { + "detect_env_vars": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, "format": { "default": "[$user]($style) in ", "type": "string" diff --git a/docs/config/README.md b/docs/config/README.md index 1c6f626a..34beb729 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -4357,6 +4357,7 @@ The module will be shown if any of the following conditions are met: - The current user isn't the same as the one that is logged in - The user is currently connected as an SSH session - The variable `show_always` is set to true +- The array `detect_env_vars` contains at least the name of one environment variable, that is set ::: tip @@ -4368,13 +4369,14 @@ these variables, one workaround is to set one of them with a dummy value. ### Options -| Option | Default | Description | -| ------------- | ----------------------- | ------------------------------------------- | -| `style_root` | `'bold red'` | The style used when the user is root/admin. | -| `style_user` | `'bold yellow'` | The style used for non-root users. | -| `format` | `'[$user]($style) in '` | The format for the module. | -| `show_always` | `false` | Always shows the `username` module. | -| `disabled` | `false` | Disables the `username` module. | +| Option | Default | Description | +| ----------------- | ----------------------- | --------------------------------------------------------- | +| `style_root` | `'bold red'` | The style used when the user is root/admin. | +| `style_user` | `'bold yellow'` | The style used for non-root users. | +| `detect_env_vars` | `[]` | Which environment variable(s) should trigger this module. | +| `format` | `'[$user]($style) in '` | The format for the module. | +| `show_always` | `false` | Always shows the `username` module. | +| `disabled` | `false` | Disables the `username` module. | ### Variables @@ -4385,6 +4387,8 @@ these variables, one workaround is to set one of them with a dummy value. ### Example +#### Always show the hostname + ```toml # ~/.config/starship.toml @@ -4396,6 +4400,17 @@ disabled = false show_always = true ``` +#### Hide the hostname in remote tmux sessions + +```toml +# ~/.config/starship.toml + +[hostname] +ssh_only = false +detect_env_vars = ['!TMUX', 'SSH_CONNECTION'] +disabled = false +``` + ## Vagrant The `vagrant` module shows the currently installed version of [Vagrant](https://www.vagrantup.com/). diff --git a/src/configs/username.rs b/src/configs/username.rs index 1dbcf4a3..807dad51 100644 --- a/src/configs/username.rs +++ b/src/configs/username.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; )] #[serde(default)] pub struct UsernameConfig<'a> { + pub detect_env_vars: Vec<&'a str>, pub format: &'a str, pub style_root: &'a str, pub style_user: &'a str, @@ -18,6 +19,7 @@ pub struct UsernameConfig<'a> { impl<'a> Default for UsernameConfig<'a> { fn default() -> Self { UsernameConfig { + detect_env_vars: vec![], format: "[$user]($style) in ", style_root: "red bold", style_user: "yellow bold", diff --git a/src/modules/username.rs b/src/modules/username.rs index af3fa6e5..038bbd75 100644 --- a/src/modules/username.rs +++ b/src/modules/username.rs @@ -15,23 +15,29 @@ const USERNAME_ENV_VAR: &str = "USERNAME"; /// - The current user is root (UID = 0) [1] /// - The current user isn't the same as the one that is logged in (`$LOGNAME` != `$USER`) [2] /// - The user is currently connected as an SSH session (`$SSH_CONNECTION`) [3] +/// - The option `username.detect_env_vars` is set with a not negated environment variable [4] +/// Does not display the username: +/// - If the option `username.detect_env_vars` is set with a negated environment variable [A] pub fn module<'a>(context: &'a Context) -> Option> { let mut username = context.get_env(USERNAME_ENV_VAR)?; let mut module = context.new_module("username"); let config: UsernameConfig = UsernameConfig::try_load(module.config); + let has_detected_env_var = context.detect_env_vars(&config.detect_env_vars); let is_root = is_root_user(); if cfg!(target_os = "windows") && is_root { username = "Administrator".to_string(); } + let show_username = config.show_always || is_root // [1] || !is_login_user(context, &username) // [2] - || is_ssh_session(context); // [3] + || is_ssh_session(context) // [3] + || ( !config.detect_env_vars.is_empty() && has_detected_env_var ); // [4] - if !show_username { - return None; + if !show_username || !has_detected_env_var { + return None; // [A] } let parsed = StringFormatter::new(config.format).and_then(|formatter| { @@ -109,11 +115,69 @@ fn is_ssh_session(context: &Context) -> bool { #[cfg(test)] mod tests { + use gix::config::key; + use crate::test::ModuleRenderer; // TODO: Add tests for if root user (UID == 0) // Requires mocking + #[test] + fn ssh_with_empty_detect_env_vars() { + let actual = ModuleRenderer::new("username") + .env(super::USERNAME_ENV_VAR, "astronaut") + .env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22") + // Test output should not change when run by root/non-root user + .config(toml::toml! { + [username] + style_root = "" + style_user = "" + detect_env_vars = [] + }) + .collect(); + + let expected = Some("astronaut in "); + assert_eq!(expected, actual.as_deref()); + } + + #[test] + fn ssh_with_matching_detect_env_vars() { + let actual = ModuleRenderer::new("username") + .env(super::USERNAME_ENV_VAR, "astronaut") + .env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22") + .env("FORCE_USERNAME", "true") + // Test output should not change when run by root/non-root user + .config(toml::toml! { + [username] + style_root = "" + style_user = "" + detect_env_vars = ["FORCE_USERNAME"] + }) + .collect(); + + let expected = Some("astronaut in "); + assert_eq!(expected, actual.as_deref()); + } + + #[test] + fn ssh_with_matching_negated_detect_env_vars() { + let actual = ModuleRenderer::new("username") + .env(super::USERNAME_ENV_VAR, "astronaut") + .env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22") + .env("NEGATED", "true") + // Test output should not change when run by root/non-root user + .config(toml::toml! { + [username] + style_root = "" + style_user = "" + detect_env_vars = ["!NEGATED"] + }) + .collect(); + + let expected = None; + assert_eq!(expected, actual.as_deref()); + } + #[test] fn no_env_variables() { let actual = ModuleRenderer::new("username").collect(); @@ -241,4 +305,22 @@ mod tests { assert_eq!(expected, actual.as_deref()); } + + #[test] + fn show_always_false() { + let actual = ModuleRenderer::new("username") + .env(super::USERNAME_ENV_VAR, "astronaut") + // Test output should not change when run by root/non-root user + .config(toml::toml! { + [username] + show_always = false + + style_root = "" + style_user = "" + }) + .collect(); + let expected = None; + + assert_eq!(expected, actual.as_deref()); + } }