use crate::configs::StarshipRootConfig;
use crate::utils;
use ansi_term::{Color, Style};
use indexmap::IndexMap;
use std::clone::Clone;
use std::collections::HashMap;
use std::io::ErrorKind;
use std::marker::Sized;
use std::env;
use toml::Value;
/// Root config of a module.
pub trait RootModuleConfig<'a>
where
Self: ModuleConfig<'a> + Default,
{
/// Load root module config from given Value and fill unset variables with default
/// values.
fn load(config: &'a Value) -> Self {
if config.get("prompt_order").is_some() {
log::warn!("\"prompt_order\" has been removed in favor of \"format\". For more details, see: https://starship.rs/migrating-to-0.45.0/")
}
Self::default().load_config(config)
}
/// Helper function that will call RootModuleConfig::load(config) if config is Some,
/// or RootModuleConfig::new() if config is None.
fn try_load(config: Option<&'a Value>) -> Self {
if let Some(config) = config {
Self::load(config)
} else {
Self::default()
}
}
}
impl<'a, T: ModuleConfig<'a> + Default> RootModuleConfig<'a> for T {}
/// Parsable config.
pub trait ModuleConfig<'a>
where
Self: Sized + Clone,
{
/// Construct a `ModuleConfig` from a toml value.
fn from_config(_config: &'a Value) -> Option<Self> {
None
}
/// Merge `self` with config from a toml table.
fn load_config(&self, config: &'a Value) -> Self {
Self::from_config(config).unwrap_or_else(|| self.clone())
}
}
// TODO: Add logging to default implementations
impl<'a> ModuleConfig<'a> for &'a str {
fn from_config(config: &'a Value) -> Option<Self> {
config.as_str()
}
}
impl<'a> ModuleConfig<'a> for Style {
fn from_config(config: &Value) -> Option<Self> {
parse_style_string(config.as_str()?)
}
}
impl<'a> ModuleConfig<'a> for bool {
fn from_config(config: &Value) -> Option<Self> {
config.as_bool()
}
}
impl<'a> ModuleConfig<'a> for i64 {
fn from_config(config: &Value) -> Option<Self> {
config.as_integer()
}
}
impl<'a> ModuleConfig<'a> for u64 {
fn from_config(config: &Value) -> Option<Self> {
match config {
Value::Integer(value) => {
// Converting i64 to u64
if *value > 0 {
Some(*value as u64)
} else {
None
}
}
Value::String(value) => value.parse::<u64>().ok(),
_ => None,
}
}
}
impl<'a> ModuleConfig<'a> for f64 {
fn from_config(config: &Value) -> Option<Self> {
config.as_float()
}
}
impl<'a> ModuleConfig<'a> for usize {
fn from_config(config: &Value) -> Option<Self> {
match config {
Value::Integer(value) => {
if *value > 0 {
Some(*value as usize)
} else {
None
}
}
Value::String(value) => value.parse::<usize>().ok(),
_ => None,
}
}
}
impl<'a, T> ModuleConfig<'a> for Vec<T>
where
T: ModuleConfig<'a>,
{
fn from_config(config: &'a Value) -> Option<Self> {
config
.as_array()?
.iter()
.map(|value| T::from_config(value))
.collect()
}
}
impl<'a, T, S: ::std::hash::BuildHasher + Default> ModuleConfig<'a> for HashMap<String, T, S>
where
T: ModuleConfig<'a>,
S: Clone,
{
fn from_config(config: &'a Value) -> Option<Self> {
let mut hm = HashMap::default();
for (x, y) in config.as_table()?.iter() {
hm.insert(x.clone(), T::from_config(y)?);
}
Some(hm)
}
}
impl<'a, T, S: ::std::hash::BuildHasher + Default> ModuleConfig<'a> for IndexMap<String, T, S>
where
T: ModuleConfig<'a>,
S: Clone,
{
fn from_config(config: &'a Value) -> Option<Self> {
let mut im = IndexMap::default();
for (x, y) in config.as_table()?.iter() {
im.insert(x.clone(), T::from_config(y)?);
}
Some(im)
}
}
impl<'a, T> ModuleConfig<'a> for Option<T>
where
T: ModuleConfig<'a> + Sized,
{
fn from_config(config: &