Skip to content

Commit

Permalink
feat: Add a disabled configuration option for modules (#86)
Browse files Browse the repository at this point in the history
• 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
  • Loading branch information
Matan Kushner committed Jul 2, 2019
1 parent 2440ed6 commit 463ec26
Show file tree
Hide file tree
Showing 24 changed files with 240 additions and 104 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -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"
Expand Down
63 changes: 60 additions & 3 deletions src/config.rs
@@ -1,4 +1,5 @@
use crate::utils;
use std::env;

use dirs::home_dir;

Expand All @@ -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);
Expand All @@ -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);
}
}
21 changes: 18 additions & 3 deletions src/context.rs
@@ -1,4 +1,4 @@
use crate::config::Config;
use crate::config::{Config, TableExt};
use crate::module::Module;

use clap::ArgMatches;
Expand Down Expand Up @@ -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
Expand Down
50 changes: 18 additions & 32 deletions src/main.rs
Expand Up @@ -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
Expand All @@ -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")
Expand All @@ -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();

Expand Down
6 changes: 3 additions & 3 deletions src/modules/battery.rs
Expand Up @@ -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 = "⇣";
Expand All @@ -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("");

Expand Down Expand Up @@ -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 => {
Expand Down
4 changes: 2 additions & 2 deletions src/modules/character.rs
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/modules/directory.rs
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/modules/git_branch.rs
Expand Up @@ -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 ");

Expand Down
4 changes: 2 additions & 2 deletions src/modules/git_status.rs
Expand Up @@ -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 = "⇡";
Expand All @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/modules/go.rs
Expand Up @@ -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"])
Expand All @@ -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)?;
Expand Down
4 changes: 2 additions & 2 deletions 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<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("");
Expand Down
24 changes: 12 additions & 12 deletions src/modules/mod.rs
Expand Up @@ -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),
}
Expand Down
4 changes: 2 additions & 2 deletions src/modules/nodejs.rs
Expand Up @@ -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"])
Expand All @@ -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();
Expand Down

0 comments on commit 463ec26

Please sign in to comment.