use std::{
collections::{btree_map::Iter, BTreeMap},
fs::read_to_string,
iter::Chain,
path::PathBuf,
process,
};
use toml_edit::{table, value};
use crate::{
constants::{
CRMRC, CRMRC_FILE, CRMRC_PATH, DL, PLEASE_TRY, REGISTRY, RUST_LANG, SOURCE, TABLE,
},
description::RegistryDescription,
toml::Toml,
utils::{append_end_spaces, home_dir, status_prefix, to_out},
};
#[derive(Debug)]
pub struct RuntimeConfig {
path: PathBuf,
config: Toml,
extend: BTreeMap<String, RegistryDescription>,
default: BTreeMap<String, RegistryDescription>,
}
impl RuntimeConfig {
pub fn new() -> Self {
let rc_path = home_dir().join(CRMRC);
let data = match read_to_string(&rc_path) {
Ok(content) => content,
Err(_) => String::new(),
};
let extend = RuntimeConfig::parse(&data);
let default = RuntimeConfig::parse(CRMRC_FILE);
RuntimeConfig {
extend: RuntimeConfig::extract_to_map(&extend),
default: RuntimeConfig::extract_to_map(&default),
path: rc_path,
config: extend,
}
}
pub fn registry_names(&self) -> Vec<String> {
self.default
.iter()
.chain(self.extend.iter())
.map(|(k, _)| k.to_string())
.collect()
}
pub fn to_string(&self, current: &String, sep: Option<&str>) -> String {
let sep = if sep.is_none() { "" } else { Option::unwrap(sep) };
self.iter()
.fold(String::new(), |mut memo, (k, v)| {
let p = status_prefix(k, current);
let k = append_end_spaces(k, None);
memo.push_str(format! {"{}{}{}{}\n", p, k, sep, v.registry }.as_str());
memo
})
.trim_end()
.to_string()
}
pub fn to_key_string(&self) -> String {
let f = |key| format!(" - {}", key);
let v1 = self.default.keys().map(f);
let v2 = self.extend.keys().map(f);
v1.chain(v2).collect::<Vec<String>>().join("\n")
}
pub fn write(&mut self) {
self.convert_from_map();
self.config.write(&self.path);
}
pub fn get(&self, registry_name: &str) -> Option<&RegistryDescription> {
match self.get_extend(registry_name) {
None => self.get_default(registry_name),
v => v,
}
}
pub fn get_extend(&self, registry_name: &str) -> Option<&RegistryDescription> {
self.extend.get(registry_name)
}
pub fn get_default(&self, registry_name: &str) -> Option<&RegistryDescription> {
self.default.get(registry_name)
}
pub fn save(&mut self, registry_name: &str, registry_addr: &str, registry_dl: &str) {
self.extend.insert(
registry_name.to_string(),
RegistryDescription {
registry: registry_addr.to_string(),
dl: registry_dl.to_string(),
},
);
}
pub fn remove(&mut self, registry_name: &str) {
self.extend.remove(registry_name);
}
pub fn to_tuples(&self, exclude_name: Option<&str>) -> Vec<(&str, &str)> {
self.iter().fold(vec![], |mut memo, (k, v)| {
if k.eq(RUST_LANG) || (exclude_name.is_some() && k.eq(exclude_name.unwrap())) {
return memo;
}
memo.push((k, &v.registry));
memo
})
}
fn iter(&self) -> Chain<Iter<String, RegistryDescription>, Iter<String, RegistryDescription>> {
self.default.iter().chain(self.extend.iter())
}
fn parse(data: &str) -> Toml {
let config = Toml::parse(data);
if config.is_err() {
to_out(format!("解析 {} 文件失败,{}", CRMRC_PATH, PLEASE_TRY));
process::exit(14);
}
let mut config = config.unwrap();
let data = config.table_mut();
let source = &data[SOURCE];
if source.is_none() {
data[SOURCE] = table();
} else if !source.is_table() {
to_out(format!(
"{} 文件中的 {} 字段不是一个{},{}",
CRMRC_PATH, SOURCE, TABLE, PLEASE_TRY
));
process::exit(15);
}
config
}
fn extract_to_map(config: &Toml) -> BTreeMap<String, RegistryDescription> {
let data = config.table();
let source = data[SOURCE].as_table().unwrap();
let mut map = BTreeMap::new();
source
.iter()
.for_each(|(key, value)| match value.as_table() {
Some(v) => {
let r = v[REGISTRY].as_str();
let d = v[DL].as_str();
if r.is_none() || d.is_none() {
to_out(format!(
"{} 文件中的 [{}.{}] 里没有包含 {} 或 {} 字段, {}",
CRMRC_PATH, SOURCE, key, REGISTRY, DL, PLEASE_TRY
));
process::exit(16);
}
let registry = r.unwrap().to_string();
let dl = d.unwrap().to_string();
map.insert(key.to_string(), RegistryDescription::new(registry, dl));
}
None => {
to_out(format!(
"{} 文件中的 {} 字段不是一个 {}, {}",
CRMRC_PATH, key, TABLE, PLEASE_TRY
));
process::exit(17);
}
});
map
}
fn convert_from_map(&mut self) {
let config = self.config.table_mut();
config[SOURCE] = table();
let source = config[SOURCE].as_table_mut().unwrap();
self.extend.iter().for_each(|(k, v)| {
let RegistryDescription { registry, dl } = v;
source[k] = table();
source[k][REGISTRY] = value(registry.to_string());
source[k][DL] = value(dl.to_string());
});
}
}
impl Default for RuntimeConfig {
fn default() -> Self {
Self::new()
}
}