1#[derive(Debug, Clone, PartialEq)]
6pub struct Query {
7 pub user: Option<String>,
9 pub hosts: Vec<String>,
12 pub long: bool,
14 pub port: u16,
16}
17
18pub const DEFAULT_PORT: u16 = 79;
20
21impl Query {
22 pub fn parse(input: Option<&str>, long: bool, port: u16) -> Query {
32 let input = input.unwrap_or("");
33
34 if input.is_empty() {
35 return Query {
36 user: None,
37 hosts: vec!["localhost".to_string()],
38 long,
39 port,
40 };
41 }
42
43 let parts: Vec<&str> = input.splitn(2, '@').collect();
45
46 if parts.len() == 1 {
47 return Query {
49 user: Some(parts[0].to_string()),
50 hosts: vec!["localhost".to_string()],
51 long,
52 port,
53 };
54 }
55
56 let user = if parts[0].is_empty() {
58 None
59 } else {
60 Some(parts[0].to_string())
61 };
62
63 let hosts: Vec<String> = parts[1].split('@').map(|s| s.to_string()).collect();
64
65 Query {
66 user,
67 hosts,
68 long,
69 port,
70 }
71 }
72
73 pub fn target_host(&self) -> &str {
75 self.hosts.last().expect("hosts must not be empty")
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn parse_user_at_host() {
85 let q = Query::parse(Some("user@host"), false, 79);
86 assert_eq!(q.user, Some("user".to_string()));
87 assert_eq!(q.hosts, vec!["host".to_string()]);
88 assert!(!q.long);
89 assert_eq!(q.port, 79);
90 }
91
92 #[test]
93 fn parse_at_host_lists_users() {
94 let q = Query::parse(Some("@host"), false, 79);
95 assert_eq!(q.user, None);
96 assert_eq!(q.hosts, vec!["host".to_string()]);
97 }
98
99 #[test]
100 fn parse_user_only_defaults_to_localhost() {
101 let q = Query::parse(Some("user"), false, 79);
102 assert_eq!(q.user, Some("user".to_string()));
103 assert_eq!(q.hosts, vec!["localhost".to_string()]);
104 }
105
106 #[test]
107 fn parse_empty_string_defaults_to_localhost() {
108 let q = Query::parse(Some(""), false, 79);
109 assert_eq!(q.user, None);
110 assert_eq!(q.hosts, vec!["localhost".to_string()]);
111 }
112
113 #[test]
114 fn parse_none_defaults_to_localhost() {
115 let q = Query::parse(None, false, 79);
116 assert_eq!(q.user, None);
117 assert_eq!(q.hosts, vec!["localhost".to_string()]);
118 }
119
120 #[test]
121 fn parse_forwarding_chain() {
122 let q = Query::parse(Some("user@host1@host2"), false, 79);
123 assert_eq!(q.user, Some("user".to_string()));
124 assert_eq!(q.hosts, vec!["host1".to_string(), "host2".to_string()]);
125 }
126
127 #[test]
128 fn parse_forwarding_chain_no_user() {
129 let q = Query::parse(Some("@host1@host2"), false, 79);
130 assert_eq!(q.user, None);
131 assert_eq!(q.hosts, vec!["host1".to_string(), "host2".to_string()]);
132 }
133
134 #[test]
135 fn parse_long_flag_preserved() {
136 let q = Query::parse(Some("user@host"), true, 79);
137 assert!(q.long);
138 }
139
140 #[test]
141 fn parse_custom_port_preserved() {
142 let q = Query::parse(Some("user@host"), false, 7979);
143 assert_eq!(q.port, 7979);
144 }
145
146 #[test]
147 fn target_host_returns_last_host() {
148 let q = Query::parse(Some("user@host1@host2"), false, 79);
149 assert_eq!(q.target_host(), "host2");
150 }
151
152 #[test]
153 fn target_host_single_host() {
154 let q = Query::parse(Some("user@host"), false, 79);
155 assert_eq!(q.target_host(), "host");
156 }
157
158 #[test]
159 fn parse_three_host_chain() {
160 let q = Query::parse(Some("user@a@b@c"), false, 79);
161 assert_eq!(q.user, Some("user".to_string()));
162 assert_eq!(
163 q.hosts,
164 vec!["a".to_string(), "b".to_string(), "c".to_string()]
165 );
166 assert_eq!(q.target_host(), "c");
167 }
168}