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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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"
|
pretty_env_logger = "0.3.0"
|
||||||
log = "0.4.6"
|
log = "0.4.6"
|
||||||
battery = "0.7.4"
|
battery = "0.7.4"
|
||||||
|
lazy_static = "1.3.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
|
|
||||||
|
@ -20,9 +21,33 @@ impl Config {
|
||||||
|
|
||||||
/// Create a config from a starship configuration file
|
/// Create a config from a starship configuration file
|
||||||
fn config_from_file() -> Option<toml::value::Table> {
|
fn config_from_file() -> Option<toml::value::Table> {
|
||||||
let file_path = home_dir()?.join(".config/starship.toml");
|
let file_path = match env::var("STARSHIP_CONFIG") {
|
||||||
let toml_content = utils::read_file(&file_path.to_str()?).ok()?;
|
Ok(path) => {
|
||||||
log::trace!("Config file content: \n{}", &toml_content);
|
// 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()?;
|
let config = toml::from_str(&toml_content).ok()?;
|
||||||
log::debug!("Config found: \n{:?}", &config);
|
log::debug!("Config found: \n{:?}", &config);
|
||||||
|
@ -40,3 +65,35 @@ impl Config {
|
||||||
module_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 crate::module::Module;
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
@ -75,8 +75,23 @@ impl<'a> Context<'a> {
|
||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_module(&self, name: &str) -> Module {
|
/// Create a new module
|
||||||
Module::new(name, self.config.get_module_config(name))
|
///
|
||||||
|
/// 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
|
// 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() {
|
fn main() {
|
||||||
pretty_env_logger::init();
|
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")
|
let matches = App::new("Starship")
|
||||||
.about("The cross-shell prompt for astronauts. ✨🚀")
|
.about("The cross-shell prompt for astronauts. ✨🚀")
|
||||||
// pull the version number from Cargo.toml
|
// pull the version number from Cargo.toml
|
||||||
|
@ -24,22 +38,8 @@ fn main() {
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("prompt")
|
SubCommand::with_name("prompt")
|
||||||
.about("Prints the full starship prompt")
|
.about("Prints the full starship prompt")
|
||||||
.arg(
|
.arg(&status_code_arg)
|
||||||
Arg::with_name("status_code")
|
.arg(&path_arg),
|
||||||
.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),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("module")
|
SubCommand::with_name("module")
|
||||||
|
@ -49,22 +49,8 @@ fn main() {
|
||||||
.help("The name of the module to be printed")
|
.help("The name of the module to be printed")
|
||||||
.required(true),
|
.required(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(&status_code_arg)
|
||||||
Arg::with_name("status_code")
|
.arg(&path_arg),
|
||||||
.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),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use ansi_term::Color;
|
||||||
use super::{Context, Module};
|
use super::{Context, Module};
|
||||||
|
|
||||||
/// Creates a segment for the battery percentage and charging state
|
/// 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_FULL: &str = "•";
|
||||||
const BATTERY_CHARGING: &str = "⇡";
|
const BATTERY_CHARGING: &str = "⇡";
|
||||||
const BATTERY_DISCHARGING: &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
|
// 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.set_style(Color::Red.bold());
|
||||||
module.get_prefix().set_value("");
|
module.get_prefix().set_value("");
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ fn get_battery_status() -> Option<BatteryStatus> {
|
||||||
Some(battery_status)
|
Some(battery_status)
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
log::debug!("Unable to access battery information:\n{}", e);
|
log::debug!("Unable to access battery information:\n{}", &e);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -9,12 +9,12 @@ use ansi_term::Color;
|
||||||
/// (green by default)
|
/// (green by default)
|
||||||
/// - If the exit-code was anything else, the arrow will be formatted with
|
/// - If the exit-code was anything else, the arrow will be formatted with
|
||||||
/// `COLOR_FAILURE` (red by default)
|
/// `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 = "➜";
|
const PROMPT_CHAR: &str = "➜";
|
||||||
let color_success = Color::Green.bold();
|
let color_success = Color::Green.bold();
|
||||||
let color_failure = Color::Red.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("");
|
module.get_prefix().set_value("");
|
||||||
|
|
||||||
let symbol = module.new_segment("symbol", PROMPT_CHAR);
|
let symbol = module.new_segment("symbol", PROMPT_CHAR);
|
||||||
|
|
|
@ -12,12 +12,12 @@ use super::{Context, Module};
|
||||||
///
|
///
|
||||||
/// **Truncation**
|
/// **Truncation**
|
||||||
/// Paths will be limited in length to `3` path components by default.
|
/// 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 HOME_SYMBOL: &str = "~";
|
||||||
const DIR_TRUNCATION_LENGTH: usize = 3;
|
const DIR_TRUNCATION_LENGTH: usize = 3;
|
||||||
let module_color = Color::Cyan.bold();
|
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);
|
module.set_style(module_color);
|
||||||
|
|
||||||
let current_dir = &context.current_dir;
|
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
|
/// Creates a segment with the Git branch in the current directory
|
||||||
///
|
///
|
||||||
/// Will display the branch name if the current directory is a git repo
|
/// 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()?;
|
let branch_name = context.branch_name.as_ref()?;
|
||||||
|
|
||||||
const GIT_BRANCH_CHAR: &str = " ";
|
const GIT_BRANCH_CHAR: &str = " ";
|
||||||
let segment_color = Color::Purple.bold();
|
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.set_style(segment_color);
|
||||||
module.get_prefix().set_value("on ");
|
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 new file has been added to the staging area
|
||||||
/// - `»` — A renamed 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
|
/// - `✘` — 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
|
// This is the order that the sections will appear in
|
||||||
const GIT_STATUS_CONFLICTED: &str = "=";
|
const GIT_STATUS_CONFLICTED: &str = "=";
|
||||||
const GIT_STATUS_AHEAD: &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 repository = Repository::open(repo_root).ok()?;
|
||||||
|
|
||||||
let module_style = Color::Red.bold();
|
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_prefix().set_value("[").set_style(module_style);
|
||||||
module.get_suffix().set_value("] ").set_style(module_style);
|
module.get_suffix().set_value("] ").set_style(module_style);
|
||||||
module.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 `Gopkg.lock` file
|
||||||
/// - Current directory contains a `.go` file
|
/// - Current directory contains a `.go` file
|
||||||
/// - Current directory contains a `Godeps` directory
|
/// - 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
|
let is_go_project = context
|
||||||
.new_scan_dir()
|
.new_scan_dir()
|
||||||
.set_files(&["go.mod", "go.sum", "glide.yaml", "Gopkg.yml", "Gopkg.lock"])
|
.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 = "🐹 ";
|
const GO_CHAR: &str = "🐹 ";
|
||||||
let module_color = Color::Cyan.bold();
|
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);
|
module.set_style(module_color);
|
||||||
|
|
||||||
let formatted_version = format_go_version(go_version)?;
|
let formatted_version = format_go_version(go_version)?;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use super::{Context, Module};
|
use super::{Context, Module};
|
||||||
|
|
||||||
/// Creates a segment for the line break
|
/// 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";
|
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_prefix().set_value("");
|
||||||
module.get_suffix().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>> {
|
pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
|
||||||
match module {
|
match module {
|
||||||
"dir" | "directory" => directory::segment(context),
|
"dir" | "directory" => directory::module(context),
|
||||||
"char" | "character" => character::segment(context),
|
"char" | "character" => character::module(context),
|
||||||
"node" | "nodejs" => nodejs::segment(context),
|
"node" | "nodejs" => nodejs::module(context),
|
||||||
"rust" | "rustlang" => rust::segment(context),
|
"rust" | "rustlang" => rust::module(context),
|
||||||
"python" => python::segment(context),
|
"python" => python::module(context),
|
||||||
"go" | "golang" => go::segment(context),
|
"go" | "golang" => go::module(context),
|
||||||
"line_break" => line_break::segment(context),
|
"line_break" => line_break::module(context),
|
||||||
"package" => package::segment(context),
|
"package" => package::module(context),
|
||||||
"git_branch" => git_branch::segment(context),
|
"git_branch" => git_branch::module(context),
|
||||||
"git_status" => git_status::segment(context),
|
"git_status" => git_status::module(context),
|
||||||
"username" => username::segment(context),
|
"username" => username::module(context),
|
||||||
"battery" => battery::segment(context),
|
"battery" => battery::module(context),
|
||||||
|
|
||||||
_ => panic!("Unknown module: {}", module),
|
_ => panic!("Unknown module: {}", module),
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use super::{Context, Module};
|
||||||
/// - Current directory contains a `.js` file
|
/// - Current directory contains a `.js` file
|
||||||
/// - Current directory contains a `package.json` file
|
/// - Current directory contains a `package.json` file
|
||||||
/// - Current directory contains a `node_modules` directory
|
/// - 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
|
let is_js_project = context
|
||||||
.new_scan_dir()
|
.new_scan_dir()
|
||||||
.set_files(&["package.json"])
|
.set_files(&["package.json"])
|
||||||
|
@ -26,7 +26,7 @@ pub fn segment<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||||
const NODE_CHAR: &str = "⬢ ";
|
const NODE_CHAR: &str = "⬢ ";
|
||||||
let module_color = Color::Green.bold();
|
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);
|
module.set_style(module_color);
|
||||||
|
|
||||||
let formatted_version = node_version.trim();
|
let formatted_version = node_version.trim();
|
||||||
|
|
|
@ -8,13 +8,13 @@ use toml;
|
||||||
/// Creates a segment with the current package version
|
/// 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)
|
/// 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() {
|
match get_package_version() {
|
||||||
Some(package_version) => {
|
Some(package_version) => {
|
||||||
const PACKAGE_CHAR: &str = "📦 ";
|
const PACKAGE_CHAR: &str = "📦 ";
|
||||||
let module_color = Color::Red.bold();
|
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.set_style(module_color);
|
||||||
module.get_prefix().set_value("is ");
|
module.get_prefix().set_value("is ");
|
||||||
|
|
||||||
|
@ -75,19 +75,21 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract_cargo_version() {
|
fn test_extract_cargo_version() {
|
||||||
let cargo_with_version = r#"
|
let cargo_with_version = toml::toml! {
|
||||||
[package]
|
[package]
|
||||||
name = "starship"
|
name = "starship"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
"#;
|
}
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let expected_version = Some("v0.1.0".to_string());
|
let expected_version = Some("v0.1.0".to_string());
|
||||||
assert_eq!(extract_cargo_version(&cargo_with_version), expected_version);
|
assert_eq!(extract_cargo_version(&cargo_with_version), expected_version);
|
||||||
|
|
||||||
let cargo_without_version = r#"
|
let cargo_without_version = toml::toml! {
|
||||||
[package]
|
[package]
|
||||||
name = "starship"
|
name = "starship"
|
||||||
"#;
|
}
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let expected_version = None;
|
let expected_version = None;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -10,7 +10,7 @@ use super::{Context, Module};
|
||||||
/// - Current directory contains a `.python-version` file
|
/// - Current directory contains a `.python-version` file
|
||||||
/// - Current directory contains a `requirements.txt` file
|
/// - Current directory contains a `requirements.txt` file
|
||||||
/// - Current directory contains a `pyproject.toml` 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
|
let is_py_project = context
|
||||||
.new_scan_dir()
|
.new_scan_dir()
|
||||||
.set_files(&["requirements.txt", ".python-version", "pyproject.toml"])
|
.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 = "🐍 ";
|
const PYTHON_CHAR: &str = "🐍 ";
|
||||||
let module_color = Color::Yellow.bold();
|
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);
|
module.set_style(module_color);
|
||||||
|
|
||||||
let formatted_version = format_python_version(python_version);
|
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:
|
/// 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 file with a `.rs` extension
|
||||||
/// - Current directory contains a `Cargo.toml` file
|
/// - 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
|
let is_rs_project = context
|
||||||
.new_scan_dir()
|
.new_scan_dir()
|
||||||
.set_files(&["Cargo.toml"])
|
.set_files(&["Cargo.toml"])
|
||||||
|
@ -24,7 +24,7 @@ pub fn segment<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||||
const RUST_CHAR: &str = "🦀 ";
|
const RUST_CHAR: &str = "🦀 ";
|
||||||
let module_color = Color::Red.bold();
|
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);
|
module.set_style(module_color);
|
||||||
|
|
||||||
let formatted_version = format_rustc_version(rust_version);
|
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> {
|
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()),
|
Ok(output) => Some(String::from_utf8(output.stdout).unwrap()),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_rustc_version(mut rustc_stdout: String) -> String {
|
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();
|
let formatted_version: String = rustc_stdout.drain(..offset).collect();
|
||||||
|
|
||||||
format!("v{}", formatted_version.replace("rustc", "").trim())
|
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)");
|
let stable_input = String::from("rustc 1.34.0 (91856ed52 2019-04-10)");
|
||||||
assert_eq!(format_rustc_version(stable_input), "v1.34.0");
|
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 isn't the same as the one that is logged in ($LOGNAME != $USER)
|
||||||
/// - The current user is root (UID = 0)
|
/// - The current user is root (UID = 0)
|
||||||
/// - The user is currently connected as an SSH session ($SSH_CONNECTION)
|
/// - 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 user = env::var("USER").ok();
|
||||||
let logname = env::var("LOGNAME").ok();
|
let logname = env::var("LOGNAME").ok();
|
||||||
let ssh_connection = env::var("SSH_CONNECTION").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();
|
let mut module_color = Color::Yellow.bold();
|
||||||
|
|
||||||
if user != logname || ssh_connection.is_some() || is_root(&mut module_color) {
|
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.set_style(module_color);
|
||||||
module.new_segment("username", user?);
|
module.new_segment("username", user?);
|
||||||
|
|
||||||
|
|
31
src/print.rs
31
src/print.rs
|
@ -6,21 +6,22 @@ use crate::context::Context;
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::modules;
|
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) {
|
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 context = Context::new(args);
|
||||||
|
|
||||||
let stdout = io::stdout();
|
let stdout = io::stdout();
|
||||||
|
@ -29,7 +30,7 @@ pub fn prompt(args: ArgMatches) {
|
||||||
// Write a new line before the prompt
|
// Write a new line before the prompt
|
||||||
writeln!(handle).unwrap();
|
writeln!(handle).unwrap();
|
||||||
|
|
||||||
let modules = prompt_order
|
let modules = PROMPT_ORDER
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|module| modules::handle(module, &context)) // Compute modules
|
.map(|module| modules::handle(module, &context)) // Compute modules
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|
|
@ -1,15 +1,36 @@
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::{io, process};
|
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");
|
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
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_module(module_name: &str) -> process::Command {
|
pub fn render_module(module_name: &str) -> process::Command {
|
||||||
let mut command = process::Command::new("./target/debug/starship");
|
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
|
command
|
||||||
}
|
}
|
||||||
|
@ -20,3 +41,21 @@ pub fn new_tempdir() -> io::Result<tempfile::TempDir> {
|
||||||
// "/var/folders", which provides us with restricted permissions (rwxr-xr-x)
|
// "/var/folders", which provides us with restricted permissions (rwxr-xr-x)
|
||||||
tempfile::tempdir_in("/tmp")
|
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 character;
|
||||||
mod common;
|
mod common;
|
||||||
|
mod configuration;
|
||||||
mod directory;
|
mod directory;
|
||||||
mod golang;
|
mod golang;
|
||||||
mod line_break;
|
mod line_break;
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::common;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_env_variables() -> io::Result<()> {
|
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();
|
let actual = String::from_utf8(output.stdout).unwrap();
|
||||||
assert_eq!("", actual);
|
assert_eq!("", actual);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -17,7 +17,6 @@ fn no_env_variables() -> io::Result<()> {
|
||||||
#[test]
|
#[test]
|
||||||
fn logname_equals_user() -> io::Result<()> {
|
fn logname_equals_user() -> io::Result<()> {
|
||||||
let output = common::render_module("username")
|
let output = common::render_module("username")
|
||||||
.env_clear()
|
|
||||||
.env("LOGNAME", "astronaut")
|
.env("LOGNAME", "astronaut")
|
||||||
.env("USER", "astronaut")
|
.env("USER", "astronaut")
|
||||||
.output()?;
|
.output()?;
|
||||||
|
@ -30,7 +29,6 @@ fn logname_equals_user() -> io::Result<()> {
|
||||||
fn ssh_wo_username() -> io::Result<()> {
|
fn ssh_wo_username() -> io::Result<()> {
|
||||||
// SSH connection w/o username
|
// SSH connection w/o username
|
||||||
let output = common::render_module("username")
|
let output = common::render_module("username")
|
||||||
.env_clear()
|
|
||||||
.env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22")
|
.env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22")
|
||||||
.output()?;
|
.output()?;
|
||||||
let actual = String::from_utf8(output.stdout).unwrap();
|
let actual = String::from_utf8(output.stdout).unwrap();
|
||||||
|
@ -41,7 +39,6 @@ fn ssh_wo_username() -> io::Result<()> {
|
||||||
#[test]
|
#[test]
|
||||||
fn current_user_not_logname() -> io::Result<()> {
|
fn current_user_not_logname() -> io::Result<()> {
|
||||||
let output = common::render_module("username")
|
let output = common::render_module("username")
|
||||||
.env_clear()
|
|
||||||
.env("LOGNAME", "astronaut")
|
.env("LOGNAME", "astronaut")
|
||||||
.env("USER", "cosmonaut")
|
.env("USER", "cosmonaut")
|
||||||
.output()?;
|
.output()?;
|
||||||
|
@ -55,7 +52,6 @@ fn current_user_not_logname() -> io::Result<()> {
|
||||||
#[test]
|
#[test]
|
||||||
fn ssh_connection() -> io::Result<()> {
|
fn ssh_connection() -> io::Result<()> {
|
||||||
let output = common::render_module("username")
|
let output = common::render_module("username")
|
||||||
.env_clear()
|
|
||||||
.env("USER", "astronaut")
|
.env("USER", "astronaut")
|
||||||
.env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22")
|
.env("SSH_CONNECTION", "192.168.223.17 36673 192.168.223.229 22")
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
Loading…
Reference in New Issue