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}