From 3e0030b85fee97bdc4912bcef388941e6c410edb Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Mon, 8 May 2023 16:59:45 -0400 Subject: [PATCH] Add support for stderr and split output handling. More percent sign doubling support. --- example.rsh | 6 +++- src/main.rs | 99 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 75 insertions(+), 30 deletions(-) diff --git a/example.rsh b/example.rsh index 5561b3a..9d10de4 100755 --- a/example.rsh +++ b/example.rsh @@ -13,9 +13,13 @@ if %echo some words%.contains("me wo") { let mem = %free -h%.lines().nth(1).unwrap() .split_whitespace().nth(3).unwrap().to_owned(); +let (_stdout, _stderr) = %^curl bob%; +println!("Collect stdout and stderr separately!"); + // for i in 0..10000 { // println!("x contains %% example? [{}]", x.contains("example")); // } +%echo Double-up your percent signs to display them literally 100%%!% + println!("There are {} of memory free", &mem); -%echo hello% diff --git a/src/main.rs b/src/main.rs index 30e183d..3cd99b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -#![feature(option_result_contains)] - use anyhow::{bail, Result}; use std::env; @@ -16,12 +14,12 @@ use std::process::Command; #[derive(PartialEq, Clone)] enum PrintType { - Out, Err, All + StdOut, StdErr, AllOutputs } -fn com(print: &PrintType, command_string: &str) -> String { +fn com(print: &PrintType, command_text: &str) -> String { let command_output = Command::new("bash").arg("-c") - .arg(command_string) + .arg(command_text) .output().unwrap(); let to_string = |bytes| str::from_utf8(bytes).unwrap().to_string(); @@ -29,9 +27,9 @@ fn com(print: &PrintType, command_string: &str) -> String { let err = to_string(&command_output.stderr); match print { - PrintType::Out => { print!("{}", out); out }, - PrintType::Err => { print!("{}", err); err }, - PrintType::All => { + PrintType::StdOut => { print!("{}", out); out }, + PrintType::StdErr => { print!("{}", err); err }, + PrintType::AllOutputs => { print!("{}", out); if err.trim().is_empty() { print!("{}", err); @@ -68,7 +66,11 @@ fn output_filename(script_name: &str) -> String { } fn run_compiled(script_name: &str) { - com(&PrintType::All, &output_filename(script_name)); + com(&PrintType::AllOutputs, &output_filename(script_name)); +} + +fn contains(opt: Option, value: T) -> bool { + opt.is_some() && opt.unwrap() == value } fn parse_bash_command( @@ -76,11 +78,27 @@ fn parse_bash_command( code: &mut String, is_expr: bool ) -> Result<()> { - code.push_str(&format!("com({}, \"", !is_expr)); + let next = iter.peek().expect("Unexpected EOF"); + code.push_str(&(match next { + '^' => { + iter.next(); + format!("out_and_err_com({}, \"", !is_expr) + }, + '?' => { + iter.next(); + format!("stderr_com({}, \"", !is_expr) + }, + _ => format!("stdout_com({}, \"", !is_expr) + })); loop { if let Some(c) = iter.next() { if c == '%' { - break; + // Double percents count as a single literal percent + if contains(iter.peek(), &'%') { + iter.next(); + } else { + break; + } } code.push(c); } else { @@ -89,7 +107,7 @@ fn parse_bash_command( } code.push_str("\")"); let peek = iter.peek(); - if !is_expr && (peek.is_none() || peek.contains(&&' ') || peek.contains(&&'\n')) { + if !is_expr && (peek.is_none() || contains(peek, &&' ') || contains(peek, &&'\n')) { code.push(';'); } Ok(()) @@ -98,23 +116,45 @@ fn parse_bash_command( fn collect_code(script_name: &str) -> Result { let script = fs::read_to_string(&script_name)?; - let mut code = " + let mut code = +"#![allow(dead_code)] + use std::str; use std::process::Command; -fn com(print: bool, comcom: &str) -> String { -let s = str::from_utf8(&Command::new(\"bash\").arg(\"-c\") - .arg(comcom).output().unwrap().stdout).unwrap().to_string(); -if print { - print!(\"{}\", s) -}; -return s; +fn stdout_com(print: bool, command_text: &str) -> String { + let s = str::from_utf8(&Command::new(\"bash\").arg(\"-c\") + .arg(command_text).output().unwrap().stdout).unwrap().to_string(); + if print { + print!(\"{}\", s) + }; + return s; } -fn main() { +fn stderr_com(print: bool, command_text: &str) -> String { + let s = str::from_utf8(&Command::new(\"bash\").arg(\"-c\") + .arg(command_text).output().unwrap().stderr).unwrap().to_string(); + if print { + print!(\"{}\", s) + }; + return s; +} + +fn out_and_err_com(print: bool, command_text: &str) -> (String, String) { + let output = Command::new(\"bash\").arg(\"-c\") + .arg(command_text).output().unwrap(); + let stdout = str::from_utf8(&output.stdout).unwrap().to_string(); + let stderr = str::from_utf8(&output.stderr).unwrap().to_string(); + if print { + print!(\"{}\", stdout); + eprint!(\"{}\", stderr); + }; + return (stdout, stderr); +} + +fn main() -> std::io::Result<()> { ".to_string(); - let mut is_expr = false; let mut iter = script.chars().peekable(); if script.starts_with("#!/") { @@ -125,10 +165,11 @@ fn main() { } } + let mut is_expr = false; while let Some(c) = iter.next() { if c == '%' { // Double percents count as a single literal percent - if iter.peek().contains(&&'%') { + if contains(iter.peek(), &&'%') { code.push('%'); iter.next(); } else { @@ -136,13 +177,13 @@ fn main() { } } else { code.push(c); - if (c == 'i' && iter.peek().contains(&&'f')) - || (c == '=' && !iter.peek().contains(&&'=')) { + if (c == 'i' && contains(iter.peek(), &&'f')) + || (c == '=' && !contains(iter.peek(), &&'=')) { is_expr = true; - } else if c == 'f' && iter.peek().contains(&&'o') { + } else if c == 'f' && contains(iter.peek(), &&'o') { iter.next(); code.push('o'); - if iter.peek().contains(&&'r') { + if contains(iter.peek(), &&'r') { is_expr = true; } } else if (c == '{' || c == ';') && is_expr { @@ -152,7 +193,7 @@ fn main() { } } } - code.push('}'); + code.push_str(" Ok(())\n}"); return Ok(code); } @@ -182,7 +223,7 @@ fn main() -> Result<()> { code_filename, output_filename(&script_name) ); - com(&PrintType::Err, &compile_command); + com(&PrintType::StdErr, &compile_command); println!("[Compiled in {}ms]\n", now.elapsed().as_millis()); run_compiled(&script_name); }