use std::fmt;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RemoteForward {
pub listen: RemoteForwardListen,
pub destination: Option<RemoteForwardDestination>,
}
impl RemoteForward {
pub fn new(listen: RemoteForwardListen, destination: Option<RemoteForwardDestination>) -> Self {
Self {
listen,
destination,
}
}
}
impl fmt::Display for RemoteForward {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{listen}", listen = self.listen)?;
if let Some(destination) = &self.destination {
write!(f, " {destination}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RemoteForwardListen {
Port(u16),
Host {
host: String,
port: u16,
},
UnixSocket(PathBuf),
}
impl fmt::Display for RemoteForwardListen {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Port(port) => write!(f, "{port}"),
Self::Host { host, port } => write_host_port(f, host, *port),
Self::UnixSocket(path) => write_socket_path(f, path),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RemoteForwardDestination {
Host {
host: String,
port: u16,
},
UnixSocket(PathBuf),
}
impl fmt::Display for RemoteForwardDestination {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Host { host, port } => write_host_port(f, host, *port),
Self::UnixSocket(path) => write_socket_path(f, path),
}
}
}
fn write_host_port(f: &mut fmt::Formatter<'_>, host: &str, port: u16) -> fmt::Result {
if host.contains(':') {
write!(f, "[{host}]:{port}")
} else {
write!(f, "{host}:{port}")
}
}
fn write_socket_path(f: &mut fmt::Formatter<'_>, path: &Path) -> fmt::Result {
let path = path.display().to_string();
if path.chars().any(char::is_whitespace) {
write!(
f,
"\"{path}\"",
path = path.replace('\\', "\\\\").replace('"', "\\\"")
)
} else {
write!(f, "{path}")
}
}