use std::env::{args_os, current_dir};
use std::ffi::OsString;
use std::fmt;
use super::{ProgramArgs, RemoteLocation};
fn extend_quoted(onto: &mut String, arg: &str) {
onto.push_str(" \'");
for ch in arg.chars() {
match ch {
'\'' => onto.push_str(r"'\''"),
'\\' => onto.push_str(r"'\\'"),
_ => onto.push(ch),
}
}
onto.push('\'');
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Location {
Remote(RemoteLocation),
Local,
}
impl Location {
pub fn into_args<I>(self, mut args: I) -> Result<ProgramArgs, ()>
where I: Iterator<Item = OsString> + ExactSizeIterator
{
args.next();
match self {
Location::Local => {
let mut prog_args = ProgramArgs::new(try!(args.next().ok_or(())));
prog_args.extend(args);
Ok(prog_args)
}
Location::Remote(remote) => {
if args.len() == 0 {
return Err(());
}
let mut prog_args = ProgramArgs::new("ssh");
prog_args.push("-qt");
prog_args.push(remote.host);
let mut cmd = String::from("cd");
extend_quoted(&mut cmd, try!(remote.path.to_str().ok_or(())));
cmd.push(';');
for arg in args {
extend_quoted(&mut cmd, try!(arg.to_str().ok_or(())));
}
prog_args.push(cmd);
Ok(prog_args)
}
}
}
#[inline]
pub fn into_env_args(self) -> Result<ProgramArgs, ()> {
self.into_args(args_os())
}
}
impl fmt::Display for Location {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Location::Local => fmt::Display::fmt(¤t_dir().unwrap().display(), f),
Location::Remote(ref remote) => fmt::Display::fmt(&remote, f),
}
}
}
#[cfg(test)]
mod tests {
use super::{Location, extend_quoted};
use super::super::{ProgramArgs, RemoteLocation};
#[test]
fn quote_noescape() {
let mut v = String::from("never");
extend_quoted(&mut v, "gonna");
extend_quoted(&mut v, "give");
extend_quoted(&mut v, "you");
extend_quoted(&mut v, "up");
assert_eq!(v, r"never 'gonna' 'give' 'you' 'up'")
}
#[test]
fn quote_escape_quote() {
let mut v = String::from("echo");
extend_quoted(&mut v, "so");
extend_quoted(&mut v, "here's");
extend_quoted(&mut v, "a string");
extend_quoted(&mut v, "I'd");
extend_quoted(&mut v, "print");
assert_eq!(v, r"echo 'so' 'here'\''s' 'a string' 'I'\''d' 'print'")
}
#[test]
fn quote_escape_slash() {
let mut v = String::from("umm");
extend_quoted(&mut v, r"/test/");
extend_quoted(&mut v, r"\testing\");
extend_quoted(&mut v, r"test\\string");
assert_eq!(v, r"umm '/test/' ''\\'testing'\\'' 'test'\\''\\'string'")
}
#[test]
fn local_empty() {
let args = vec!["rrun"].into_iter().map(Into::into);
assert!(Location::Local.into_args(args).is_err());
}
#[test]
fn local_hello_world() {
let args = vec!["rrun", "echo", "Hello,", "world!"].into_iter().map(Into::into);
let mut test_args = ProgramArgs::new("echo");
test_args.push("Hello,");
test_args.push("world!");
assert_eq!(Location::Local.into_args(args), Ok(test_args));
}
#[test]
fn remote_empty() {
let remote = RemoteLocation::new("someone@example.com:/home/user").unwrap();
let args = vec!["rrun"].into_iter().map(Into::into);
assert!(Location::Remote(remote).into_args(args).is_err());
}
#[test]
fn remote_hello_world() {
let remote = RemoteLocation::new("someone@example.com:/home/user").unwrap();
let args = vec!["rrun", "echo", "Hello,", "world!"].into_iter().map(Into::into);
let mut test_args = ProgramArgs::new("ssh");
test_args.push("-qt");
test_args.push("someone@example.com");
test_args.push("cd '/home/user'; 'echo' 'Hello,' 'world!'");
assert_eq!(Location::Remote(remote).into_args(args), Ok(test_args));
}
}