Add support for stderr and split output handling.
More percent sign doubling support.
This commit is contained in:
parent
36ba81e4c4
commit
3e0030b85f
|
@ -13,9 +13,13 @@ if %echo some words%.contains("me wo") {
|
||||||
let mem = %free -h%.lines().nth(1).unwrap()
|
let mem = %free -h%.lines().nth(1).unwrap()
|
||||||
.split_whitespace().nth(3).unwrap().to_owned();
|
.split_whitespace().nth(3).unwrap().to_owned();
|
||||||
|
|
||||||
|
let (_stdout, _stderr) = %^curl bob%;
|
||||||
|
println!("Collect stdout and stderr separately!");
|
||||||
|
|
||||||
// for i in 0..10000 {
|
// for i in 0..10000 {
|
||||||
// println!("x contains %% example? [{}]", x.contains("example"));
|
// 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);
|
println!("There are {} of memory free", &mem);
|
||||||
%echo hello%
|
|
||||||
|
|
95
src/main.rs
95
src/main.rs
|
@ -1,5 +1,3 @@
|
||||||
#![feature(option_result_contains)]
|
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -16,12 +14,12 @@ use std::process::Command;
|
||||||
|
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(PartialEq, Clone)]
|
||||||
enum PrintType {
|
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")
|
let command_output = Command::new("bash").arg("-c")
|
||||||
.arg(command_string)
|
.arg(command_text)
|
||||||
.output().unwrap();
|
.output().unwrap();
|
||||||
|
|
||||||
let to_string = |bytes| str::from_utf8(bytes).unwrap().to_string();
|
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);
|
let err = to_string(&command_output.stderr);
|
||||||
|
|
||||||
match print {
|
match print {
|
||||||
PrintType::Out => { print!("{}", out); out },
|
PrintType::StdOut => { print!("{}", out); out },
|
||||||
PrintType::Err => { print!("{}", err); err },
|
PrintType::StdErr => { print!("{}", err); err },
|
||||||
PrintType::All => {
|
PrintType::AllOutputs => {
|
||||||
print!("{}", out);
|
print!("{}", out);
|
||||||
if err.trim().is_empty() {
|
if err.trim().is_empty() {
|
||||||
print!("{}", err);
|
print!("{}", err);
|
||||||
|
@ -68,7 +66,11 @@ fn output_filename(script_name: &str) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_compiled(script_name: &str) {
|
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(
|
fn parse_bash_command(
|
||||||
|
@ -76,12 +78,28 @@ fn parse_bash_command(
|
||||||
code: &mut String,
|
code: &mut String,
|
||||||
is_expr: bool
|
is_expr: bool
|
||||||
) -> Result<()> {
|
) -> 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 {
|
loop {
|
||||||
if let Some(c) = iter.next() {
|
if let Some(c) = iter.next() {
|
||||||
if c == '%' {
|
if c == '%' {
|
||||||
|
// Double percents count as a single literal percent
|
||||||
|
if contains(iter.peek(), &'%') {
|
||||||
|
iter.next();
|
||||||
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
code.push(c);
|
code.push(c);
|
||||||
} else {
|
} else {
|
||||||
bail!("Unexpected end-of-file while parsing bash command");
|
bail!("Unexpected end-of-file while parsing bash command");
|
||||||
|
@ -89,7 +107,7 @@ fn parse_bash_command(
|
||||||
}
|
}
|
||||||
code.push_str("\")");
|
code.push_str("\")");
|
||||||
let peek = iter.peek();
|
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(';');
|
code.push(';');
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -98,23 +116,45 @@ fn parse_bash_command(
|
||||||
fn collect_code(script_name: &str) -> Result<String> {
|
fn collect_code(script_name: &str) -> Result<String> {
|
||||||
let script = fs::read_to_string(&script_name)?;
|
let script = fs::read_to_string(&script_name)?;
|
||||||
|
|
||||||
let mut code = "
|
let mut code =
|
||||||
|
"#![allow(dead_code)]
|
||||||
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
fn com(print: bool, comcom: &str) -> String {
|
fn stdout_com(print: bool, command_text: &str) -> String {
|
||||||
let s = str::from_utf8(&Command::new(\"bash\").arg(\"-c\")
|
let s = str::from_utf8(&Command::new(\"bash\").arg(\"-c\")
|
||||||
.arg(comcom).output().unwrap().stdout).unwrap().to_string();
|
.arg(command_text).output().unwrap().stdout).unwrap().to_string();
|
||||||
if print {
|
if print {
|
||||||
print!(\"{}\", s)
|
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();
|
".to_string();
|
||||||
|
|
||||||
let mut is_expr = false;
|
|
||||||
|
|
||||||
let mut iter = script.chars().peekable();
|
let mut iter = script.chars().peekable();
|
||||||
if script.starts_with("#!/") {
|
if script.starts_with("#!/") {
|
||||||
|
@ -125,10 +165,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut is_expr = false;
|
||||||
while let Some(c) = iter.next() {
|
while let Some(c) = iter.next() {
|
||||||
if c == '%' {
|
if c == '%' {
|
||||||
// Double percents count as a single literal percent
|
// Double percents count as a single literal percent
|
||||||
if iter.peek().contains(&&'%') {
|
if contains(iter.peek(), &&'%') {
|
||||||
code.push('%');
|
code.push('%');
|
||||||
iter.next();
|
iter.next();
|
||||||
} else {
|
} else {
|
||||||
|
@ -136,13 +177,13 @@ fn main() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
code.push(c);
|
code.push(c);
|
||||||
if (c == 'i' && iter.peek().contains(&&'f'))
|
if (c == 'i' && contains(iter.peek(), &&'f'))
|
||||||
|| (c == '=' && !iter.peek().contains(&&'=')) {
|
|| (c == '=' && !contains(iter.peek(), &&'=')) {
|
||||||
is_expr = true;
|
is_expr = true;
|
||||||
} else if c == 'f' && iter.peek().contains(&&'o') {
|
} else if c == 'f' && contains(iter.peek(), &&'o') {
|
||||||
iter.next();
|
iter.next();
|
||||||
code.push('o');
|
code.push('o');
|
||||||
if iter.peek().contains(&&'r') {
|
if contains(iter.peek(), &&'r') {
|
||||||
is_expr = true;
|
is_expr = true;
|
||||||
}
|
}
|
||||||
} else if (c == '{' || c == ';') && is_expr {
|
} else if (c == '{' || c == ';') && is_expr {
|
||||||
|
@ -152,7 +193,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
code.push('}');
|
code.push_str(" Ok(())\n}");
|
||||||
return Ok(code);
|
return Ok(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +223,7 @@ fn main() -> Result<()> {
|
||||||
code_filename,
|
code_filename,
|
||||||
output_filename(&script_name)
|
output_filename(&script_name)
|
||||||
);
|
);
|
||||||
com(&PrintType::Err, &compile_command);
|
com(&PrintType::StdErr, &compile_command);
|
||||||
println!("[32m[Compiled in {}ms][0m\n", now.elapsed().as_millis());
|
println!("[32m[Compiled in {}ms][0m\n", now.elapsed().as_millis());
|
||||||
run_compiled(&script_name);
|
run_compiled(&script_name);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue