1use std::env::{args_os, current_dir};
2use std::ffi::OsString;
3use std::fmt;
4use super::{ProgramArgs, RemoteLocation};
5
6fn extend_quoted(onto: &mut String, arg: &str) {
8 onto.push_str(" \'");
9 for ch in arg.chars() {
10 match ch {
11 '\'' => onto.push_str(r"'\''"),
12 '\\' => onto.push_str(r"'\\'"),
13 _ => onto.push(ch),
14 }
15 }
16 onto.push('\'');
17}
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum Location {
22 Remote(RemoteLocation),
24
25 Local,
27}
28impl Location {
29 pub fn into_args<I>(self, mut args: I) -> Result<ProgramArgs, ()>
31 where I: Iterator<Item = OsString> + ExactSizeIterator
32 {
33 args.next();
34 match self {
35 Location::Local => {
36 let mut prog_args = ProgramArgs::new(try!(args.next().ok_or(())));
37 prog_args.extend(args);
38 Ok(prog_args)
39 }
40
41 Location::Remote(remote) => {
42 if args.len() == 0 {
43 return Err(());
44 }
45
46 let mut prog_args = ProgramArgs::new("ssh");
47 prog_args.push("-qt");
48 prog_args.push(remote.host);
49
50 let mut cmd = String::from("cd");
51 extend_quoted(&mut cmd, try!(remote.path.to_str().ok_or(())));
52 cmd.push(';');
53 for arg in args {
54 extend_quoted(&mut cmd, try!(arg.to_str().ok_or(())));
55 }
56 prog_args.push(cmd);
57
58 Ok(prog_args)
59 }
60 }
61 }
62
63 #[inline]
65 pub fn into_env_args(self) -> Result<ProgramArgs, ()> {
66 self.into_args(args_os())
67 }
68}
69impl fmt::Display for Location {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 match *self {
72 Location::Local => fmt::Display::fmt(¤t_dir().unwrap().display(), f),
73 Location::Remote(ref remote) => fmt::Display::fmt(&remote, f),
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::{Location, extend_quoted};
81 use super::super::{ProgramArgs, RemoteLocation};
82
83 #[test]
84 fn quote_noescape() {
85 let mut v = String::from("never");
86 extend_quoted(&mut v, "gonna");
87 extend_quoted(&mut v, "give");
88 extend_quoted(&mut v, "you");
89 extend_quoted(&mut v, "up");
90 assert_eq!(v, r"never 'gonna' 'give' 'you' 'up'")
91 }
92
93 #[test]
94 fn quote_escape_quote() {
95 let mut v = String::from("echo");
96 extend_quoted(&mut v, "so");
97 extend_quoted(&mut v, "here's");
98 extend_quoted(&mut v, "a string");
99 extend_quoted(&mut v, "I'd");
100 extend_quoted(&mut v, "print");
101 assert_eq!(v, r"echo 'so' 'here'\''s' 'a string' 'I'\''d' 'print'")
102 }
103
104 #[test]
105 fn quote_escape_slash() {
106 let mut v = String::from("umm");
107 extend_quoted(&mut v, r"/test/");
108 extend_quoted(&mut v, r"\testing\");
109 extend_quoted(&mut v, r"test\\string");
110 assert_eq!(v, r"umm '/test/' ''\\'testing'\\'' 'test'\\''\\'string'")
111 }
112
113 #[test]
114 fn local_empty() {
115 let args = vec!["rrun"].into_iter().map(Into::into);
116 assert!(Location::Local.into_args(args).is_err());
117 }
118
119 #[test]
120 fn local_hello_world() {
121 let args = vec!["rrun", "echo", "Hello,", "world!"].into_iter().map(Into::into);
122
123 let mut test_args = ProgramArgs::new("echo");
124 test_args.push("Hello,");
125 test_args.push("world!");
126
127 assert_eq!(Location::Local.into_args(args), Ok(test_args));
128 }
129
130 #[test]
131 fn remote_empty() {
132 let remote = RemoteLocation::new("someone@example.com:/home/user").unwrap();
133
134 let args = vec!["rrun"].into_iter().map(Into::into);
135
136 assert!(Location::Remote(remote).into_args(args).is_err());
137 }
138
139 #[test]
140 fn remote_hello_world() {
141 let remote = RemoteLocation::new("someone@example.com:/home/user").unwrap();
142
143 let args = vec!["rrun", "echo", "Hello,", "world!"].into_iter().map(Into::into);
144
145 let mut test_args = ProgramArgs::new("ssh");
146 test_args.push("-qt");
147 test_args.push("someone@example.com");
148 test_args.push("cd '/home/user'; 'echo' 'Hello,' 'world!'");
149
150 assert_eq!(Location::Remote(remote).into_args(args), Ok(test_args));
151 }
152}