par_term_ssh/
discovery.rs1use super::config_parser;
4use super::history;
5use super::known_hosts;
6use super::types::SshHost;
7use std::collections::HashSet;
8
9pub fn discover_local_hosts() -> Vec<SshHost> {
11 let mut all_hosts = Vec::new();
12 let mut seen = HashSet::new();
13
14 if let Some(home) = dirs::home_dir() {
15 let ssh_dir = home.join(".ssh");
16
17 let config_path = ssh_dir.join("config");
18 if config_path.exists() {
19 for host in config_parser::parse_ssh_config(&config_path) {
20 let key = dedup_key(&host);
21 if seen.insert(key) {
22 all_hosts.push(host);
23 }
24 }
25 }
26
27 let known_hosts_path = ssh_dir.join("known_hosts");
28 if known_hosts_path.exists() {
29 for host in known_hosts::parse_known_hosts(&known_hosts_path) {
30 let key = dedup_key(&host);
31 if seen.insert(key) {
32 all_hosts.push(host);
33 }
34 }
35 }
36 }
37
38 for host in history::scan_history() {
39 let key = dedup_key(&host);
40 if seen.insert(key) {
41 all_hosts.push(host);
42 }
43 }
44
45 all_hosts
46}
47
48fn dedup_key(host: &SshHost) -> String {
49 let target = host.hostname.as_deref().unwrap_or(&host.alias);
50 let port = host.port.unwrap_or(22);
51 format!("{}:{}", target.to_lowercase(), port)
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57 use crate::types::SshHostSource;
58
59 #[test]
60 fn test_dedup_key() {
61 let host = SshHost {
62 alias: "myhost".to_string(),
63 hostname: Some("MyHost.Example.COM".to_string()),
64 user: None,
65 port: None,
66 identity_file: None,
67 proxy_jump: None,
68 source: SshHostSource::Config,
69 };
70 assert_eq!(dedup_key(&host), "myhost.example.com:22");
71 }
72
73 #[test]
74 fn test_dedup_key_with_port() {
75 let host = SshHost {
76 alias: "myhost".to_string(),
77 hostname: Some("myhost.example.com".to_string()),
78 user: None,
79 port: Some(2222),
80 identity_file: None,
81 proxy_jump: None,
82 source: SshHostSource::Config,
83 };
84 assert_eq!(dedup_key(&host), "myhost.example.com:2222");
85 }
86}