Merge human and shell format types

pull/38/head
Chip Senkbeil 3 years ago
parent 67b09c50ce
commit c7c9c237d7
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

@ -85,16 +85,11 @@ pub enum SessionSubcommand {
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, IsVariant)]
pub enum ResponseFormat {
/// Output responses in human-readable format
#[display(fmt = "human")]
Human,
/// Output responses in JSON format
#[display(fmt = "json")]
Json,
/// Provides special formatting to stdout & stderr that only
/// outputs that of the remotely-executed program
/// Output responses in a manner that makes sense from a shell
#[display(fmt = "shell")]
Shell,
}
@ -109,7 +104,6 @@ impl FromStr for ResponseFormat {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim() {
"human" => Ok(Self::Human),
"json" => Ok(Self::Json),
"shell" => Ok(Self::Shell),
x => Err(ResponseFormatParseError::InvalidVariant(x.to_string())),
@ -125,14 +119,13 @@ pub struct SendSubcommand {
///
/// Currently, there are two possible formats:
/// 1. "json": printing out JSON for external program usage
/// 2. "program": printing out verbatim all stdout and stderr of remotely-executed program
/// 3. "shell": printing out human-readable results for interactive shell usage
#[structopt(
short,
long,
value_name = "human|json|shell",
default_value = "human",
possible_values = &["human", "json", "shell"]
value_name = "json|shell",
default_value = "shell",
possible_values = &["json", "shell"]
)]
pub format: ResponseFormat,

@ -44,7 +44,7 @@ async fn run_async(cmd: SendSubcommand, _opt: CommonOpt) -> Result<(), Error> {
_ => 0,
};
print_response(cmd.format, res)?;
format_response(cmd.format, res)?.print();
// If we are executing a process and not detaching, we want to continue receiving
// responses sent to us
@ -79,7 +79,8 @@ async fn run_async(cmd: SendSubcommand, _opt: CommonOpt) -> Result<(), Error> {
)
})?;
let done = res.payload.is_proc_done();
print_response(cmd.format, res)?;
format_response(cmd.format, res)?.print();
if done {
break;
@ -90,35 +91,6 @@ async fn run_async(cmd: SendSubcommand, _opt: CommonOpt) -> Result<(), Error> {
Ok(())
}
fn print_response(fmt: ResponseFormat, res: Response) -> io::Result<()> {
// If we are not shell format or we are shell format and got stdout/stderr, we want
// to print out the results
let is_fmt_shell = fmt.is_shell();
let is_type_stderr = res.payload.is_proc_stderr();
let is_type_stdout = res.payload.is_proc_stdout();
let do_print = !is_fmt_shell || is_type_stderr || is_type_stdout;
let out = format_response(fmt, res)?;
// Print out our response if flagged to do so
if do_print {
// If we are shell format and got stderr, write it to stderr without altering content
if is_fmt_shell && is_type_stderr {
eprint!("{}", out);
// Else, if we are shell format and got stdout, write it to stdout without altering content
} else if is_fmt_shell && is_type_stdout {
print!("{}", out);
// Otherwise, always go to stdout with traditional println
} else {
println!("{}", out);
}
}
Ok(())
}
fn spawn_stdin_reader() -> mpsc::Receiver<String> {
let (tx, rx) = mpsc::channel(1);
@ -145,63 +117,81 @@ fn spawn_stdin_reader() -> mpsc::Receiver<String> {
rx
}
fn format_response(fmt: ResponseFormat, res: Response) -> io::Result<String> {
Ok(match fmt {
ResponseFormat::Human => format_human(res),
ResponseFormat::Json => serde_json::to_string(&res)
.map_err(|x| io::Error::new(io::ErrorKind::InvalidData, x))?,
ResponseFormat::Shell => format_shell(res),
})
/// Represents the output content and destination
enum ResponseOut {
Stdout(String),
Stderr(String),
None,
}
fn format_shell(res: Response) -> String {
match res.payload {
ResponsePayload::ProcStdout { data, .. } => String::from_utf8_lossy(&data).to_string(),
ResponsePayload::ProcStderr { data, .. } => String::from_utf8_lossy(&data).to_string(),
_ => String::new(),
impl ResponseOut {
pub fn print(self) {
match self {
Self::Stdout(x) => print!("{}", x),
Self::Stderr(x) => eprint!("{}", x),
Self::None => {}
}
}
}
fn format_human(res: Response) -> String {
fn format_response(fmt: ResponseFormat, res: Response) -> io::Result<ResponseOut> {
Ok(match fmt {
ResponseFormat::Json => ResponseOut::Stdout(
serde_json::to_string(&res)
.map_err(|x| io::Error::new(io::ErrorKind::InvalidData, x))?,
),
ResponseFormat::Shell => format_shell(res),
})
}
fn format_shell(res: Response) -> ResponseOut {
match res.payload {
ResponsePayload::Ok => "Done.".to_string(),
ResponsePayload::Error { description } => format!("Failed: '{}'.", description),
ResponsePayload::Blob { data } => String::from_utf8_lossy(&data).to_string(),
ResponsePayload::Text { data } => data,
ResponsePayload::DirEntries { entries } => entries
.into_iter()
.map(|entry| {
format!(
"{}{}",
entry.path.as_os_str().to_string_lossy(),
if entry.file_type.is_dir() {
std::path::MAIN_SEPARATOR.to_string()
} else {
String::new()
},
)
})
.collect::<Vec<String>>()
.join("\n"),
ResponsePayload::ProcEntries { entries } => entries
.into_iter()
.map(|entry| format!("{}: {} {}", entry.id, entry.cmd, entry.args.join(" ")))
.collect::<Vec<String>>()
.join("\n"),
ResponsePayload::ProcStart { id } => format!("Proc({}): Started.", id),
ResponsePayload::ProcStdout { id, data } => {
format!("Stdout({}): '{}'.", id, String::from_utf8_lossy(&data))
ResponsePayload::Ok => ResponseOut::None,
ResponsePayload::Error { description } => {
ResponseOut::Stderr(format!("Failed: '{}'.", description))
}
ResponsePayload::Blob { data } => {
ResponseOut::Stdout(String::from_utf8_lossy(&data).to_string())
}
ResponsePayload::Text { data } => ResponseOut::Stdout(data),
ResponsePayload::DirEntries { entries } => ResponseOut::Stdout(
entries
.into_iter()
.map(|entry| {
format!(
"{}{}",
entry.path.as_os_str().to_string_lossy(),
if entry.file_type.is_dir() {
std::path::MAIN_SEPARATOR.to_string()
} else {
String::new()
},
)
})
.collect::<Vec<String>>()
.join("\n"),
),
ResponsePayload::ProcEntries { entries } => ResponseOut::Stdout(
entries
.into_iter()
.map(|entry| format!("{}: {} {}", entry.id, entry.cmd, entry.args.join(" ")))
.collect::<Vec<String>>()
.join("\n"),
),
ResponsePayload::ProcStart { .. } => ResponseOut::None,
ResponsePayload::ProcStdout { data, .. } => {
ResponseOut::Stdout(String::from_utf8_lossy(&data).to_string())
}
ResponsePayload::ProcStderr { id, data } => {
format!("Stderr({}): '{}'.", id, String::from_utf8_lossy(&data))
ResponsePayload::ProcStderr { data, .. } => {
ResponseOut::Stderr(String::from_utf8_lossy(&data).to_string())
}
ResponsePayload::ProcDone { id, success, code } => {
if success {
format!("Proc({}): Done.", id)
ResponseOut::None
} else if let Some(code) = code {
format!("Proc({}): Failed with code {}.", id, code)
ResponseOut::Stderr(format!("Proc {} failed with code {}", id, code))
} else {
format!("Proc({}): Failed.", id)
ResponseOut::Stderr(format!("Proc {} failed", id))
}
}
}

Loading…
Cancel
Save