use crate::SshAuth;
use anyhow::Result;
use serde::{Deserialize, Deserializer};
use std::net::{IpAddr, ToSocketAddrs};
#[derive(Deserialize)]
#[serde(from = "MasshHostConfigEnum")]
pub struct MasshHostConfig {
pub addr: IpAddr,
pub auth: Option<SshAuth>,
pub port: Option<u16>,
pub user: Option<String>,
}
#[derive(Deserialize)]
pub struct MasshConfig {
pub default_auth: SshAuth,
pub default_port: u16,
pub default_user: String,
pub threads: u64,
pub timeout: u64,
pub hosts: Vec<MasshHostConfig>,
}
impl MasshConfig {
pub fn from_json(json: &str) -> Result<Self> {
let config: MasshConfig = serde_json::from_str(json)?;
Ok(config)
}
pub fn from_yaml(yaml: &str) -> Result<Self> {
let config: MasshConfig = serde_yaml::from_str(yaml)?;
Ok(config)
}
}
#[derive(Deserialize)]
struct InnerMasshHostConfig {
addr: IpAddr,
auth: Option<SshAuth>,
port: Option<u16>,
user: Option<String>,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum MasshHostConfigEnum {
FromMap(InnerMasshHostConfig),
#[serde(deserialize_with = "deserialize_host_from_str")]
FromStr(InnerMasshHostConfig),
}
impl From<MasshHostConfigEnum> for MasshHostConfig {
fn from(e: MasshHostConfigEnum) -> MasshHostConfig {
let inner = match e {
MasshHostConfigEnum::FromMap(inner) => inner,
MasshHostConfigEnum::FromStr(inner) => inner,
};
MasshHostConfig {
addr: inner.addr,
auth: inner.auth,
port: inner.port,
user: inner.user,
}
}
}
fn deserialize_host_from_str<'de, D>(deserializer: D) -> Result<InnerMasshHostConfig>
where
D: Deserializer<'de>,
{
let e = anyhow::anyhow!("String deserialization failed");
let value = match String::deserialize(deserializer) {
Ok(value) => value,
Err(_) => return Err(e),
};
let (user, value) = match value.split_once('@') {
Some((left, right)) => (Some(left.to_owned()), right.to_owned()),
None => (None, value),
};
let (mut addrs, no_port) = if let Ok(addrs) = value.to_socket_addrs() {
(addrs, false)
} else if let Ok(addrs) = format!("{}:22", value).to_socket_addrs() {
(addrs, true)
} else {
return Err(e);
};
let socket = match addrs.next() {
Some(socket) => socket,
None => return Err(e),
};
let addr = socket.ip();
let port = if no_port { None } else { Some(socket.port()) };
Ok(InnerMasshHostConfig {
addr,
auth: None,
port,
user,
})
}