1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub enum SshHostSource {
8 Config,
10 KnownHosts,
12 History,
14 Mdns,
16}
17
18impl std::fmt::Display for SshHostSource {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 Self::Config => write!(f, "SSH Config"),
22 Self::KnownHosts => write!(f, "Known Hosts"),
23 Self::History => write!(f, "History"),
24 Self::Mdns => write!(f, "mDNS"),
25 }
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct SshHost {
32 pub alias: String,
34 pub hostname: Option<String>,
36 pub user: Option<String>,
38 pub port: Option<u16>,
40 pub identity_file: Option<String>,
42 pub proxy_jump: Option<String>,
44 pub source: SshHostSource,
46}
47
48impl SshHost {
49 pub fn display_name(&self) -> &str {
51 &self.alias
52 }
53
54 pub fn connection_target(&self) -> &str {
56 self.hostname.as_deref().unwrap_or(&self.alias)
57 }
58
59 pub fn ssh_args(&self) -> Vec<String> {
61 let mut args = Vec::new();
62
63 if let Some(port) = self.port
64 && port != 22
65 {
66 args.push("-p".to_string());
67 args.push(port.to_string());
68 }
69
70 if let Some(ref identity) = self.identity_file {
71 args.push("-i".to_string());
72 args.push(identity.clone());
73 }
74
75 if let Some(ref proxy) = self.proxy_jump {
76 args.push("-J".to_string());
77 args.push(proxy.clone());
78 }
79
80 let target = if let Some(ref user) = self.user {
81 format!("{}@{}", user, self.connection_target())
82 } else {
83 self.connection_target().to_string()
84 };
85 args.push(target);
86
87 args
88 }
89
90 pub fn connection_string(&self) -> String {
92 let mut s = String::new();
93 if let Some(ref user) = self.user {
94 s.push_str(user);
95 s.push('@');
96 }
97 s.push_str(self.connection_target());
98 if let Some(port) = self.port
99 && port != 22
100 {
101 s.push(':');
102 s.push_str(&port.to_string());
103 }
104 s
105 }
106}