use anyhow::Result; use colored::Colorize; use crate::{ platform::Platform, input }; use flexbuffers::{ FlexbufferSerializer, Reader }; use std::collections::BTreeSet; use std::fs; use std::path; use serde::{ Serialize, Deserialize }; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Host { pub platform: Platform, pub id: String, pub user: String, pub host: String, pub port: i16, pub config: String } impl std::fmt::Display for Host { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(std::format_args!("[{}] {}@{}", self.platform, self.user, self.host)) } } impl Host { pub fn edit(&mut self, config: &mut ConfigManager) -> Result { let data = toml::to_string_pretty(self)?; let t = input::edit_temp_from_string(&data)?; match toml::from_str::(&fs::read_to_string(t)?) { Ok(d) => { println!(" ## {} ## ", "Definition OK".green()); // Replace old config with new config config.configs.remove(self); config.configs.insert(d.to_owned()); config.save(); Ok(d) }, Err(e) => { println!(" ## {}: {} ## ", "Rejecting definition".red(), e); Ok(self.to_owned()) }, } } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ConfigManager { #[serde(skip)] pub config_dir: path::PathBuf, #[serde(skip)] pub search_path: path::PathBuf, pub configs: BTreeSet, } impl ConfigManager { pub fn new(given_config_root: Option, search_path: Option) -> Result { // Resolve config root let config_root = match given_config_root { Some(c) => path::Path::new(&c).to_owned(), None => dirs::data_local_dir().unwrap().join("keyman/"), }; // Make config root if it doesn't exist (eg: first run) if !fs::try_exists(config_root.to_owned()).unwrap_or(false) { fs::create_dir_all(config_root.to_owned())?; } // Resolve search path let search_path = match search_path { Some(s) => std::path::Path::new(&s).to_owned(), None => dirs::home_dir().unwrap().join(".ssh/"), }; // Read config let config = fs::read(config_root.join("config")); if config.is_err() { return Ok(Self { config_dir: config_root.to_owned(), search_path, configs: BTreeSet::new() }) } let config = config.unwrap(); // Deserialize let root = Reader::get_root(config.as_slice())?; let mgr = ConfigManager::deserialize(root); if mgr.is_err() { return Ok(Self { config_dir: config_root, search_path, configs: BTreeSet::new() }) } let mgr = mgr.unwrap(); Ok(Self { config_dir: config_root, search_path, configs: mgr.configs.to_owned() }) } pub fn remove(&mut self, host: &Host) { self.configs.remove(host); self.save(); } pub fn insert(&mut self, host: Host) { self.configs.insert(host); self.save(); } pub fn save(&mut self) { let mut s = FlexbufferSerializer::new(); self.serialize(&mut s).unwrap(); fs::write(self.config_dir.to_owned().join("config"), s.view()).unwrap(); } pub fn new_host(&mut self) -> Result { let platform = input::get_platform()?; let host = platform.new_host(self)?; self.configs.insert(host.to_owned()); self.save(); Ok(host) } } impl Drop for ConfigManager { fn drop(&mut self) { self.save(); } }