1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
pub mod ssh_config; pub mod known_hosts; pub mod launchagent; pub mod errors; #[macro_use] extern crate error_chain; use std::path::{Path, PathBuf}; use std::io::BufReader; use std::io::prelude::*; use std::fs::File; use errors::*; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Host { name: String, protocol: String, } impl Host { pub fn new(name: &str, protocol: &str) -> Host { Host{ name: name.to_string(), protocol: protocol.to_string(), } } pub fn named(name: &str) -> Host { Host{ name: name.to_string(), protocol: "ssh".to_string(), } } pub fn write_bookmark(&self, dir: &Path) -> Result<()> { let name = format!("{} ({}).webloc", self.name, self.protocol); let namepart = Path::new(&name); let mut path = PathBuf::from(dir); if namepart.is_absolute() { bail!(ErrorKind::NameError(self.name.to_string(), self.protocol.to_string())); } path.push(namepart); let mut bookmark_text = String::new(); bookmark_text.push_str(self.protocol.as_str()); bookmark_text.push_str("://"); bookmark_text.push_str(self.name.as_str()); let bookmark = format!(r#"<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"><dict><key>URL</key><string>{}</string></dict></plist> "#, bookmark_text); let mut f = try!(File::create(path)); try!(f.write_all(bookmark.as_bytes())); Ok(()) } pub fn ineligible(&self) -> bool { self.name.contains('*') || self.name.contains('?') } } pub trait ConfigFile { fn pathname<'a>(&'a self) -> &'a Path; fn parse_entries<R: BufRead>(&self, r: R) -> Result<Vec<Host>>; fn entries(&self) -> Result<Vec<Host>> { let f = try!(File::open(self.pathname())); let file = BufReader::new(&f); self.parse_entries(file) } } pub fn process<T>(pathnames: Vec<String>) -> Result<Vec<Host>> where T: From<PathBuf> + ConfigFile { let mut hosts: Vec<Host> = vec![]; for pn in pathnames { let path = PathBuf::from(pn); let file = T::from(path); hosts.extend(try!(file.entries())); } Ok(hosts) } #[test] fn test_host_creation() { let ohai = Host::named("ohai"); assert_eq!(ohai.name, "ohai"); assert_eq!(ohai.protocol, "ssh"); let mosh_ohai = Host::new("ohai", "mosh"); assert_eq!(mosh_ohai.name, "ohai"); assert_eq!(mosh_ohai.protocol, "mosh"); } #[test] fn test_host_eligibility() { assert_eq!(Host::named("foo*.oink.example.com").ineligible(), true); assert_eq!(Host::named("*").ineligible(), true); assert_eq!(Host::named("foobar.oink.example.com").ineligible(), false); }