use std::io::Result;
use std::path::PathBuf;
use std::process::{Child, Command, ExitStatus, Output};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Remote {
pub addr: String,
pub port: u16,
}
pub trait RemoteCommand {
fn remote_output(&mut self, remote: &Remote) -> Result<Output>;
fn remote_spawn(&mut self, remote: &Remote) -> Result<Child>;
fn remote_status(&mut self, remote: &Remote) -> Result<ExitStatus>;
}
fn reconstruct_ssh_command(remote: &Remote, command: &Command) -> Command {
let mut cmd = Command::new("ssh");
cmd.arg("-p").arg(remote.port.to_string());
cmd.arg(remote.addr.clone());
let path: PathBuf = [
home::home_dir().unwrap().to_str().unwrap(),
".ssh",
"cs6991",
"cs6991-id",
]
.iter()
.collect();
if path.is_file() {
cmd.arg("-i");
cmd.arg(path.as_os_str());
}
cmd.arg("--");
cmd.arg(command.get_program());
cmd.args(command.get_args());
cmd
}
impl RemoteCommand for Command {
fn remote_output(&mut self, remote: &Remote) -> Result<Output> {
let mut cmd = reconstruct_ssh_command(remote, self);
cmd.output()
}
fn remote_spawn(&mut self, remote: &Remote) -> Result<Child> {
let mut cmd = reconstruct_ssh_command(remote, self);
let cmd = cmd.stdin(std::process::Stdio::piped());
let cmd = cmd.stdout(std::process::Stdio::piped());
cmd.spawn()
}
fn remote_status(&mut self, remote: &Remote) -> Result<ExitStatus> {
let mut cmd = reconstruct_ssh_command(remote, self);
cmd.status()
}
}
pub fn parse_line(s: &str) -> Option<Vec<Vec<String>>> {
let mut cmd = vec![];
let mut cmds = vec![];
for token in shlex::split(s)? {
if token == ";" && !cmd.is_empty() {
cmds.push(cmd);
cmd = vec![];
} else {
let starts_with_split = token.starts_with(';');
let ends_with_split = token.ends_with(';');
let token = token.trim_matches(';').to_string();
if starts_with_split && !cmd.is_empty() {
cmds.push(cmd);
cmd = vec![];
}
if !token.is_empty() {
cmd.push(token);
}
if ends_with_split && !cmd.is_empty() {
cmds.push(cmd);
cmd = vec![];
}
}
}
if !cmd.is_empty() {
cmds.push(cmd);
}
Some(cmds)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_line() {
assert_eq!(
parse_line("echo hello"),
Some(vec![vec!["echo".to_string(), "hello".to_string()]])
);
assert_eq!(
parse_line("echo hello; echo world"),
Some(vec![
vec!["echo".to_string(), "hello".to_string()],
vec!["echo".to_string(), "world".to_string()]
])
);
assert_eq!(
parse_line("echo hello; echo world;"),
Some(vec![
vec!["echo".to_string(), "hello".to_string()],
vec!["echo".to_string(), "world".to_string()]
])
);
assert_eq!(
parse_line("echo hello; echo world; "),
Some(vec![
vec!["echo".to_string(), "hello".to_string()],
vec!["echo".to_string(), "world".to_string()]
])
);
assert_eq!(
parse_line("echo hello; echo world; echo"),
Some(vec![
vec!["echo".to_string(), "hello".to_string()],
vec!["echo".to_string(), "world".to_string()],
vec!["echo".to_string()]
])
);
assert_eq!(
parse_line("echo hello ; echo world; echo;"),
Some(vec![
vec!["echo".to_string(), "hello".to_string()],
vec!["echo".to_string(), "world".to_string()],
vec!["echo".to_string()]
])
);
assert_eq!(
parse_line("echo hello ;echo world; echo ;"),
Some(vec![
vec!["echo".to_string(), "hello".to_string()],
vec!["echo".to_string(), "world".to_string()],
vec!["echo".to_string()]
])
);
assert_eq!(
parse_line("echo hello; echo world; echo ; "),
Some(vec![
vec!["echo".to_string(), "hello".to_string()],
vec!["echo".to_string(), "world".to_string()],
vec!["echo".to_string()]
])
);
assert_eq!(
parse_line("echo hello; echo world; echo ; ;"),
Some(vec![
vec!["echo".to_string(), "hello".to_string()],
vec!["echo".to_string(), "world".to_string()],
vec!["echo".to_string()]
])
);
assert_eq!(
parse_line("echo 'hello; yeet'; echo world; echo ; ;"),
Some(vec![
vec!["echo".to_string(), "hello; yeet".to_string()],
vec!["echo".to_string(), "world".to_string()],
vec!["echo".to_string()]
])
);
}
}