Skip to main content

ssm_core/
host.rs

1use crate::config::{Config, Host};
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5pub enum HostError {
6    #[error("host alias '{0}' already exists")]
7    DuplicateAlias(String),
8    #[error("host alias '{0}' not found")]
9    NotFound(String),
10}
11
12impl Config {
13    pub fn add_host(&mut self, host: Host) -> Result<(), HostError> {
14        if self.hosts.iter().any(|h| h.alias == host.alias) {
15            return Err(HostError::DuplicateAlias(host.alias));
16        }
17        self.hosts.push(host);
18        Ok(())
19    }
20
21    pub fn remove_host(&mut self, alias: &str) -> Result<Host, HostError> {
22        let idx = self
23            .hosts
24            .iter()
25            .position(|h| h.alias == alias)
26            .ok_or_else(|| HostError::NotFound(alias.into()))?;
27        Ok(self.hosts.remove(idx))
28    }
29
30    pub fn update_host(&mut self, alias: &str, updated: Host) -> Result<(), HostError> {
31        let idx = self
32            .hosts
33            .iter()
34            .position(|h| h.alias == alias)
35            .ok_or_else(|| HostError::NotFound(alias.into()))?;
36        if alias != updated.alias && self.hosts.iter().any(|h| h.alias == updated.alias) {
37            return Err(HostError::DuplicateAlias(updated.alias));
38        }
39        self.hosts[idx] = updated;
40        Ok(())
41    }
42
43    pub fn find_host(&self, alias: &str) -> Option<&Host> {
44        self.hosts.iter().find(|h| h.alias == alias)
45    }
46
47    pub fn find_host_mut(&mut self, alias: &str) -> Option<&mut Host> {
48        self.hosts.iter_mut().find(|h| h.alias == alias)
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use crate::config::Config;
56
57    fn make_host(alias: &str) -> Host {
58        Host {
59            alias: alias.into(),
60            hostname: "1.2.3.4".into(),
61            user: None,
62            port: 22,
63            identity_file: None,
64            tags: vec![],
65            notes: None,
66            tunnels: vec![],
67            commands: vec![],
68        }
69    }
70
71    #[test]
72    fn test_add_host() {
73        let mut config = Config::default();
74        config.add_host(make_host("server1")).unwrap();
75        assert_eq!(config.hosts.len(), 1);
76        assert_eq!(config.hosts[0].alias, "server1");
77    }
78
79    #[test]
80    fn test_add_duplicate_alias_fails() {
81        let mut config = Config::default();
82        config.add_host(make_host("server1")).unwrap();
83        let err = config.add_host(make_host("server1")).unwrap_err();
84        assert!(matches!(err, HostError::DuplicateAlias(_)));
85    }
86
87    #[test]
88    fn test_remove_host() {
89        let mut config = Config::default();
90        config.add_host(make_host("server1")).unwrap();
91        config.add_host(make_host("server2")).unwrap();
92        let removed = config.remove_host("server1").unwrap();
93        assert_eq!(removed.alias, "server1");
94        assert_eq!(config.hosts.len(), 1);
95    }
96
97    #[test]
98    fn test_remove_nonexistent_fails() {
99        let mut config = Config::default();
100        let err = config.remove_host("nope").unwrap_err();
101        assert!(matches!(err, HostError::NotFound(_)));
102    }
103
104    #[test]
105    fn test_update_host() {
106        let mut config = Config::default();
107        config.add_host(make_host("server1")).unwrap();
108        let mut updated = make_host("server1");
109        updated.hostname = "5.6.7.8".into();
110        config.update_host("server1", updated).unwrap();
111        assert_eq!(config.hosts[0].hostname, "5.6.7.8");
112    }
113
114    #[test]
115    fn test_update_host_rename() {
116        let mut config = Config::default();
117        config.add_host(make_host("old-name")).unwrap();
118        let renamed = make_host("new-name");
119        config.update_host("old-name", renamed).unwrap();
120        assert_eq!(config.hosts[0].alias, "new-name");
121    }
122
123    #[test]
124    fn test_update_host_rename_to_existing_fails() {
125        let mut config = Config::default();
126        config.add_host(make_host("server1")).unwrap();
127        config.add_host(make_host("server2")).unwrap();
128        let renamed = make_host("server2");
129        let err = config.update_host("server1", renamed).unwrap_err();
130        assert!(matches!(err, HostError::DuplicateAlias(_)));
131    }
132
133    #[test]
134    fn test_find_host() {
135        let mut config = Config::default();
136        config.add_host(make_host("server1")).unwrap();
137        assert!(config.find_host("server1").is_some());
138        assert!(config.find_host("nope").is_none());
139    }
140}