Skip to main content

par_term_ssh/
discovery.rs

1//! SSH host discovery aggregator.
2
3use super::config_parser;
4use super::history;
5use super::known_hosts;
6use super::types::SshHost;
7use std::collections::HashSet;
8
9/// Discover SSH hosts from all local sources (config, known_hosts, history).
10pub 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}