Add support for stderr and split output handling.

More percent sign doubling support.
This commit is contained in:
Sage Vaillancourt 2023-05-08 16:59:45 -04:00
parent 36ba81e4c4
commit 3e0030b85f
2 changed files with 75 additions and 30 deletions

View File

@ -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%

View File

@ -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<T: std::cmp::PartialEq>(opt: Option<T>, value: T) -> bool {
opt.is_some() && opt.unwrap() == value
}
fn parse_bash_command(
@ -76,12 +78,28 @@ 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 == '%' {
// Double percents count as a single literal percent
if contains(iter.peek(), &'%') {
iter.next();
} else {
break;
}
}
code.push(c);
} else {
bail!("Unexpected end-of-file while parsing bash command");
@ -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<String> {
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 {
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;
};
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);
}