use std::{
collections::HashMap,
env, fs,
io::{self, Read},
path::{Path, PathBuf},
};
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};
use crate::{
conn_expr::ConnectionExpr,
error::Error,
host_options::{HostKey, HostOptions, HostOptionsTable},
};
pub fn load_default_hosts_files() -> Result<HostOptionsTable, Error> {
let mut hosts = HostOptionsTable::default();
let ps = matching_config_files("nreplops-hosts.toml")
.map_err(Error::FailedToLoadDefaultHostConfig)?;
for p in ps.into_iter().rev() {
let mut f =
fs::File::open(p).map_err(Error::FailedToLoadDefaultHostConfig)?;
let mut s = String::new();
let _ = f
.read_to_string(&mut s)
.map_err(Error::FailedToLoadDefaultHostConfig)?;
let new_hosts: Hosts = toml::from_str(&s).unwrap();
hosts.extend(new_hosts.into_iter().map(|(k, v)| (k, v.into())));
}
Ok(hosts)
}
pub type Hosts = HashMap<HostKey, HostOptionsDe>;
#[serde_as]
#[derive(Debug, Deserialize)]
pub struct HostOptionsDe {
name: Option<String>,
#[serde_as(as = "DisplayFromStr")]
connection: ConnectionExpr,
confirm: Option<bool>,
}
#[allow(clippy::from_over_into)]
impl Into<HostOptions> for HostOptionsDe {
fn into(self) -> HostOptions {
HostOptions {
name: self.name,
conn_expr: self.connection,
ask_confirmation: self.confirm,
}
}
}
fn matching_config_files<P>(file_name: P) -> Result<Vec<PathBuf>, io::Error>
where
P: AsRef<Path>,
{
let mut found = Vec::new();
let mut current = PathBuf::from(".").canonicalize()?;
loop {
let mut p = current.clone();
p.push(file_name.as_ref());
if p.is_file() {
found.push(p);
}
if !current.pop() {
break;
}
}
if let Some(dir) = env::var_os("HOME") {
let mut p = PathBuf::from(dir);
if p.is_absolute() {
p.push("Library");
p.push("Application Support");
p.push("nreplops");
p.push(file_name.as_ref());
if p.is_file() {
found.push(p);
}
}
}
if let Some(dir) = env::var_os("XDG_CONFIG_HOME") {
let mut p = PathBuf::from(dir);
if p.is_absolute() {
p.push("nreplops");
p.push(file_name.as_ref());
if p.is_file() {
found.push(p);
}
}
}
if let Some(dir) = env::var_os("HOME") {
let mut p = PathBuf::from(dir);
if p.is_absolute() {
p.push(".nreplops");
p.push(file_name.as_ref());
if p.is_file() {
found.push(p);
}
}
}
Ok(found)
}