You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
3.2 KiB
120 lines
3.2 KiB
use std::collections::HashMap; |
|
use std::fs::read_to_string; |
|
|
|
struct LineLexer { |
|
linenum: usize, |
|
content: Vec<String>, |
|
current: String, |
|
previous: String, |
|
} |
|
|
|
impl LineLexer { |
|
pub fn new(data: String) -> Self { |
|
Self { |
|
linenum: 0, |
|
content: data.lines().map(String::from).collect(), |
|
current: String::new(), |
|
previous: String::new(), |
|
} |
|
} |
|
|
|
pub fn next(&mut self) -> Option<String> { |
|
let line = self.content.get(self.linenum); |
|
if line.is_some() { |
|
self.previous = self.current.clone(); |
|
self.current = line.unwrap().trim().to_string(); |
|
self.linenum += 1; |
|
} |
|
line.map(|s| s.clone()) |
|
} |
|
|
|
pub fn next_if_nonempty(&mut self) -> Option<String> { |
|
let l = self.next(); |
|
if l.is_none() || l.as_ref().unwrap().len() > 0 { |
|
l |
|
} else { |
|
None |
|
} |
|
} |
|
|
|
pub fn next_nonempty(&mut self) -> Option<String> { |
|
loop { |
|
let l = self.next(); |
|
if l.is_none() { |
|
return l; |
|
} |
|
if l.as_ref().unwrap().len() > 0 { |
|
return l; |
|
} |
|
} |
|
} |
|
} |
|
|
|
#[derive(Debug)] |
|
pub struct TinySSHHost { |
|
hostname: String, |
|
entries: HashMap<String, String>, |
|
} |
|
|
|
impl TinySSHHost { |
|
pub fn new(hostname: String) -> Self { |
|
TinySSHHost { |
|
hostname, |
|
entries: HashMap::new(), |
|
} |
|
} |
|
|
|
pub fn get(&self, k: &str) -> Option<&String> { |
|
self.entries.get(&k.to_string()) |
|
} |
|
|
|
pub fn can_be_proxy(&self) -> bool { |
|
self.get("DynamicForward").is_some() |
|
} |
|
} |
|
|
|
pub struct TinySSHConfig { |
|
hosts: Vec<TinySSHHost>, |
|
} |
|
|
|
impl TinySSHConfig { |
|
pub fn parse<S: Into<String>>(filename: S) -> Result<Self, String> { |
|
let data = read_to_string(filename.into()).map_err(|e| e.to_string())?; |
|
let mut tsc = Self { hosts: vec![] }; |
|
let mut lexer = LineLexer::new(data); |
|
loop { |
|
let line = lexer.next_nonempty(); |
|
if line.is_none() { |
|
break; |
|
} |
|
let mut host = if lexer.current.starts_with("Host") { |
|
match lexer.current.split_whitespace().nth(1) { |
|
Some(h) => TinySSHHost::new(h.trim().to_string()), |
|
None => return Err("No hostname found".to_string()), |
|
} |
|
} else { |
|
return Err("Wrong section order".to_string()); |
|
}; |
|
loop { |
|
let l = lexer.next_if_nonempty(); |
|
if l.is_none() { |
|
break; |
|
} |
|
let l = l.unwrap(); |
|
let mut param: Vec<String> = l.trim().split(' ').map(String::from).collect(); |
|
if param.len() != 2 { |
|
return Err(format!("Wrong param format: {}", l)); |
|
} |
|
let v = param.pop().unwrap(); |
|
let k = param.pop().unwrap(); |
|
host.entries.insert(k, v); |
|
} |
|
tsc.hosts.push(host); |
|
} |
|
Ok(tsc) |
|
} |
|
|
|
pub fn get_host(&self, host: &str) -> Option<&TinySSHHost> { |
|
self.hosts.iter().find(|h| h.hostname == host) |
|
} |
|
}
|
|
|