feat: Add a `disabled` configuration option for modules (#86)
• 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
This commit is contained in:
parent
2440ed60d0
commit
463ec26024
|
@ -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)",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<toml::value::Table> {
|
||||
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<bool>;
|
||||
}
|
||||
|
||||
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<bool> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Module> {
|
||||
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
|
||||
|
|
50
src/main.rs
50
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();
|
||||
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
const BATTERY_FULL: &str = "•";
|
||||
const BATTERY_CHARGING: &str = "⇡";
|
||||
const BATTERY_DISCHARGING: &str = "⇣";
|
||||
|
@ -22,7 +22,7 @@ pub fn segment<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
|
||||
// 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<BatteryStatus> {
|
|||
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 => {
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
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);
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
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;
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
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 ");
|
||||
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
// 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<Module<'a>> {
|
|||
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);
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
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<Module<'a>> {
|
|||
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)?;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use super::{Context, Module};
|
||||
|
||||
/// Creates a segment for the line break
|
||||
pub fn segment<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
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("");
|
||||
|
|
|
@ -16,18 +16,18 @@ use crate::module::Module;
|
|||
|
||||
pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
|
||||
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),
|
||||
}
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let is_js_project = context
|
||||
.new_scan_dir()
|
||||
.set_files(&["package.json"])
|
||||
|
@ -26,7 +26,7 @@ pub fn segment<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
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();
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
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!(
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
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<Module<'a>> {
|
|||
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);
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let is_rs_project = context
|
||||
.new_scan_dir()
|
||||
.set_files(&["Cargo.toml"])
|
||||
|
@ -24,7 +24,7 @@ pub fn segment<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
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<Module<'a>> {
|
|||
}
|
||||
|
||||
fn get_rust_version() -> Option<String> {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Module<'a>> {
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
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<Module<'a>> {
|
|||
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?);
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ use crate::context::Context;
|
|||
use crate::module::Module;
|
||||
use crate::modules;
|
||||
|
||||
pub fn prompt(args: ArgMatches) {
|
||||
let prompt_order = vec![
|
||||
const PROMPT_ORDER: &[&str] = &[
|
||||
"battery",
|
||||
"username",
|
||||
"directory",
|
||||
|
@ -20,7 +19,9 @@ pub fn prompt(args: ArgMatches) {
|
|||
"go",
|
||||
"line_break",
|
||||
"character",
|
||||
];
|
||||
];
|
||||
|
||||
pub fn prompt(args: ArgMatches) {
|
||||
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()
|
||||
|
|
|
@ -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<tempfile::TempDir> {
|
|||
// "/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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
mod character;
|
||||
mod common;
|
||||
mod configuration;
|
||||
mod directory;
|
||||
mod golang;
|
||||
mod line_break;
|
||||
|
|
|
@ -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()?;
|
||||
|
|
Loading…
Reference in New Issue