use std::io::Result;
use std::path::{Path, PathBuf};
use std::process::{Output, Child, ExitStatus, Command};
use shlex::Shlex;
#[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 mut cmd = cmd.stdin(std::process::Stdio::piped());
let mut 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()]]));
}
}