ssh_bookmarker 1.0.0

A tool that lets you automatically generate SSH bookmarks under macOS
Documentation
use std::env;
use ::errors::*;

pub fn create(configs: Vec<String>, known_hosts: Vec<String>, output: String) -> Result<String> {
    let curr_exe = env::current_exe().
        chain_err(|| "Couldn't determine the currently running program")?;
    let exe = curr_exe.
        to_str().ok_or("How did you get a non-unicodeable executable name?")?;
    Ok(create_for_exe(exe, configs, known_hosts, output.as_str()))
}

fn command_lineify<'a>(prefix: &'a str, args: &[&'a str]) -> Vec<&'a str> {
    let prefix_iter = vec![prefix].into_iter().cycle();
    prefix_iter.zip(args.into_iter()).flat_map(|(p, arg)| vec![p, arg]).collect()
}

fn plist_stringify(args: &[&str]) -> String {
    if args.len() > 0 {
        let mut interspersed = String::from("<string>");
        interspersed.push_str(args.join("</string><string>").as_str());
        interspersed.push_str("</string>");
        interspersed
    } else {
        String::from("")
    }
}

fn create_for_exe(exe: &str, configs: Vec<String>, known_hosts: Vec<String>, output: &str) -> String {
    let configs: Vec<&str> = configs.iter().map(|&ref s| s.as_str()).collect();
    let config_slice = configs.as_slice();
    let known_hosts: Vec<&str> = known_hosts.iter().map(|&ref s| s.as_str()).collect();
    let known_hosts_slice = known_hosts.as_slice();

    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>Label</key>
    <string>net.boinkor.ssh-bookmarker</string>
    <key>ProgramArguments</key>
    <array><string>{}</string><string>create</string>{}{}{}</array>
    <key>QueueDirectories</key>
    <array/>
    <key>RunAtLoad</key>
    <true/>
    <key>StartCalendarInterval</key>
    <dict>
      <key>Hour</key>
      <integer>0</integer>
      <key>Minute</key>
      <integer>0</integer>
    </dict>
    <key>WatchPaths</key>
    <array>{}{}</array>
  </dict>
</plist>"##,
            exe,
            plist_stringify(command_lineify("-c", config_slice).as_slice()),
            plist_stringify(command_lineify("-k", known_hosts_slice).as_slice()),
            plist_stringify(vec![output].as_slice()),
            plist_stringify(config_slice), plist_stringify(known_hosts_slice))
}

#[test]
fn test_command_lineify() {
    let expected: Vec<String> = ["-c", "foo", "-c", "bar"].iter().map(|&s| s.into()).collect();
    assert_eq!(command_lineify("-c", &["foo", "bar"]), expected);
}

#[test]
fn test_plist_stringify() {
    assert_eq!(plist_stringify(&["foo", "bar"]), "<string>foo</string><string>bar</string>".to_string());
    let empty: Vec<&str> = vec![];
    assert_eq!(plist_stringify(empty.as_slice()), "".to_string());
}

#[test]
fn test_create_for_exe() {
    assert_eq!(create_for_exe("program", vec!["/etc/ssh/ssh_config".to_string()], vec!["/etc/ssh/ssh_known_hosts".to_string()], "/tmp/foo"),
    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>Label</key>
    <string>net.boinkor.ssh-bookmarker</string>
    <key>ProgramArguments</key>
    <array><string>program</string><string>create</string><string>-c</string><string>/etc/ssh/ssh_config</string><string>-k</string><string>/etc/ssh/ssh_known_hosts</string><string>/tmp/foo</string></array>
    <key>QueueDirectories</key>
    <array/>
    <key>RunAtLoad</key>
    <true/>
    <key>StartCalendarInterval</key>
    <dict>
      <key>Hour</key>
      <integer>0</integer>
      <key>Minute</key>
      <integer>0</integer>
    </dict>
    <key>WatchPaths</key>
    <array><string>/etc/ssh/ssh_config</string><string>/etc/ssh/ssh_known_hosts</string></array>
  </dict>
</plist>"##)
}