use anyhow::Result; 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!("{} via {}@{}", self.platform, self.user, self.host)) } } impl Host { pub fn edit(&mut self) -> Result { let mut data = toml::to_string_pretty(self)?; { let t = mktemp::Temp::new_file().unwrap(); fs::write(t.to_owned(), data)?; let editor = std::env::var("EDITOR").expect("EDITOR is not set"); std::process::Command::new(editor) .arg(t.to_owned().to_string_lossy().to_string()) .spawn()?.wait()?; data = fs::read_to_string(t)?; } Ok(toml::from_str(&data)?) } } #[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 { let config_root = match given_config_root { Some(c) => path::Path::new(&c).to_owned(), None => dirs::data_local_dir().unwrap().join("keyman/"), }; if !fs::try_exists(config_root.to_owned()).unwrap_or(false) { fs::create_dir_all(config_root.to_owned())?; } let search_path = match search_path { Some(s) => std::path::Path::new(&s).to_owned(), None => dirs::home_dir().unwrap().join(".ssh/"), }; match fs::read(config_root.join("config")) { Ok(c) => { let root = Reader::get_root(c.as_slice())?; match ConfigManager::deserialize(root) { Ok(mut mgr) => { mgr.config_dir = config_root; mgr.search_path = search_path; Ok(mgr) }, Err(_) => Ok(Self { config_dir: config_root, search_path, configs: BTreeSet::new() }), } } Err(_) => Ok(Self { config_dir: config_root, search_path, configs: BTreeSet::new() }), } } 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(); } }