2020-04-26 13:58:39 +00:00
|
|
|
use std::process;
|
2021-09-04 01:56:43 +00:00
|
|
|
use std::process::Stdio;
|
2021-10-23 08:15:46 +00:00
|
|
|
use std::str::FromStr;
|
2019-12-18 21:09:36 +00:00
|
|
|
|
2022-03-26 09:42:19 +00:00
|
|
|
use crate::config::ModuleConfig;
|
2020-10-17 09:09:27 +00:00
|
|
|
use crate::config::StarshipConfig;
|
2021-09-01 18:32:32 +00:00
|
|
|
use crate::configs::PROMPT_ORDER;
|
2023-04-14 00:29:21 +00:00
|
|
|
use crate::context::Context;
|
2021-07-04 19:32:58 +00:00
|
|
|
use crate::utils;
|
2020-04-26 13:58:39 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::Write;
|
2021-10-23 08:15:46 +00:00
|
|
|
use toml_edit::Document;
|
2020-04-26 13:58:39 +00:00
|
|
|
|
2020-08-21 16:41:36 +00:00
|
|
|
#[cfg(not(windows))]
|
2019-12-18 21:09:36 +00:00
|
|
|
const STD_EDITOR: &str = "vi";
|
2020-08-21 16:41:36 +00:00
|
|
|
#[cfg(windows)]
|
|
|
|
const STD_EDITOR: &str = "notepad.exe";
|
2019-12-18 21:09:36 +00:00
|
|
|
|
2023-04-14 00:29:21 +00:00
|
|
|
pub fn update_configuration(context: &Context, name: &str, value: &str) {
|
|
|
|
let mut doc = get_configuration_edit(context);
|
2021-10-23 08:15:46 +00:00
|
|
|
|
|
|
|
match handle_update_configuration(&mut doc, name, value) {
|
|
|
|
Err(e) => {
|
2022-11-05 11:40:46 +00:00
|
|
|
eprintln!("{e}");
|
2021-10-23 08:15:46 +00:00
|
|
|
process::exit(1);
|
|
|
|
}
|
2023-04-14 00:29:21 +00:00
|
|
|
_ => write_configuration(context, &doc),
|
2020-04-26 13:58:39 +00:00
|
|
|
}
|
2021-10-23 08:15:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_update_configuration(doc: &mut Document, name: &str, value: &str) -> Result<(), String> {
|
2021-11-11 10:58:47 +00:00
|
|
|
let mut keys = name.split('.');
|
2020-04-26 13:58:39 +00:00
|
|
|
|
2021-11-11 10:58:47 +00:00
|
|
|
let first_key = keys.next().unwrap_or_default();
|
|
|
|
if first_key.is_empty() {
|
|
|
|
return Err("Empty table keys are not supported".to_owned());
|
|
|
|
}
|
|
|
|
|
|
|
|
let table = doc.as_table_mut();
|
|
|
|
let mut current_item = table.entry(first_key).or_insert_with(toml_edit::table);
|
|
|
|
|
|
|
|
for key in keys {
|
2021-10-23 08:15:46 +00:00
|
|
|
if !current_item.is_table_like() {
|
|
|
|
return Err("This command can only index into TOML tables".to_owned());
|
2020-04-26 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
2021-10-23 08:15:46 +00:00
|
|
|
if key.is_empty() {
|
|
|
|
return Err("Empty table keys are not supported".to_owned());
|
|
|
|
}
|
2020-04-26 13:58:39 +00:00
|
|
|
|
2021-10-23 08:15:46 +00:00
|
|
|
let table = current_item.as_table_like_mut().unwrap();
|
2020-04-26 13:58:39 +00:00
|
|
|
|
2021-10-23 08:15:46 +00:00
|
|
|
if !table.contains_key(key) {
|
|
|
|
table.insert(key, toml_edit::table());
|
2020-04-26 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
2021-10-23 08:15:46 +00:00
|
|
|
current_item = table.get_mut(key).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut new_value = toml_edit::Value::from_str(value)
|
2022-05-23 10:58:27 +00:00
|
|
|
.map_or_else(|_| toml_edit::value(value), toml_edit::Item::Value);
|
2021-10-23 08:15:46 +00:00
|
|
|
|
|
|
|
if let Some(value) = current_item.as_value() {
|
|
|
|
*new_value.as_value_mut().unwrap().decor_mut() = value.decor().clone();
|
2020-04-26 13:58:39 +00:00
|
|
|
}
|
2021-10-23 08:15:46 +00:00
|
|
|
|
|
|
|
*current_item = new_value;
|
|
|
|
|
|
|
|
Ok(())
|
2020-04-26 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
2023-04-14 00:29:21 +00:00
|
|
|
pub fn print_configuration(context: &Context, use_default: bool, paths: &[String]) -> String {
|
2021-03-31 15:31:55 +00:00
|
|
|
let config = if use_default {
|
|
|
|
// Get default config
|
|
|
|
let default_config = crate::configs::FullConfig::default();
|
|
|
|
// Convert back to Value because toml can't serialize FullConfig directly
|
|
|
|
toml::value::Value::try_from(default_config).unwrap()
|
|
|
|
} else {
|
|
|
|
// Get config as toml::Value
|
2023-04-14 00:29:21 +00:00
|
|
|
let user_config = get_configuration(context);
|
2021-03-31 15:31:55 +00:00
|
|
|
// Convert into FullConfig and fill in default values
|
2022-03-26 09:42:19 +00:00
|
|
|
let user_config = crate::configs::FullConfig::load(&user_config);
|
2021-03-31 15:31:55 +00:00
|
|
|
// Convert back to Value because toml can't serialize FullConfig directly
|
|
|
|
toml::value::Value::try_from(user_config).unwrap()
|
|
|
|
};
|
|
|
|
|
2021-09-01 18:32:32 +00:00
|
|
|
println!("# Warning: This config does not include keys that have an unset value\n");
|
|
|
|
|
2021-11-15 05:45:19 +00:00
|
|
|
// These are only used for format specifiers so don't print them if we aren't showing formats.
|
|
|
|
if paths.is_empty()
|
|
|
|
|| paths
|
|
|
|
.iter()
|
2022-01-04 09:49:42 +00:00
|
|
|
.any(|path| path == "format" || path == "right_format")
|
2021-11-15 05:45:19 +00:00
|
|
|
{
|
2021-09-01 18:32:32 +00:00
|
|
|
println!(
|
2021-11-15 05:45:19 +00:00
|
|
|
"# $all is shorthand for {}",
|
|
|
|
PROMPT_ORDER
|
|
|
|
.iter()
|
2022-11-05 11:40:46 +00:00
|
|
|
.map(|module_name| format!("${module_name}"))
|
2021-09-01 18:32:32 +00:00
|
|
|
.collect::<String>()
|
|
|
|
);
|
2021-11-15 05:45:19 +00:00
|
|
|
|
|
|
|
// Unwrapping is fine because config is based on FullConfig
|
|
|
|
let custom_modules = config.get("custom").unwrap().as_table().unwrap();
|
|
|
|
if !use_default && !custom_modules.is_empty() {
|
|
|
|
println!(
|
|
|
|
"# $custom (excluding any modules already listed in `format`) is shorthand for {}",
|
|
|
|
custom_modules
|
|
|
|
.keys()
|
2022-11-05 11:40:46 +00:00
|
|
|
.map(|module_name| format!("${{custom.{module_name}}}"))
|
2021-11-15 05:45:19 +00:00
|
|
|
.collect::<String>()
|
|
|
|
);
|
|
|
|
}
|
2021-09-01 18:32:32 +00:00
|
|
|
}
|
2021-11-15 05:45:19 +00:00
|
|
|
|
|
|
|
let print_config = if paths.is_empty() {
|
|
|
|
config
|
|
|
|
} else {
|
|
|
|
extract_toml_paths(config, paths)
|
|
|
|
};
|
|
|
|
|
|
|
|
let string_config = toml::to_string_pretty(&print_config).unwrap();
|
|
|
|
|
2022-11-05 11:40:46 +00:00
|
|
|
println!("{string_config}");
|
2023-04-14 00:29:21 +00:00
|
|
|
string_config
|
2021-03-31 15:31:55 +00:00
|
|
|
}
|
|
|
|
|
2022-01-04 09:49:42 +00:00
|
|
|
fn extract_toml_paths(mut config: toml::Value, paths: &[String]) -> toml::Value {
|
2021-11-15 05:45:19 +00:00
|
|
|
// Extract all the requested sections into a new configuration.
|
|
|
|
let mut subset = toml::value::Table::new();
|
2023-01-27 09:33:24 +00:00
|
|
|
let Some(config) = config.as_table_mut() else {
|
2021-11-15 05:45:19 +00:00
|
|
|
// This function doesn't make any sense if the root is not a table.
|
|
|
|
return toml::Value::Table(subset);
|
|
|
|
};
|
|
|
|
|
2022-01-04 09:49:42 +00:00
|
|
|
'paths: for path in paths {
|
2021-11-15 05:45:19 +00:00
|
|
|
let path_segments: Vec<_> = path.split('.').collect();
|
|
|
|
let (&end, parents) = path_segments.split_last().unwrap_or((&"", &[]));
|
|
|
|
|
|
|
|
// Locate the parent table to remove the value from.
|
|
|
|
let mut source_cursor = &mut *config;
|
|
|
|
for &segment in parents {
|
|
|
|
source_cursor = if let Some(child) = source_cursor
|
|
|
|
.get_mut(segment)
|
2022-05-23 10:58:27 +00:00
|
|
|
.and_then(toml::Value::as_table_mut)
|
2021-11-15 05:45:19 +00:00
|
|
|
{
|
|
|
|
child
|
|
|
|
} else {
|
|
|
|
// We didn't find a value for this path, so move on to the next path.
|
|
|
|
continue 'paths;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract the value to move.
|
2023-01-27 09:33:24 +00:00
|
|
|
let Some(value) = source_cursor.remove(end) else {
|
2021-11-15 05:45:19 +00:00
|
|
|
// We didn't find a value for this path, so move on to the next path.
|
|
|
|
continue 'paths;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Create a destination for that value.
|
|
|
|
let mut destination_cursor = &mut subset;
|
|
|
|
for &segment in &path_segments[..path_segments.len() - 1] {
|
|
|
|
// Because we initialize `subset` to be a table, and only add additional values that
|
|
|
|
// exist in `config`, it's impossible for the value here to not be a table.
|
|
|
|
destination_cursor = destination_cursor
|
|
|
|
.entry(segment)
|
|
|
|
.or_insert_with(|| toml::Value::Table(toml::value::Table::new()))
|
|
|
|
.as_table_mut()
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
destination_cursor.insert(end.to_owned(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
toml::Value::Table(subset)
|
|
|
|
}
|
|
|
|
|
2023-04-14 00:29:21 +00:00
|
|
|
pub fn toggle_configuration(context: &Context, name: &str, key: &str) {
|
|
|
|
let mut doc = get_configuration_edit(context);
|
2021-10-23 08:15:46 +00:00
|
|
|
|
|
|
|
match handle_toggle_configuration(&mut doc, name, key) {
|
|
|
|
Err(e) => {
|
2022-11-05 11:40:46 +00:00
|
|
|
eprintln!("{e}");
|
2021-10-23 08:15:46 +00:00
|
|
|
process::exit(1);
|
|
|
|
}
|
2023-04-14 00:29:21 +00:00
|
|
|
_ => write_configuration(context, &doc),
|
2021-01-07 18:04:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-23 08:15:46 +00:00
|
|
|
fn handle_toggle_configuration(doc: &mut Document, name: &str, key: &str) -> Result<(), String> {
|
|
|
|
if name.is_empty() || key.is_empty() {
|
|
|
|
return Err("Empty table keys are not supported".to_owned());
|
|
|
|
}
|
|
|
|
|
|
|
|
let table = doc.as_table_mut();
|
|
|
|
|
|
|
|
let values = table
|
|
|
|
.get_mut(name)
|
2022-11-05 11:40:46 +00:00
|
|
|
.ok_or_else(|| format!("Given module '{name}' not found in config file"))?
|
2021-10-23 08:15:46 +00:00
|
|
|
.as_table_like_mut()
|
2022-11-05 11:40:46 +00:00
|
|
|
.ok_or_else(|| format!("Given config entry '{key}' is not a module"))?;
|
2021-10-23 08:15:46 +00:00
|
|
|
|
|
|
|
let old_value = values
|
|
|
|
.get(key)
|
2022-11-05 11:40:46 +00:00
|
|
|
.ok_or_else(|| format!("Given config key '{key}' must exist in config file"))?;
|
2021-10-23 08:15:46 +00:00
|
|
|
|
|
|
|
let old = old_value
|
|
|
|
.as_bool()
|
2022-11-05 11:40:46 +00:00
|
|
|
.ok_or_else(|| format!("Given config key '{key}' must be in 'boolean' format"))?;
|
2021-10-23 08:15:46 +00:00
|
|
|
|
|
|
|
let mut new_value = toml_edit::value(!old);
|
|
|
|
// Above code already checks if it is a value (bool)
|
|
|
|
*new_value.as_value_mut().unwrap().decor_mut() = old_value.as_value().unwrap().decor().clone();
|
|
|
|
|
|
|
|
values.insert(key, new_value);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-04-14 00:29:21 +00:00
|
|
|
pub fn get_configuration(context: &Context) -> toml::Table {
|
|
|
|
let starship_config = StarshipConfig::initialize(&context.get_config_path_os());
|
2021-01-07 18:04:06 +00:00
|
|
|
|
2023-04-14 00:29:21 +00:00
|
|
|
starship_config.config.unwrap_or(toml::Table::new())
|
2021-01-07 18:04:06 +00:00
|
|
|
}
|
|
|
|
|
2023-04-14 00:29:21 +00:00
|
|
|
pub fn get_configuration_edit(context: &Context) -> Document {
|
|
|
|
let config_file_path = context.get_config_path_os();
|
|
|
|
let toml_content = StarshipConfig::read_config_content_as_str(&config_file_path);
|
2021-10-23 08:15:46 +00:00
|
|
|
|
|
|
|
toml_content
|
|
|
|
.unwrap_or_default()
|
|
|
|
.parse::<Document>()
|
|
|
|
.expect("Failed to load starship config")
|
|
|
|
}
|
|
|
|
|
2023-04-14 00:29:21 +00:00
|
|
|
pub fn write_configuration(context: &Context, doc: &Document) {
|
|
|
|
let config_path = context.get_config_path_os().unwrap_or_else(|| {
|
|
|
|
eprintln!("config path required to write configuration");
|
|
|
|
process::exit(1);
|
|
|
|
});
|
2021-01-07 18:04:06 +00:00
|
|
|
|
2021-10-23 08:15:46 +00:00
|
|
|
let config_str = doc.to_string();
|
2021-01-07 18:04:06 +00:00
|
|
|
|
2022-11-05 11:40:46 +00:00
|
|
|
File::create(config_path)
|
2021-01-07 18:04:06 +00:00
|
|
|
.and_then(|mut file| file.write_all(config_str.as_ref()))
|
|
|
|
.expect("Error writing starship config");
|
|
|
|
}
|
|
|
|
|
2023-04-14 00:29:21 +00:00
|
|
|
pub fn edit_configuration(
|
|
|
|
context: &Context,
|
|
|
|
editor_override: Option<&str>,
|
|
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
2022-05-02 16:43:27 +00:00
|
|
|
// Argument currently only used for testing, but could be used to specify
|
|
|
|
// an editor override on the command line.
|
2023-04-14 00:29:21 +00:00
|
|
|
let config_path = context.get_config_path_os().unwrap_or_else(|| {
|
|
|
|
eprintln!("config path required to edit configuration");
|
|
|
|
process::exit(1);
|
|
|
|
});
|
2019-12-23 15:27:00 +00:00
|
|
|
|
2022-05-02 16:43:27 +00:00
|
|
|
let editor_cmd = shell_words::split(&get_editor(editor_override))?;
|
|
|
|
let mut command = match utils::create_command(&editor_cmd[0]) {
|
|
|
|
Ok(cmd) => cmd,
|
|
|
|
Err(e) => {
|
|
|
|
eprintln!(
|
|
|
|
"Unable to find editor {:?}. Are $VISUAL and $EDITOR set correctly?",
|
|
|
|
editor_cmd[0]
|
|
|
|
);
|
|
|
|
return Err(Box::new(e));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let res = command
|
2021-09-04 01:56:43 +00:00
|
|
|
.stdin(Stdio::inherit())
|
|
|
|
.stdout(Stdio::inherit())
|
|
|
|
.stderr(Stdio::inherit())
|
2020-08-21 16:41:36 +00:00
|
|
|
.args(&editor_cmd[1..])
|
|
|
|
.arg(config_path)
|
|
|
|
.status();
|
2020-03-05 15:50:34 +00:00
|
|
|
|
2022-05-02 16:43:27 +00:00
|
|
|
if let Err(e) = res {
|
2022-11-05 11:40:46 +00:00
|
|
|
eprintln!("Unable to launch editor {editor_cmd:?}");
|
2022-05-02 16:43:27 +00:00
|
|
|
return Err(Box::new(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2019-12-18 21:09:36 +00:00
|
|
|
}
|
|
|
|
|
2022-05-02 16:43:27 +00:00
|
|
|
fn get_editor(editor_override: Option<&str>) -> String {
|
|
|
|
if let Some(cmd) = editor_override {
|
|
|
|
cmd.to_string()
|
|
|
|
} else {
|
|
|
|
get_editor_internal(std::env::var("VISUAL").ok(), std::env::var("EDITOR").ok())
|
|
|
|
}
|
2019-12-23 15:27:00 +00:00
|
|
|
}
|
|
|
|
|
2020-08-21 16:41:36 +00:00
|
|
|
fn get_editor_internal(visual: Option<String>, editor: Option<String>) -> String {
|
2021-03-26 20:01:20 +00:00
|
|
|
let editor_name = visual.unwrap_or_default();
|
2019-12-23 15:27:00 +00:00
|
|
|
if !editor_name.is_empty() {
|
|
|
|
return editor_name;
|
|
|
|
}
|
2021-03-26 20:01:20 +00:00
|
|
|
let editor_name = editor.unwrap_or_default();
|
2019-12-23 15:27:00 +00:00
|
|
|
if !editor_name.is_empty() {
|
|
|
|
return editor_name;
|
|
|
|
}
|
|
|
|
STD_EDITOR.into()
|
2019-12-18 21:09:36 +00:00
|
|
|
}
|
|
|
|
|
2019-12-23 15:27:00 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-04-14 00:29:21 +00:00
|
|
|
use std::{fs::create_dir, io};
|
|
|
|
|
|
|
|
use tempfile::TempDir;
|
|
|
|
use toml_edit::Item;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
context::{Shell, Target},
|
|
|
|
context_env::Env,
|
|
|
|
};
|
|
|
|
|
2019-12-23 15:27:00 +00:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
// This is every possible permutation, 3² = 9.
|
|
|
|
#[test]
|
|
|
|
fn visual_set_editor_set() {
|
|
|
|
let actual = get_editor_internal(Some("foo".into()), Some("bar".into()));
|
|
|
|
assert_eq!("foo", actual);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn visual_set_editor_empty() {
|
|
|
|
let actual = get_editor_internal(Some("foo".into()), None);
|
|
|
|
assert_eq!("foo", actual);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn visual_set_editor_not_set() {
|
|
|
|
let actual = get_editor_internal(Some("foo".into()), None);
|
|
|
|
assert_eq!("foo", actual);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn visual_empty_editor_set() {
|
2022-11-05 11:40:46 +00:00
|
|
|
let actual = get_editor_internal(Some(String::new()), Some("bar".into()));
|
2019-12-23 15:27:00 +00:00
|
|
|
assert_eq!("bar", actual);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn visual_empty_editor_empty() {
|
2022-11-05 11:40:46 +00:00
|
|
|
let actual = get_editor_internal(Some(String::new()), Some(String::new()));
|
2020-08-21 16:41:36 +00:00
|
|
|
assert_eq!(STD_EDITOR, actual);
|
2019-12-23 15:27:00 +00:00
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn visual_empty_editor_not_set() {
|
2022-11-05 11:40:46 +00:00
|
|
|
let actual = get_editor_internal(Some(String::new()), None);
|
2020-08-21 16:41:36 +00:00
|
|
|
assert_eq!(STD_EDITOR, actual);
|
2019-12-23 15:27:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn visual_not_set_editor_set() {
|
|
|
|
let actual = get_editor_internal(None, Some("bar".into()));
|
|
|
|
assert_eq!("bar", actual);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn visual_not_set_editor_empty() {
|
2022-11-05 11:40:46 +00:00
|
|
|
let actual = get_editor_internal(None, Some(String::new()));
|
2020-08-21 16:41:36 +00:00
|
|
|
assert_eq!(STD_EDITOR, actual);
|
2019-12-23 15:27:00 +00:00
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn visual_not_set_editor_not_set() {
|
|
|
|
let actual = get_editor_internal(None, None);
|
2020-08-21 16:41:36 +00:00
|
|
|
assert_eq!(STD_EDITOR, actual);
|
2019-12-23 15:27:00 +00:00
|
|
|
}
|
2021-10-23 08:15:46 +00:00
|
|
|
|
2022-05-02 16:43:27 +00:00
|
|
|
#[test]
|
2023-03-22 18:42:46 +00:00
|
|
|
fn no_panic_when_editor_unparsable() {
|
2023-04-14 00:29:21 +00:00
|
|
|
let outcome = edit_configuration(&Default::default(), Some("\"vim"));
|
2022-05-02 16:43:27 +00:00
|
|
|
assert!(outcome.is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_panic_when_editor_not_found() {
|
2023-04-14 00:29:21 +00:00
|
|
|
let outcome = edit_configuration(&Default::default(), Some("this_editor_does_not_exist"));
|
2022-05-02 16:43:27 +00:00
|
|
|
assert!(outcome.is_err());
|
|
|
|
}
|
|
|
|
|
2021-11-15 05:45:19 +00:00
|
|
|
#[test]
|
|
|
|
fn test_extract_toml_paths() {
|
|
|
|
let config = toml::toml! {
|
|
|
|
extract_root = true
|
|
|
|
ignore_root = false
|
|
|
|
|
|
|
|
[extract_section]
|
|
|
|
ok = true
|
|
|
|
|
|
|
|
[extract_section.subsection]
|
|
|
|
ok = true
|
|
|
|
|
|
|
|
[ignore_section]
|
|
|
|
ok = false
|
|
|
|
|
|
|
|
[extract_subsection]
|
|
|
|
ok = false
|
|
|
|
|
|
|
|
[extract_subsection.extracted]
|
|
|
|
ok = true
|
|
|
|
|
|
|
|
[extract_subsection.ignored]
|
|
|
|
ok = false
|
|
|
|
};
|
|
|
|
let expected_config = toml::toml! {
|
|
|
|
extract_root = true
|
|
|
|
|
|
|
|
[extract_section]
|
|
|
|
ok = true
|
|
|
|
|
|
|
|
[extract_section.subsection]
|
|
|
|
ok = true
|
|
|
|
|
|
|
|
[extract_subsection.extracted]
|
|
|
|
ok = true
|
|
|
|
};
|
|
|
|
let actual_config = extract_toml_paths(
|
2023-01-30 22:12:27 +00:00
|
|
|
toml::Value::Table(config),
|
2021-11-15 05:45:19 +00:00
|
|
|
&[
|
2022-01-04 09:49:42 +00:00
|
|
|
"extract_root".to_owned(),
|
|
|
|
"extract_section".to_owned(),
|
|
|
|
"extract_subsection.extracted".to_owned(),
|
2021-11-15 05:45:19 +00:00
|
|
|
],
|
|
|
|
);
|
|
|
|
|
2023-01-30 22:12:27 +00:00
|
|
|
assert_eq!(toml::Value::Table(expected_config), actual_config);
|
2021-11-15 05:45:19 +00:00
|
|
|
}
|
|
|
|
|
2021-10-23 08:15:46 +00:00
|
|
|
fn create_doc() -> Document {
|
|
|
|
let config = concat!(
|
|
|
|
" # comment\n",
|
|
|
|
" [status] # comment\n",
|
|
|
|
"disabled = false # comment\n",
|
|
|
|
"# comment\n",
|
|
|
|
"\n"
|
|
|
|
);
|
|
|
|
|
|
|
|
config.parse::<Document>().unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_toggle_simple() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
|
|
|
|
assert!(!doc["status"]["disabled"].as_bool().unwrap());
|
|
|
|
|
|
|
|
handle_toggle_configuration(&mut doc, "status", "disabled").unwrap();
|
|
|
|
|
|
|
|
assert!(doc["status"]["disabled"].as_bool().unwrap());
|
|
|
|
|
|
|
|
let new_config = concat!(
|
|
|
|
" # comment\n",
|
|
|
|
" [status] # comment\n",
|
|
|
|
"disabled = true # comment\n",
|
|
|
|
"# comment\n",
|
|
|
|
"\n"
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(doc.to_string(), new_config)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_toggle_missing_module() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
assert!(handle_toggle_configuration(&mut doc, "missing_module", "disabled").is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_toggle_missing_key() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
assert!(handle_toggle_configuration(&mut doc, "status", "missing").is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_toggle_wrong_type() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
doc["status"]["disabled"] = toml_edit::value("a");
|
|
|
|
|
|
|
|
assert!(handle_toggle_configuration(&mut doc, "status", "disabled").is_err());
|
|
|
|
|
|
|
|
doc["format"] = toml_edit::value("$all");
|
|
|
|
|
|
|
|
assert!(handle_toggle_configuration(&mut doc, "format", "disabled").is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_toggle_empty() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
|
|
|
|
doc["status"][""] = toml_edit::value(true);
|
|
|
|
doc[""]["disabled"] = toml_edit::value(true);
|
|
|
|
|
|
|
|
assert!(handle_toggle_configuration(&mut doc, "status", "").is_err());
|
|
|
|
assert!(handle_toggle_configuration(&mut doc, "", "disabled").is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_update_config_wrong_type() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
handle_update_configuration(&mut doc, "status.disabled.not_a_table", "true").is_err()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_update_config_simple() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
|
|
|
|
assert!(!doc["status"]["disabled"].as_bool().unwrap());
|
|
|
|
|
|
|
|
handle_update_configuration(&mut doc, "status.disabled", "true").unwrap();
|
|
|
|
|
|
|
|
assert!(doc["status"]["disabled"].as_bool().unwrap());
|
|
|
|
|
|
|
|
let new_config = concat!(
|
|
|
|
" # comment\n",
|
|
|
|
" [status] # comment\n",
|
|
|
|
"disabled = true # comment\n",
|
|
|
|
"# comment\n",
|
|
|
|
"\n"
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(doc.to_string(), new_config)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_update_config_parse() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
|
|
|
|
handle_update_configuration(&mut doc, "test", "true").unwrap();
|
|
|
|
|
|
|
|
assert!(doc["test"].as_bool().unwrap());
|
|
|
|
|
|
|
|
handle_update_configuration(&mut doc, "test", "0").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(doc["test"].as_integer().unwrap(), 0);
|
|
|
|
|
|
|
|
handle_update_configuration(&mut doc, "test", "0.0").unwrap();
|
|
|
|
|
|
|
|
assert!(doc["test"].is_float());
|
|
|
|
|
|
|
|
handle_update_configuration(&mut doc, "test", "a string").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(doc["test"].as_str().unwrap(), "a string");
|
|
|
|
|
|
|
|
handle_update_configuration(&mut doc, "test", "\"true\"").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(doc["test"].as_str().unwrap(), "true");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_update_config_empty() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
|
|
|
|
assert!(handle_update_configuration(&mut doc, "", "true").is_err());
|
|
|
|
assert!(handle_update_configuration(&mut doc, ".....", "true").is_err());
|
|
|
|
assert!(handle_update_configuration(&mut doc, "a.a.a..a.a", "true").is_err());
|
|
|
|
assert!(handle_update_configuration(&mut doc, "a.a.a.a.a.", "true").is_err());
|
2021-11-11 10:58:47 +00:00
|
|
|
assert!(handle_update_configuration(&mut doc, ".a.a.a.a.a", "true").is_err());
|
2021-10-23 08:15:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_update_config_deep() {
|
|
|
|
let mut doc = create_doc();
|
|
|
|
|
|
|
|
handle_update_configuration(&mut doc, "a.b.c.d.e.f.g.h", "true").unwrap();
|
|
|
|
|
|
|
|
assert!(doc["a"]["b"]["c"]["d"]["e"]["f"]["g"]["h"]
|
|
|
|
.as_bool()
|
|
|
|
.unwrap())
|
|
|
|
}
|
2023-04-14 00:29:21 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn write_and_get_configuration_test() -> io::Result<()> {
|
|
|
|
let dir = tempfile::tempdir()?;
|
|
|
|
let context = setup_config(&dir, true, StarshipConfigEnvScenario::NotSpecified)?;
|
|
|
|
let mut doc = get_configuration_edit(&context);
|
|
|
|
doc["directory"]["format"] = Item::Value("myformat".into());
|
|
|
|
write_configuration(&context, &doc);
|
|
|
|
let doc_reloaded = get_configuration_edit(&context);
|
|
|
|
assert_eq!(
|
|
|
|
"myformat",
|
|
|
|
doc_reloaded["directory"]["format"].as_str().unwrap()
|
|
|
|
);
|
|
|
|
dir.close()
|
|
|
|
}
|
|
|
|
|
|
|
|
const PRINT_CONFIG_DEFAULT: &str = "[custom]";
|
|
|
|
const PRINT_CONFIG_HOME: &str = "[custom.home]";
|
|
|
|
const PRINT_CONFIG_ENV: &str = "[custom.STARSHIP_CONFIG]";
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn print_configuration_scenarios() -> io::Result<()> {
|
|
|
|
run_print_configuration_test(
|
|
|
|
"~/.config/starship.toml, no STARSHIP_CONFIG uses home",
|
|
|
|
true,
|
|
|
|
StarshipConfigEnvScenario::NotSpecified,
|
|
|
|
PRINT_CONFIG_HOME,
|
|
|
|
)?;
|
|
|
|
run_print_configuration_test(
|
|
|
|
"no ~/.config/starship.toml, no STARSHIP_CONFIG uses default",
|
|
|
|
false,
|
|
|
|
StarshipConfigEnvScenario::NotSpecified,
|
|
|
|
PRINT_CONFIG_DEFAULT,
|
|
|
|
)?;
|
|
|
|
run_print_configuration_test(
|
|
|
|
"~/.config/starship.toml, STARSHIP_CONFIG nonexisting file uses default",
|
|
|
|
true,
|
|
|
|
StarshipConfigEnvScenario::NonExistingFile,
|
|
|
|
PRINT_CONFIG_DEFAULT,
|
|
|
|
)?;
|
|
|
|
run_print_configuration_test(
|
|
|
|
"~/.config/starship.toml, STARSHIP_CONFIG existing file uses STARSHIP_CONFIG file",
|
|
|
|
true,
|
|
|
|
StarshipConfigEnvScenario::ExistingFile,
|
|
|
|
PRINT_CONFIG_ENV,
|
|
|
|
)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
enum StarshipConfigEnvScenario {
|
|
|
|
NotSpecified,
|
|
|
|
NonExistingFile,
|
|
|
|
ExistingFile,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_print_configuration_test(
|
|
|
|
message: &str,
|
|
|
|
home_file_exists: bool,
|
|
|
|
starship_config_env_scenario: StarshipConfigEnvScenario,
|
|
|
|
expected_first_line: &str,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
let dir = tempfile::tempdir()?;
|
|
|
|
let context = setup_config(&dir, home_file_exists, starship_config_env_scenario)?;
|
|
|
|
let config = print_configuration(&context, false, &["custom".to_string()]);
|
|
|
|
let first_line = config.split('\n').next().unwrap();
|
|
|
|
assert_eq!(expected_first_line, first_line, "{message}");
|
|
|
|
dir.close()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn setup_config(
|
|
|
|
dir: &TempDir,
|
|
|
|
home_file_exists: bool,
|
|
|
|
starship_config_env_scenario: StarshipConfigEnvScenario,
|
|
|
|
) -> io::Result<Context> {
|
|
|
|
let config_path = dir.path().to_path_buf().join(".config");
|
|
|
|
create_dir(&config_path)?;
|
|
|
|
let home_starship_toml = config_path.join("starship.toml");
|
|
|
|
let env_toml = dir.path().join("env.toml");
|
|
|
|
if home_file_exists {
|
|
|
|
let mut home_file = File::create(home_starship_toml)?;
|
|
|
|
home_file.write_all(PRINT_CONFIG_HOME.as_bytes())?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let env_starship_config = match starship_config_env_scenario {
|
|
|
|
StarshipConfigEnvScenario::NotSpecified => None,
|
|
|
|
StarshipConfigEnvScenario::NonExistingFile => Some(env_toml),
|
|
|
|
StarshipConfigEnvScenario::ExistingFile => {
|
|
|
|
let mut env_toml_file = File::create(&env_toml)?;
|
|
|
|
env_toml_file.write_all(PRINT_CONFIG_ENV.as_bytes())?;
|
|
|
|
Some(env_toml)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut env = Env::default();
|
|
|
|
if let Some(v) = env_starship_config {
|
|
|
|
env.insert("STARSHIP_CONFIG", v.to_string_lossy().to_string());
|
|
|
|
}
|
|
|
|
env.insert(
|
|
|
|
"HOME",
|
|
|
|
dir.path().to_path_buf().to_string_lossy().to_string(),
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(Context::new_with_shell_and_path(
|
|
|
|
Default::default(),
|
|
|
|
Shell::Unknown,
|
|
|
|
Target::Main,
|
|
|
|
Default::default(),
|
|
|
|
Default::default(),
|
|
|
|
env,
|
|
|
|
))
|
|
|
|
}
|
2019-12-23 15:27:00 +00:00
|
|
|
}
|