Skip to content

Commit d2e8327

Browse files
committed
fix includes parsing
1 parent 0ec119f commit d2e8327

File tree

1 file changed

+60
-28
lines changed

1 file changed

+60
-28
lines changed

src/ssh_config/parser.rs

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ impl Parser {
5959

6060
fn parse_raw(&self, reader: &mut impl BufRead) -> Result<(Host, Vec<Host>), ParseError> {
6161
let mut global_host = Host::new(Vec::new());
62-
let mut is_in_host_block = false;
6362
let mut hosts = Vec::new();
63+
let mut current_host: Option<Host> = None;
6464

6565
let mut line = String::new();
6666
while reader.read_line(&mut line)? > 0 {
@@ -84,10 +84,16 @@ impl Parser {
8484
}
8585
}
8686
EntryType::Host => {
87+
if let Some(host) = current_host.take() {
88+
hosts.push(host);
89+
}
8790
let patterns = parse_patterns(&entry.1);
88-
hosts.push(Host::new(patterns));
89-
is_in_host_block = true;
90-
91+
if patterns.contains(&"*".to_string()) {
92+
global_host = Host::new(patterns.clone());
93+
hosts.push(global_host.clone());
94+
} else {
95+
current_host = Some(Host::new(patterns));
96+
}
9197
continue;
9298
}
9399
EntryType::Include => {
@@ -124,41 +130,25 @@ impl Parser {
124130
let mut file = BufReader::new(File::open(path)?);
125131
let (included_global_host, included_hosts) = self.parse_raw(&mut file)?;
126132

127-
if is_in_host_block {
128-
// Can't include hosts inside a host block
129-
if !included_hosts.is_empty() {
130-
return Err(InvalidIncludeError {
131-
line,
132-
details: InvalidIncludeErrorDetails::HostsInsideHostBlock,
133-
}
134-
.into());
135-
}
136-
137-
hosts
138-
.last_mut()
139-
.unwrap()
140-
.extend_entries(&included_global_host);
141-
} else {
142-
if !included_global_host.is_empty() {
143-
global_host.extend_entries(&included_global_host);
144-
}
145-
146-
hosts.extend(included_hosts);
147-
}
133+
global_host.extend_entries(&included_global_host);
134+
hosts.extend(included_hosts);
148135
}
149-
150136
continue;
151137
}
152138
_ => {}
153139
}
154140

155-
if is_in_host_block {
156-
hosts.last_mut().unwrap().update(entry);
141+
if let Some(host) = current_host.as_mut() {
142+
host.update(entry);
157143
} else {
158144
global_host.update(entry);
159145
}
160146
}
161147

148+
if let Some(host) = current_host {
149+
hosts.push(host);
150+
}
151+
162152
Ok((global_host, hosts))
163153
}
164154
}
@@ -218,3 +208,45 @@ fn parse_patterns(entry_value: &str) -> Vec<String> {
218208

219209
patterns
220210
}
211+
212+
#[cfg(test)]
213+
mod tests {
214+
use super::*;
215+
use std::io::Cursor;
216+
217+
#[test]
218+
fn test_basic_host_parsing() {
219+
let config = "\nHost example\n User test\n Port 2222\n";
220+
let mut reader = Cursor::new(config);
221+
let result = Parser::new().parse_raw(&mut reader);
222+
assert!(result.is_ok());
223+
let (_, hosts) = result.unwrap();
224+
assert_eq!(hosts.len(), 1);
225+
let host = hosts.first().unwrap();
226+
assert!(host.get(&EntryType::User).is_some());
227+
assert_eq!(host.get(&EntryType::User).unwrap(), "test");
228+
assert!(host.get(&EntryType::Port).is_some());
229+
assert_eq!(host.get(&EntryType::Port).unwrap(), "2222");
230+
}
231+
232+
#[test]
233+
fn test_include_directive() {
234+
let config = "\nInclude other_config\nHost example\n User test\n";
235+
let mut reader = Cursor::new(config);
236+
let result = Parser::new().parse_raw(&mut reader);
237+
assert!(result.is_ok());
238+
}
239+
240+
#[test]
241+
fn test_host_wildcard() {
242+
let config = "\nHost *\n Compression yes\nHost test\n Compression no\n";
243+
let mut reader = Cursor::new(config);
244+
let result = Parser::new().parse_raw(&mut reader);
245+
assert!(result.is_ok());
246+
let (global_host, hosts) = result.unwrap();
247+
assert!(!global_host.is_empty());
248+
assert!(global_host.get(&EntryType::Compression).is_some());
249+
assert_eq!(global_host.get(&EntryType::Compression).unwrap(), "yes");
250+
assert_eq!(hosts.len(), 2);
251+
}
252+
}

0 commit comments

Comments
 (0)