diff --git a/docs/config/README.md b/docs/config/README.md index 2ba70b45..b5be43da 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -243,6 +243,7 @@ $openstack\ $env_var\ $crystal\ $custom\ +$sudo\ $cmd_duration\ $line_break\ $jobs\ @@ -2947,6 +2948,58 @@ disabled = false ``` +## Sudo + +The `sudo` module displays if sudo credentials are currently cached. +The module will only be shown if credentials are cached. + +::: tip + +This module is disabled by default. +To enable it, set `disabled` to `false` in your configuration file. + +::: + +### Options + +| Option | Default | Description | +| -------------- | ----------------------- | ------------------------------------------------------------ | +| `format` | `[as $symbol]($style)"` | The format of the module | +| `symbol` | `"🧙 "` | The symbol displayed when credentials are cached | +| `style` | `"bold blue"` | The style for the module. | +| `allow_windows`| `false` | Since windows has no default sudo, default is disabled. | +| `disabled` | `true` | Disables the `sudo` module. | + +### Variables + +| Variable | Example | Description | +| --------- | ------- | ------------------------------------ | +| symbol | | Mirrors the value of option `symbol` | +| style\* | | Mirrors the value of option `style` | + +\*: This variable can only be used as a part of a style string + +### Example + +```toml + +# ~/.config/starship.toml + +[sudo] +style = "bold green" +symbol = "👩‍💻 " +disabled = false +``` + +```toml +# On windows +# $HOME\.starship\config.toml + +[sudo] +allow_windows = true +disabled = false +``` + ## Swift By default the `swift` module shows the currently installed version of [Swift](https://swift.org/). diff --git a/docs/presets/README.md b/docs/presets/README.md index 9ea120a5..90a084ba 100644 --- a/docs/presets/README.md +++ b/docs/presets/README.md @@ -226,6 +226,9 @@ format = '\[[$symbol($version)]($style)\]' [scala] format = '\[[$symbol($version)]($style)\]' +[sudo] +format = '\[[as $symbol]\] + [swift] format = '\[[$symbol($version)]($style)\]' @@ -375,6 +378,9 @@ symbol = "rs " [scala] symbol = "scala " +[sudo] +symbol = "sudo " + [swift] symbol = "swift " ``` diff --git a/src/configs/mod.rs b/src/configs/mod.rs index 86fe1554..e1434b66 100644 --- a/src/configs/mod.rs +++ b/src/configs/mod.rs @@ -61,6 +61,7 @@ pub mod shlvl; pub mod singularity; mod starship_root; pub mod status; +pub mod sudo; pub mod swift; pub mod terraform; pub mod time; @@ -138,6 +139,7 @@ pub struct FullConfig<'a> { shlvl: shlvl::ShLvlConfig<'a>, singularity: singularity::SingularityConfig<'a>, status: status::StatusConfig<'a>, + sudo: sudo::SudoConfig<'a>, swift: swift::SwiftConfig<'a>, terraform: terraform::TerraformConfig<'a>, time: time::TimeConfig<'a>, @@ -214,6 +216,7 @@ impl<'a> Default for FullConfig<'a> { shlvl: Default::default(), singularity: Default::default(), status: Default::default(), + sudo: Default::default(), swift: Default::default(), terraform: Default::default(), time: Default::default(), diff --git a/src/configs/starship_root.rs b/src/configs/starship_root.rs index b4c5bc82..a5b7f394 100644 --- a/src/configs/starship_root.rs +++ b/src/configs/starship_root.rs @@ -76,6 +76,7 @@ pub const PROMPT_ORDER: &[&str] = &[ "env_var", "crystal", "custom", + "sudo", "cmd_duration", "line_break", "jobs", diff --git a/src/configs/sudo.rs b/src/configs/sudo.rs new file mode 100644 index 00000000..c2a5b6ed --- /dev/null +++ b/src/configs/sudo.rs @@ -0,0 +1,25 @@ +use crate::config::ModuleConfig; + +use serde::Serialize; +use starship_module_config_derive::ModuleConfig; + +#[derive(Clone, ModuleConfig, Serialize)] +pub struct SudoConfig<'a> { + pub format: &'a str, + pub symbol: &'a str, + pub style: &'a str, + pub allow_windows: bool, + pub disabled: bool, +} + +impl<'a> Default for SudoConfig<'a> { + fn default() -> Self { + SudoConfig { + format: "[as $symbol]($style)", + symbol: "🧙 ", + style: "bold blue", + allow_windows: false, + disabled: true, + } + } +} diff --git a/src/module.rs b/src/module.rs index c886b6a3..6c7bfb97 100644 --- a/src/module.rs +++ b/src/module.rs @@ -65,6 +65,7 @@ pub const ALL_MODULES: &[&str] = &[ "shlvl", "singularity", "status", + "sudo", "swift", "terraform", "time", diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 61640f78..15ec1793 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -55,6 +55,7 @@ mod shell; mod shlvl; mod singularity; mod status; +mod sudo; mod swift; mod terraform; mod time; @@ -139,6 +140,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option> { "singularity" => singularity::module(context), "swift" => swift::module(context), "status" => status::module(context), + "sudo" => sudo::module(context), "terraform" => terraform::module(context), "time" => time::module(context), "crystal" => crystal::module(context), @@ -226,6 +228,7 @@ pub fn description(module: &str) -> &'static str { "shlvl" => "The current value of SHLVL", "singularity" => "The currently used Singularity image", "status" => "The status of the last command", + "sudo" => "The sudo credentials are currently cached", "swift" => "The currently installed version of Swift", "terraform" => "The currently selected terraform workspace and version", "time" => "The current local time", diff --git a/src/modules/sudo.rs b/src/modules/sudo.rs new file mode 100644 index 00000000..32daf871 --- /dev/null +++ b/src/modules/sudo.rs @@ -0,0 +1,113 @@ +use std::env; + +use super::{Context, Module, RootModuleConfig}; + +use crate::configs::sudo::SudoConfig; +use crate::formatter::StringFormatter; + +/// Creates a module with sudo credential cache status +pub fn module<'a>(context: &'a Context) -> Option> { + let mut module = context.new_module("sudo"); + let config = SudoConfig::try_load(module.config); + + if config.disabled { + return None; + } + + if !config.allow_windows && env::consts::FAMILY == "windows" { + return None; + } + + let is_sudo_cached = context.exec_cmd("sudo", &["-n", "true"]).is_some(); + + if !is_sudo_cached { + return None; + } + + let parsed = StringFormatter::new(config.format).and_then(|formatter| { + formatter + .map_meta(|variable, _| match variable { + "symbol" => Some(config.symbol), + _ => None, + }) + .map_style(|variable| match variable { + "style" => Some(Ok(config.style)), + _ => None, + }) + .parse(None, Some(context)) + }); + + module.set_segments(match parsed { + Ok(segments) => segments, + Err(error) => { + log::warn!("Error in module `sudo`:\n{}", error); + return None; + } + }); + + Some(module) +} + +#[cfg(test)] +mod tests { + use crate::{test::ModuleRenderer, utils::CommandOutput}; + use ansi_term::Color; + + #[test] + fn test_sudo_not_cached() { + let actual = ModuleRenderer::new("sudo") + .cmd("sudo -n true", None) + .config(toml::toml! { + [sudo] + disabled = false + allow_windows = true + }) + .collect(); + let expected = None; + + assert_eq!(expected, actual); + } + + #[test] + fn test_sudo_cached() { + let actual = ModuleRenderer::new("sudo") + .cmd( + "sudo -n true", + Some(CommandOutput { + stdout: "".to_owned(), + stderr: "".to_owned(), + }), + ) + .config(toml::toml! { + [sudo] + disabled = false + allow_windows = true + }) + .collect(); + let expected = Some(format!("{}", Color::Blue.bold().paint("as 🧙 "))); + + assert_eq!(expected, actual); + } + + #[test] + #[cfg(windows)] + fn test_allow_windows_disabled_blocks_windows() { + let actual = ModuleRenderer::new("sudo") + .cmd( + "sudo -n true", + Some(CommandOutput { + stdout: "".to_owned(), + stderr: "".to_owned(), + }), + ) + .config(toml::toml! { + [sudo] + disabled = false + allow_windows = false + }) + .collect(); + let expected = None; + + assert_eq!(expected, actual); + } +}