From 463ec260247fa0e62d2ea14e237681a499955392 Mon Sep 17 00:00:00 2001 From: Matan Kushner Date: Tue, 2 Jul 2019 16:12:53 -0400 Subject: [PATCH] feat: Add a `disabled` configuration option for modules (#86) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit โ€ข Add support for the disabled configuration option This will allow you to selectively disable modules that you don't want or need. ๐Ÿ˜„ โ€ข Overwrite starship configuration file path with STARSHIP_CONFIG environment variable โ€ข Write tests for the two configuration options that are available --- Cargo.lock | 1 + Cargo.toml | 1 + src/config.rs | 63 ++++++++++++++++++++++++++++++-- src/context.rs | 21 +++++++++-- src/main.rs | 50 +++++++++---------------- src/modules/battery.rs | 6 +-- src/modules/character.rs | 4 +- src/modules/directory.rs | 4 +- src/modules/git_branch.rs | 4 +- src/modules/git_status.rs | 4 +- src/modules/go.rs | 4 +- src/modules/line_break.rs | 4 +- src/modules/mod.rs | 24 ++++++------ src/modules/nodejs.rs | 4 +- src/modules/package.rs | 14 ++++--- src/modules/python.rs | 4 +- src/modules/rust.rs | 11 ++++-- src/modules/username.rs | 4 +- src/print.rs | 31 ++++++++-------- tests/fixtures/empty_config.toml | 0 tests/testsuite/common.rs | 45 +++++++++++++++++++++-- tests/testsuite/configuration.rs | 34 +++++++++++++++++ tests/testsuite/main.rs | 1 + tests/testsuite/username.rs | 6 +-- 24 files changed, 240 insertions(+), 104 deletions(-) create mode 100644 tests/fixtures/empty_config.toml create mode 100644 tests/testsuite/configuration.rs diff --git a/Cargo.lock b/Cargo.lock index ed807090..943c7b47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -818,6 +818,7 @@ dependencies = [ "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 64128220..097426fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ rayon = "1.1.0" pretty_env_logger = "0.3.0" log = "0.4.6" battery = "0.7.4" +lazy_static = "1.3.0" [dev-dependencies] tempfile = "3.1.0" diff --git a/src/config.rs b/src/config.rs index 7c0509a2..74ff447d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,5 @@ use crate::utils; +use std::env; use dirs::home_dir; @@ -20,9 +21,33 @@ impl Config { /// Create a config from a starship configuration file fn config_from_file() -> Option { - let file_path = home_dir()?.join(".config/starship.toml"); - let toml_content = utils::read_file(&file_path.to_str()?).ok()?; - log::trace!("Config file content: \n{}", &toml_content); + let file_path = match env::var("STARSHIP_CONFIG") { + Ok(path) => { + // Use $STARSHIP_CONFIG as the config path if available + log::debug!("STARSHIP_CONFIG is set: {}", &path); + path + } + Err(_) => { + // Default to using ~/.config/starhip.toml + log::debug!("STARSHIP_CONFIG is not set"); + let config_path = home_dir()?.join(".config/starship.toml"); + let config_path_str = config_path.to_str()?.to_owned(); + + log::debug!("Using default config path: {}", config_path_str); + config_path_str + } + }; + + let toml_content = match utils::read_file(&file_path) { + Ok(content) => { + log::trace!("Config file content: \n{}", &content); + Some(content) + } + Err(e) => { + log::debug!("Unable to read config file content: \n{}", &e); + None + } + }?; let config = toml::from_str(&toml_content).ok()?; log::debug!("Config found: \n{:?}", &config); @@ -40,3 +65,35 @@ impl Config { module_config } } + +/// Extends `toml::value::Table` with useful methods +pub trait TableExt { + fn get_as_bool(&self, key: &str) -> Option; +} + +impl TableExt for toml::value::Table { + /// Get a key from a module's configuration as a boolean + fn get_as_bool(&self, key: &str) -> Option { + self.get(key).map(toml::Value::as_bool).unwrap_or(None) + } +} + +mod tests { + use super::*; + + #[test] + fn table_get_as_bool() { + let mut table = toml::value::Table::new(); + + // Use with boolean value + table.insert("boolean".to_string(), toml::value::Value::Boolean(true)); + assert_eq!(table.get_as_bool("boolean"), Some(true)); + + // Use with string value + table.insert( + "string".to_string(), + toml::value::Value::String("true".to_string()), + ); + assert_eq!(table.get_as_bool("string"), None); + } +} diff --git a/src/context.rs b/src/context.rs index fd0ccc82..f62ea76c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,4 @@ -use crate::config::Config; +use crate::config::{Config, TableExt}; use crate::module::Module; use clap::ArgMatches; @@ -75,8 +75,23 @@ impl<'a> Context<'a> { dir } - pub fn new_module(&self, name: &str) -> Module { - Module::new(name, self.config.get_module_config(name)) + /// Create a new module + /// + /// Will return `None` if the module is disabled by configuration, by setting + /// the `disabled` key to `true` in the configuration for that module. + pub fn new_module(&self, name: &str) -> Option { + let config = self.config.get_module_config(name); + + // If the segment has "disabled" set to "true", don't show it + let disabled = config + .map(|table| table.get_as_bool("disabled")) + .unwrap_or(None); + + if disabled == Some(true) { + return None; + } + + Some(Module::new(name, config)) } // returns a new ScanDir struct with reference to current dir_files of context diff --git a/src/main.rs b/src/main.rs index 70f68e2e..371109e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,20 @@ use clap::{App, Arg, SubCommand}; fn main() { pretty_env_logger::init(); + let status_code_arg = Arg::with_name("status_code") + .short("s") + .long("status") + .value_name("STATUS_CODE") + .help("The status code of the previously run command") + .takes_value(true); + + let path_arg = Arg::with_name("path") + .short("p") + .long("path") + .value_name("PATH") + .help("The path that the prompt should render for") + .takes_value(true); + let matches = App::new("Starship") .about("The cross-shell prompt for astronauts. โœจ๐Ÿš€") // pull the version number from Cargo.toml @@ -24,22 +38,8 @@ fn main() { .subcommand( SubCommand::with_name("prompt") .about("Prints the full starship prompt") - .arg( - Arg::with_name("status_code") - .short("s") - .long("status") - .value_name("STATUS_CODE") - .help("The status code of the previously run command") - .takes_value(true), - ) - .arg( - Arg::with_name("path") - .short("p") - .long("path") - .value_name("PATH") - .help("The path that the prompt should render for ($PWD by default)") - .takes_value(true), - ), + .arg(&status_code_arg) + .arg(&path_arg), ) .subcommand( SubCommand::with_name("module") @@ -49,22 +49,8 @@ fn main() { .help("The name of the module to be printed") .required(true), ) - .arg( - Arg::with_name("status_code") - .short("s") - .long("status") - .value_name("STATUS_CODE") - .help("The status code of the previously run command") - .takes_value(true), - ) - .arg( - Arg::with_name("path") - .short("p") - .long("path") - .value_name("PATH") - .help("The path the prompt should render for ($PWD by default)") - .takes_value(true), - ), + .arg(&status_code_arg) + .arg(&path_arg), ) .get_matches(); diff --git a/src/modules/battery.rs b/src/modules/battery.rs index 75ac1c55..048abe05 100644 --- a/src/modules/battery.rs +++ b/src/modules/battery.rs @@ -3,7 +3,7 @@ use ansi_term::Color; use super::{Context, Module}; /// Creates a segment for the battery percentage and charging state -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { const BATTERY_FULL: &str = "โ€ข"; const BATTERY_CHARGING: &str = "โ‡ก"; const BATTERY_DISCHARGING: &str = "โ‡ฃ"; @@ -22,7 +22,7 @@ pub fn segment<'a>(context: &'a Context) -> Option> { } // TODO: Set style based on percentage when threshold is modifiable - let mut module = context.new_module("battery"); + let mut module = context.new_module("battery")?; module.set_style(Color::Red.bold()); module.get_prefix().set_value(""); @@ -61,7 +61,7 @@ fn get_battery_status() -> Option { Some(battery_status) } Some(Err(e)) => { - log::debug!("Unable to access battery information:\n{}", e); + log::debug!("Unable to access battery information:\n{}", &e); None } None => { diff --git a/src/modules/character.rs b/src/modules/character.rs index 1a55cf78..447fd759 100644 --- a/src/modules/character.rs +++ b/src/modules/character.rs @@ -9,12 +9,12 @@ use ansi_term::Color; /// (green by default) /// - If the exit-code was anything else, the arrow will be formatted with /// `COLOR_FAILURE` (red by default) -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { const PROMPT_CHAR: &str = "โžœ"; let color_success = Color::Green.bold(); let color_failure = Color::Red.bold(); - let mut module = context.new_module("char"); + let mut module = context.new_module("char")?; module.get_prefix().set_value(""); let symbol = module.new_segment("symbol", PROMPT_CHAR); diff --git a/src/modules/directory.rs b/src/modules/directory.rs index 8e719c45..a2cafac1 100644 --- a/src/modules/directory.rs +++ b/src/modules/directory.rs @@ -12,12 +12,12 @@ use super::{Context, Module}; /// /// **Truncation** /// Paths will be limited in length to `3` path components by default. -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { const HOME_SYMBOL: &str = "~"; const DIR_TRUNCATION_LENGTH: usize = 3; let module_color = Color::Cyan.bold(); - let mut module = context.new_module("directory"); + let mut module = context.new_module("directory")?; module.set_style(module_color); let current_dir = &context.current_dir; diff --git a/src/modules/git_branch.rs b/src/modules/git_branch.rs index 010c4ecc..d2861c47 100644 --- a/src/modules/git_branch.rs +++ b/src/modules/git_branch.rs @@ -5,13 +5,13 @@ use super::{Context, Module}; /// Creates a segment with the Git branch in the current directory /// /// Will display the branch name if the current directory is a git repo -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { let branch_name = context.branch_name.as_ref()?; const GIT_BRANCH_CHAR: &str = "๎‚  "; let segment_color = Color::Purple.bold(); - let mut module = context.new_module("git_branch"); + let mut module = context.new_module("git_branch")?; module.set_style(segment_color); module.get_prefix().set_value("on "); diff --git a/src/modules/git_status.rs b/src/modules/git_status.rs index 6db29ba1..c5ec57e4 100644 --- a/src/modules/git_status.rs +++ b/src/modules/git_status.rs @@ -17,7 +17,7 @@ use super::{Context, Module}; /// - `+` โ€” A new file has been added to the staging area /// - `ยป` โ€” A renamed file has been added to the staging area /// - `โœ˜` โ€” A file's deletion has been added to the staging area -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { // This is the order that the sections will appear in const GIT_STATUS_CONFLICTED: &str = "="; const GIT_STATUS_AHEAD: &str = "โ‡ก"; @@ -35,7 +35,7 @@ pub fn segment<'a>(context: &'a Context) -> Option> { let repository = Repository::open(repo_root).ok()?; let module_style = Color::Red.bold(); - let mut module = context.new_module("git_status"); + let mut module = context.new_module("git_status")?; module.get_prefix().set_value("[").set_style(module_style); module.get_suffix().set_value("] ").set_style(module_style); module.set_style(module_style); diff --git a/src/modules/go.rs b/src/modules/go.rs index e4b689af..a1b192e4 100644 --- a/src/modules/go.rs +++ b/src/modules/go.rs @@ -13,7 +13,7 @@ use super::{Context, Module}; /// - Current directory contains a `Gopkg.lock` file /// - Current directory contains a `.go` file /// - Current directory contains a `Godeps` directory -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { let is_go_project = context .new_scan_dir() .set_files(&["go.mod", "go.sum", "glide.yaml", "Gopkg.yml", "Gopkg.lock"]) @@ -30,7 +30,7 @@ pub fn segment<'a>(context: &'a Context) -> Option> { const GO_CHAR: &str = "๐Ÿน "; let module_color = Color::Cyan.bold(); - let mut module = context.new_module("go"); + let mut module = context.new_module("go")?; module.set_style(module_color); let formatted_version = format_go_version(go_version)?; diff --git a/src/modules/line_break.rs b/src/modules/line_break.rs index 0b11367e..b86f85e3 100644 --- a/src/modules/line_break.rs +++ b/src/modules/line_break.rs @@ -1,10 +1,10 @@ use super::{Context, Module}; /// Creates a segment for the line break -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { const LINE_ENDING: &str = "\n"; - let mut module = context.new_module("line_break"); + let mut module = context.new_module("line_break")?; module.get_prefix().set_value(""); module.get_suffix().set_value(""); diff --git a/src/modules/mod.rs b/src/modules/mod.rs index cf1c6401..30a459ef 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -16,18 +16,18 @@ use crate::module::Module; pub fn handle<'a>(module: &str, context: &'a Context) -> Option> { match module { - "dir" | "directory" => directory::segment(context), - "char" | "character" => character::segment(context), - "node" | "nodejs" => nodejs::segment(context), - "rust" | "rustlang" => rust::segment(context), - "python" => python::segment(context), - "go" | "golang" => go::segment(context), - "line_break" => line_break::segment(context), - "package" => package::segment(context), - "git_branch" => git_branch::segment(context), - "git_status" => git_status::segment(context), - "username" => username::segment(context), - "battery" => battery::segment(context), + "dir" | "directory" => directory::module(context), + "char" | "character" => character::module(context), + "node" | "nodejs" => nodejs::module(context), + "rust" | "rustlang" => rust::module(context), + "python" => python::module(context), + "go" | "golang" => go::module(context), + "line_break" => line_break::module(context), + "package" => package::module(context), + "git_branch" => git_branch::module(context), + "git_status" => git_status::module(context), + "username" => username::module(context), + "battery" => battery::module(context), _ => panic!("Unknown module: {}", module), } diff --git a/src/modules/nodejs.rs b/src/modules/nodejs.rs index e542acf1..9aa5a437 100644 --- a/src/modules/nodejs.rs +++ b/src/modules/nodejs.rs @@ -9,7 +9,7 @@ use super::{Context, Module}; /// - Current directory contains a `.js` file /// - Current directory contains a `package.json` file /// - Current directory contains a `node_modules` directory -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { let is_js_project = context .new_scan_dir() .set_files(&["package.json"]) @@ -26,7 +26,7 @@ pub fn segment<'a>(context: &'a Context) -> Option> { const NODE_CHAR: &str = "โฌข "; let module_color = Color::Green.bold(); - let mut module = context.new_module("node"); + let mut module = context.new_module("node")?; module.set_style(module_color); let formatted_version = node_version.trim(); diff --git a/src/modules/package.rs b/src/modules/package.rs index a9af6389..527d5eae 100644 --- a/src/modules/package.rs +++ b/src/modules/package.rs @@ -8,13 +8,13 @@ use toml; /// Creates a segment with the current package version /// /// Will display if a version is defined for your Node.js or Rust project (if one exists) -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { match get_package_version() { Some(package_version) => { const PACKAGE_CHAR: &str = "๐Ÿ“ฆ "; let module_color = Color::Red.bold(); - let mut module = context.new_module("package"); + let mut module = context.new_module("package")?; module.set_style(module_color); module.get_prefix().set_value("is "); @@ -75,19 +75,21 @@ mod tests { #[test] fn test_extract_cargo_version() { - let cargo_with_version = r#" + let cargo_with_version = toml::toml! { [package] name = "starship" version = "0.1.0" - "#; + } + .to_string(); let expected_version = Some("v0.1.0".to_string()); assert_eq!(extract_cargo_version(&cargo_with_version), expected_version); - let cargo_without_version = r#" + let cargo_without_version = toml::toml! { [package] name = "starship" - "#; + } + .to_string(); let expected_version = None; assert_eq!( diff --git a/src/modules/python.rs b/src/modules/python.rs index 8b72e518..a1a36cd3 100644 --- a/src/modules/python.rs +++ b/src/modules/python.rs @@ -10,7 +10,7 @@ use super::{Context, Module}; /// - Current directory contains a `.python-version` file /// - Current directory contains a `requirements.txt` file /// - Current directory contains a `pyproject.toml` file -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { let is_py_project = context .new_scan_dir() .set_files(&["requirements.txt", ".python-version", "pyproject.toml"]) @@ -26,7 +26,7 @@ pub fn segment<'a>(context: &'a Context) -> Option> { const PYTHON_CHAR: &str = "๐Ÿ "; let module_color = Color::Yellow.bold(); - let mut module = context.new_module("python"); + let mut module = context.new_module("python")?; module.set_style(module_color); let formatted_version = format_python_version(python_version); diff --git a/src/modules/rust.rs b/src/modules/rust.rs index 9ccdcc5d..05734406 100644 --- a/src/modules/rust.rs +++ b/src/modules/rust.rs @@ -8,7 +8,7 @@ use super::{Context, Module}; /// Will display the Rust version if any of the following criteria are met: /// - Current directory contains a file with a `.rs` extension /// - Current directory contains a `Cargo.toml` file -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { let is_rs_project = context .new_scan_dir() .set_files(&["Cargo.toml"]) @@ -24,7 +24,7 @@ pub fn segment<'a>(context: &'a Context) -> Option> { const RUST_CHAR: &str = "๐Ÿฆ€ "; let module_color = Color::Red.bold(); - let mut module = context.new_module("rust"); + let mut module = context.new_module("rust")?; module.set_style(module_color); let formatted_version = format_rustc_version(rust_version); @@ -38,14 +38,14 @@ pub fn segment<'a>(context: &'a Context) -> Option> { } fn get_rust_version() -> Option { - match Command::new("rustc").arg("-V").output() { + match Command::new("rustc").arg("--version").output() { Ok(output) => Some(String::from_utf8(output.stdout).unwrap()), Err(_) => None, } } fn format_rustc_version(mut rustc_stdout: String) -> String { - let offset = &rustc_stdout.find('(').unwrap(); + let offset = &rustc_stdout.find('(').unwrap_or(rustc_stdout.len()); let formatted_version: String = rustc_stdout.drain(..offset).collect(); format!("v{}", formatted_version.replace("rustc", "").trim()) @@ -65,5 +65,8 @@ mod tests { let stable_input = String::from("rustc 1.34.0 (91856ed52 2019-04-10)"); assert_eq!(format_rustc_version(stable_input), "v1.34.0"); + + let version_without_hash = String::from("rustc 1.34.0"); + assert_eq!(format_rustc_version(version_without_hash), "v1.34.0"); } } diff --git a/src/modules/username.rs b/src/modules/username.rs index 8885791b..73295b79 100644 --- a/src/modules/username.rs +++ b/src/modules/username.rs @@ -10,7 +10,7 @@ use super::{Context, Module}; /// - The current user isn't the same as the one that is logged in ($LOGNAME != $USER) /// - The current user is root (UID = 0) /// - The user is currently connected as an SSH session ($SSH_CONNECTION) -pub fn segment<'a>(context: &'a Context) -> Option> { +pub fn module<'a>(context: &'a Context) -> Option> { let user = env::var("USER").ok(); let logname = env::var("LOGNAME").ok(); let ssh_connection = env::var("SSH_CONNECTION").ok(); @@ -18,7 +18,7 @@ pub fn segment<'a>(context: &'a Context) -> Option> { let mut module_color = Color::Yellow.bold(); if user != logname || ssh_connection.is_some() || is_root(&mut module_color) { - let mut module = context.new_module("username"); + let mut module = context.new_module("username")?; module.set_style(module_color); module.new_segment("username", user?); diff --git a/src/print.rs b/src/print.rs index 71d453e3..ae712e12 100644 --- a/src/print.rs +++ b/src/print.rs @@ -6,21 +6,22 @@ use crate::context::Context; use crate::module::Module; use crate::modules; +const PROMPT_ORDER: &[&str] = &[ + "battery", + "username", + "directory", + "git_branch", + "git_status", + "package", + "nodejs", + "rust", + "python", + "go", + "line_break", + "character", +]; + pub fn prompt(args: ArgMatches) { - let prompt_order = vec![ - "battery", - "username", - "directory", - "git_branch", - "git_status", - "package", - "nodejs", - "rust", - "python", - "go", - "line_break", - "character", - ]; let context = Context::new(args); let stdout = io::stdout(); @@ -29,7 +30,7 @@ pub fn prompt(args: ArgMatches) { // Write a new line before the prompt writeln!(handle).unwrap(); - let modules = prompt_order + let modules = PROMPT_ORDER .par_iter() .map(|module| modules::handle(module, &context)) // Compute modules .flatten() diff --git a/tests/fixtures/empty_config.toml b/tests/fixtures/empty_config.toml new file mode 100644 index 00000000..e69de29b diff --git a/tests/testsuite/common.rs b/tests/testsuite/common.rs index 45188028..ee817d8d 100644 --- a/tests/testsuite/common.rs +++ b/tests/testsuite/common.rs @@ -1,15 +1,36 @@ +use lazy_static::lazy_static; +use std::io::prelude::*; +use std::path::{Path, PathBuf}; use std::{io, process}; -pub fn render_prompt() -> process::Command { +lazy_static! { + static ref MANIFEST_DIR: &'static Path = Path::new(env!("CARGO_MANIFEST_DIR")); + pub static ref FIXTURES_DIR: PathBuf = MANIFEST_DIR.join("tests/fixtures"); + static ref EMPTY_CONFIG: PathBuf = MANIFEST_DIR.join("empty_config.toml"); +} + +/// Run an instance of starship +fn run_starship() -> process::Command { let mut command = process::Command::new("./target/debug/starship"); - command.arg("prompt"); + + command + .arg("prompt") + .env_clear() + .env("PATH", env!("PATH")) // Provide the $PATH variable so that external programs are runnable + .env("STARSHIP_CONFIG", EMPTY_CONFIG.as_os_str()); command } pub fn render_module(module_name: &str) -> process::Command { let mut command = process::Command::new("./target/debug/starship"); - command.arg("module").arg(module_name); + + command + .arg("module") + .arg(module_name) + .env_clear() + .env("PATH", env!("PATH")) // Provide the $PATH variable so that external programs are runnable + .env("STARSHIP_CONFIG", EMPTY_CONFIG.as_os_str()); command } @@ -20,3 +41,21 @@ pub fn new_tempdir() -> io::Result { // "/var/folders", which provides us with restricted permissions (rwxr-xr-x) tempfile::tempdir_in("/tmp") } + +/// Extends `std::process::Command` with methods for testing +pub trait TestCommand { + fn use_config(&mut self, toml: toml::value::Value) -> &mut process::Command; +} + +impl TestCommand for process::Command { + /// Create a configuration file with the provided TOML and use it + fn use_config(&mut self, toml: toml::value::Value) -> &mut process::Command { + // Create a persistent config file in a tempdir + let (mut config_file, config_path) = + tempfile::NamedTempFile::new().unwrap().keep().unwrap(); + write!(config_file, "{}", toml.to_string()).unwrap(); + + // Set that newly-created file as the config for the prompt instance + self.env("STARSHIP_CONFIG", config_path) + } +} diff --git a/tests/testsuite/configuration.rs b/tests/testsuite/configuration.rs new file mode 100644 index 00000000..231e0b99 --- /dev/null +++ b/tests/testsuite/configuration.rs @@ -0,0 +1,34 @@ +use ansi_term::Color; +use std::io; + +use crate::common::{self, TestCommand}; + +#[test] +fn char_symbol_configuration() -> io::Result<()> { + let expected = format!("{} ", Color::Green.bold().paint("โฏ")); + + let output = common::render_module("char") + .use_config(toml::toml! { + [char] + symbol = "โฏ" + }) + .output()?; + let actual = String::from_utf8(output.stdout).unwrap(); + assert_eq!(expected, actual); + + Ok(()) +} + +#[test] +fn disabled_module() -> io::Result<()> { + let output = common::render_module("package") + .use_config(toml::toml! { + [package] + disabled = true + }) + .output()?; + let actual = String::from_utf8(output.stdout).unwrap(); + assert_eq!("", actual); + + Ok(()) +} diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index 1705ae3a..d7989e7a 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -1,5 +1,6 @@ mod character; mod common; +mod configuration; mod directory; mod golang; mod line_break; diff --git a/tests/testsuite/username.rs b/tests/testsuite/username.rs index ed71e323..49b1d637 100644 --- a/tests/testsuite/username.rs +++ b/tests/testsuite/username.rs @@ -8,7 +8,7 @@ use crate::common; #[test] fn no_env_variables() -> io::Result<()> { - let output = common::render_module("username").env_clear().output()?; + let output = common::render_module("username").output()?; let actual = String::from_utf8(output.stdout).unwrap(); assert_eq!("", actual); Ok(()) @@ -17,7 +17,6 @@ fn no_env_variables() -> io::Result<()> { #[test] fn logname_equals_user() -> io::Result<()> { let output = common::render_module("username") - .env_clear() .env("LOGNAME", "astronaut") .env("USER", "astronaut") .output()?; @@ -30,7 +29,6 @@ fn logname_equals_user() -> io::Result<()> { fn ssh_wo_username() -> io::Result<()> { // SSH connection w/o username let output = common::render_module("username") - .env_clear() .env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22") .output()?; let actual = String::from_utf8(output.stdout).unwrap(); @@ -41,7 +39,6 @@ fn ssh_wo_username() -> io::Result<()> { #[test] fn current_user_not_logname() -> io::Result<()> { let output = common::render_module("username") - .env_clear() .env("LOGNAME", "astronaut") .env("USER", "cosmonaut") .output()?; @@ -55,7 +52,6 @@ fn current_user_not_logname() -> io::Result<()> { #[test] fn ssh_connection() -> io::Result<()> { let output = common::render_module("username") - .env_clear() .env("USER", "astronaut") .env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22") .output()?;