Backticks implemented. Error messages still suck though.
This commit is contained in:
parent
8b149b66fc
commit
980c5d0b33
9
notes
9
notes
@ -1,14 +1,10 @@
|
|||||||
notes
|
notes
|
||||||
-----
|
-----
|
||||||
|
|
||||||
- actually run backticks
|
- unit tests for as many error types as possible
|
||||||
. test all error types, errors should underline backtick token
|
- integration tests for as many error types as possible, errors should underline backtick token
|
||||||
. test success in assignment
|
|
||||||
. test success in recipe interpolations
|
|
||||||
|
|
||||||
- test shebang recipe `` evaluation order
|
- test shebang recipe `` evaluation order
|
||||||
make sure that nothing happens if a `` fails on a later line
|
make sure that nothing happens if a `` fails on a later line
|
||||||
|
|
||||||
- test command recipe `` evaluation order
|
- test command recipe `` evaluation order
|
||||||
do some stuff, then have a line with a `` that fails
|
do some stuff, then have a line with a `` that fails
|
||||||
make sure that stuff before that actually happened
|
make sure that stuff before that actually happened
|
||||||
@ -57,6 +53,7 @@ notes
|
|||||||
no longer accept a program or change its meaning
|
no longer accept a program or change its meaning
|
||||||
. habit of using clever commands and writing little scripts
|
. habit of using clever commands and writing little scripts
|
||||||
. debugging with --debug or --evaluate
|
. debugging with --debug or --evaluate
|
||||||
|
. `` strips a single newline
|
||||||
. very low friction to write a script (no new file, chmod, add to rcs)
|
. very low friction to write a script (no new file, chmod, add to rcs)
|
||||||
. make list of contributors, include travis
|
. make list of contributors, include travis
|
||||||
. alias .j='just --justfile ~/.justfile --working-directory ~'
|
. alias .j='just --justfile ~/.justfile --working-directory ~'
|
||||||
|
@ -41,7 +41,7 @@ fn integration_test(
|
|||||||
|
|
||||||
let stderr = super::std::str::from_utf8(&output.stderr).unwrap();
|
let stderr = super::std::str::from_utf8(&output.stderr).unwrap();
|
||||||
if stderr != expected_stderr {
|
if stderr != expected_stderr {
|
||||||
println!("bad stdout:\ngot:\n{}\n\nexpected:\n{}", stderr, expected_stderr);
|
println!("bad stderr:\ngot:\n{}\n\nexpected:\n{}", stderr, expected_stderr);
|
||||||
failure = true;
|
failure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +141,28 @@ c:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn print() {
|
||||||
|
let text =
|
||||||
|
"b:
|
||||||
|
echo b
|
||||||
|
a:
|
||||||
|
echo a
|
||||||
|
d:
|
||||||
|
echo d
|
||||||
|
c:
|
||||||
|
echo c";
|
||||||
|
integration_test(
|
||||||
|
"select",
|
||||||
|
&["d", "c"],
|
||||||
|
text,
|
||||||
|
0,
|
||||||
|
"d\nc\n",
|
||||||
|
"echo d\necho c\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn show() {
|
fn show() {
|
||||||
let text =
|
let text =
|
||||||
@ -215,3 +237,27 @@ fn error() {
|
|||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backtick_success() {
|
||||||
|
integration_test(
|
||||||
|
"backtick_success",
|
||||||
|
&[],
|
||||||
|
"a = `printf Hello,`\nbar:\n printf '{{a + `printf ' world!'`}}'",
|
||||||
|
0,
|
||||||
|
"Hello, world!",
|
||||||
|
"printf 'Hello, world!'\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backtick_trimming() {
|
||||||
|
integration_test(
|
||||||
|
"backtick_trimming",
|
||||||
|
&[],
|
||||||
|
"a = `echo Hello,`\nbar:\n echo '{{a + `echo ' world!'`}}'",
|
||||||
|
0,
|
||||||
|
"Hello, world!\n",
|
||||||
|
"echo 'Hello, world!'\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
91
src/lib.rs
91
src/lib.rs
@ -134,7 +134,21 @@ fn error_from_signal(recipe: &str, exit_status: process::ExitStatus) -> RunError
|
|||||||
RunError::UnknownFailure{recipe: recipe}
|
RunError::UnknownFailure{recipe: recipe}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_backtick<'a>(raw: &'a str, _token: &Token) -> Result<String, RunError<'a>> {
|
#[cfg(unix)]
|
||||||
|
fn backtick_error_from_signal(exit_status: process::ExitStatus) -> RunError<'static> {
|
||||||
|
use std::os::unix::process::ExitStatusExt;
|
||||||
|
match exit_status.signal() {
|
||||||
|
Some(signal) => RunError::BacktickSignal{signal: signal},
|
||||||
|
None => RunError::BacktickUnknownFailure,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn backtick_error_from_signal(exit_status: process::ExitStatus) -> RunError<'static> {
|
||||||
|
RunError::BacktickUnknownFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_backtick<'a>(raw: &str, _token: &Token<'a>) -> Result<String, RunError<'a>> {
|
||||||
let output = process::Command::new("sh")
|
let output = process::Command::new("sh")
|
||||||
.arg("-cu")
|
.arg("-cu")
|
||||||
.arg(raw)
|
.arg(raw)
|
||||||
@ -142,33 +156,36 @@ fn run_backtick<'a>(raw: &'a str, _token: &Token) -> Result<String, RunError<'a>
|
|||||||
.output();
|
.output();
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
Ok(output) => if let Some(code) = output.status.code() {
|
Ok(output) => {
|
||||||
|
if let Some(code) = output.status.code() {
|
||||||
if code != 0 {
|
if code != 0 {
|
||||||
return Err(RunError::BacktickCode {
|
return Err(RunError::BacktickCode {
|
||||||
raw: raw,
|
|
||||||
code: code,
|
code: code,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
_ => {}
|
return Err(backtick_error_from_signal(output.status));
|
||||||
|
}
|
||||||
|
match std::str::from_utf8(&output.stdout) {
|
||||||
|
Err(error) => return Err(RunError::BacktickUtf8Error{utf8_error: error}),
|
||||||
|
Ok(utf8) => {
|
||||||
|
Ok(if utf8.ends_with('\n') {
|
||||||
|
&utf8[0..utf8.len()-1]
|
||||||
|
} else if utf8.ends_with("\r\n") {
|
||||||
|
&utf8[0..utf8.len()-2]
|
||||||
|
} else {
|
||||||
|
utf8
|
||||||
|
}.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => Err(RunError::BacktickIoError{io_error: error}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// if !output.status.success() {
|
|
||||||
// panic!("backtick evaluation failed");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// warn!("{}",
|
|
||||||
|
|
||||||
// status
|
|
||||||
// stdout
|
|
||||||
// stderr
|
|
||||||
|
|
||||||
Ok("".into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Recipe<'a> {
|
impl<'a> Recipe<'a> {
|
||||||
fn run(
|
fn run(
|
||||||
&'a self,
|
&self,
|
||||||
arguments: &[&'a str],
|
arguments: &[&'a str],
|
||||||
scope: &BTreeMap<&'a str, String>
|
scope: &BTreeMap<&'a str, String>
|
||||||
) -> Result<(), RunError<'a>> {
|
) -> Result<(), RunError<'a>> {
|
||||||
@ -475,7 +492,7 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_assignments<'a>(
|
fn evaluate_assignments<'a>(
|
||||||
assignments: &'a BTreeMap<&'a str, Expression<'a>>,
|
assignments: &BTreeMap<&'a str, Expression<'a>>,
|
||||||
) -> Result<BTreeMap<&'a str, String>, RunError<'a>> {
|
) -> Result<BTreeMap<&'a str, String>, RunError<'a>> {
|
||||||
let mut evaluator = Evaluator {
|
let mut evaluator = Evaluator {
|
||||||
evaluated: BTreeMap::new(),
|
evaluated: BTreeMap::new(),
|
||||||
@ -499,7 +516,7 @@ struct Evaluator<'a: 'b, 'b> {
|
|||||||
impl<'a, 'b> Evaluator<'a, 'b> {
|
impl<'a, 'b> Evaluator<'a, 'b> {
|
||||||
fn evaluate_line(
|
fn evaluate_line(
|
||||||
&mut self,
|
&mut self,
|
||||||
line: &'a [Fragment<'a>],
|
line: &[Fragment<'a>],
|
||||||
arguments: &BTreeMap<&str, &str>
|
arguments: &BTreeMap<&str, &str>
|
||||||
) -> Result<String, RunError<'a>> {
|
) -> Result<String, RunError<'a>> {
|
||||||
let mut evaluated = String::new();
|
let mut evaluated = String::new();
|
||||||
@ -533,7 +550,7 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
|||||||
|
|
||||||
fn evaluate_expression(
|
fn evaluate_expression(
|
||||||
&mut self,
|
&mut self,
|
||||||
expression: &'a Expression<'a>,
|
expression: &Expression<'a>,
|
||||||
arguments: &BTreeMap<&str, &str>
|
arguments: &BTreeMap<&str, &str>
|
||||||
) -> Result<String, RunError<'a>> {
|
) -> Result<String, RunError<'a>> {
|
||||||
Ok(match *expression {
|
Ok(match *expression {
|
||||||
@ -808,7 +825,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
|
|
||||||
fn run_recipe<'c>(
|
fn run_recipe<'c>(
|
||||||
&'c self,
|
&'c self,
|
||||||
recipe: &'c Recipe<'a>,
|
recipe: &Recipe<'a>,
|
||||||
arguments: &[&'a str],
|
arguments: &[&'a str],
|
||||||
scope: &BTreeMap<&'c str, String>,
|
scope: &BTreeMap<&'c str, String>,
|
||||||
ran: &mut HashSet<&'a str>
|
ran: &mut HashSet<&'a str>
|
||||||
@ -860,10 +877,11 @@ enum RunError<'a> {
|
|||||||
TmpdirIoError{recipe: &'a str, io_error: io::Error},
|
TmpdirIoError{recipe: &'a str, io_error: io::Error},
|
||||||
UnknownFailure{recipe: &'a str},
|
UnknownFailure{recipe: &'a str},
|
||||||
UnknownRecipes{recipes: Vec<&'a str>},
|
UnknownRecipes{recipes: Vec<&'a str>},
|
||||||
BacktickCode{raw: &'a str, code: i32},
|
BacktickCode{code: i32},
|
||||||
// BacktickSignal{backtick: Token<'a>, code: i32},
|
BacktickIoError{io_error: io::Error},
|
||||||
// BacktickIoError{backtick: Token<'a>, io_error: io::Error},
|
BacktickSignal{signal: i32},
|
||||||
// BacktickUTF8Error
|
BacktickUtf8Error{utf8_error: std::str::Utf8Error},
|
||||||
|
BacktickUnknownFailure,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Display for RunError<'a> {
|
impl<'a> Display for RunError<'a> {
|
||||||
@ -897,11 +915,30 @@ impl<'a> Display for RunError<'a> {
|
|||||||
try!(match io_error.kind() {
|
try!(match io_error.kind() {
|
||||||
io::ErrorKind::NotFound => write!(f, "Recipe \"{}\" could not be run because j could not find `sh` the command:\n{}", recipe, io_error),
|
io::ErrorKind::NotFound => write!(f, "Recipe \"{}\" could not be run because j could not find `sh` the command:\n{}", recipe, io_error),
|
||||||
io::ErrorKind::PermissionDenied => write!(f, "Recipe \"{}\" could not be run because j could not run `sh`:\n{}", recipe, io_error),
|
io::ErrorKind::PermissionDenied => write!(f, "Recipe \"{}\" could not be run because j could not run `sh`:\n{}", recipe, io_error),
|
||||||
_ => write!(f, "Recipe \"{}\" could not be run because of an IO error while launching the `sh`:\n{}", recipe, io_error),
|
_ => write!(f, "Recipe \"{}\" could not be run because of an IO error while launching `sh`:\n{}", recipe, io_error),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
RunError::TmpdirIoError{recipe, ref io_error} =>
|
RunError::TmpdirIoError{recipe, ref io_error} =>
|
||||||
try!(write!(f, "Recipe \"{}\" could not be run because of an IO error while trying to create a temporary directory or write a file to that directory`:\n{}", recipe, io_error)),
|
try!(write!(f, "Recipe \"{}\" could not be run because of an IO error while trying to create a temporary directory or write a file to that directory`:\n{}", recipe, io_error)),
|
||||||
|
RunError::BacktickCode{code} => {
|
||||||
|
try!(writeln!(f, "backtick failed with exit code {}", code));
|
||||||
|
}
|
||||||
|
RunError::BacktickSignal{signal} => {
|
||||||
|
try!(writeln!(f, "backtick was terminated by signal {}", signal));
|
||||||
|
}
|
||||||
|
RunError::BacktickUnknownFailure => {
|
||||||
|
try!(writeln!(f, "backtick failed for an uknown reason"));
|
||||||
|
}
|
||||||
|
RunError::BacktickIoError{ref io_error} => {
|
||||||
|
try!(match io_error.kind() {
|
||||||
|
io::ErrorKind::NotFound => write!(f, "backtick could not be run because j could not find `sh` the command:\n{}", io_error),
|
||||||
|
io::ErrorKind::PermissionDenied => write!(f, "backtick could not be run because j could not run `sh`:\n{}", io_error),
|
||||||
|
_ => write!(f, "backtick could not be run because of an IO error while launching `sh`:\n{}", io_error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RunError::BacktickUtf8Error{ref utf8_error} => {
|
||||||
|
try!(write!(f, "backtick succeeded but stdout was not utf8: {}", utf8_error));
|
||||||
|
}
|
||||||
RunError::InternalError{ref message} => {
|
RunError::InternalError{ref message} => {
|
||||||
try!(writeln!(f, "internal error, this may indicate a bug in j: {}\n consider filing an issue: https://github.com/casey/j/issues/new", message));
|
try!(writeln!(f, "internal error, this may indicate a bug in j: {}\n consider filing an issue: https://github.com/casey/j/issues/new", message));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user