Actually run recipes, add a bunch of tests
This commit is contained in:
parent
823b7a5181
commit
c957165f4c
19
Cargo.lock
generated
19
Cargo.lock
generated
@ -4,6 +4,7 @@ version = "0.1.5"
|
||||
dependencies = [
|
||||
"clap 2.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -61,6 +62,14 @@ dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.1.77"
|
||||
@ -83,6 +92,14 @@ name = "strsim"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "tempdir"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term_size"
|
||||
version = "0.2.1"
|
||||
@ -148,9 +165,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
|
||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||
"checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
|
||||
"checksum regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)" = "64b03446c466d35b42f2a8b203c8e03ed8b91c0f17b56e1f84f7210a257aa665"
|
||||
"checksum regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279401017ae31cf4e15344aa3f085d0e2e5c1e70067289ef906906fdbe92c8fd"
|
||||
"checksum strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50c069df92e4b01425a8bf3576d5d417943a6a7272fbabaf5bd80b1aaa76442e"
|
||||
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
|
||||
"checksum term_size 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f7f5f3f71b0040cecc71af239414c23fd3c73570f5ff54cf50e03cef637f2a0"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
|
@ -10,3 +10,4 @@ homepage = "https://github.com/casey/j"
|
||||
|
||||
regex = "^0.1.77"
|
||||
clap = "^2.0.0"
|
||||
tempdir = "^0.3.5"
|
||||
|
2
justfile
2
justfile
@ -1,6 +1,6 @@
|
||||
test:
|
||||
cargo test --lib
|
||||
cargo run -- quine
|
||||
cargo run -- quine clean > /dev/null 2> /dev/null
|
||||
|
||||
# list all recipies
|
||||
list:
|
||||
|
5
notes
5
notes
@ -2,12 +2,11 @@ notes
|
||||
-----
|
||||
|
||||
-- report double compile error
|
||||
-- actually run recipes
|
||||
-- actually parse recipe and validate contents
|
||||
-- think about maybe using multiple cores
|
||||
-- should pre-requisite order really be arbitrary?
|
||||
-- test plan order
|
||||
|
||||
-- use multiple cores
|
||||
|
||||
- look through all justfiles for features of make that I use. so far:
|
||||
. phony
|
||||
. SHELL := zsh
|
||||
|
58
src/lib.rs
58
src/lib.rs
@ -5,7 +5,7 @@ extern crate regex;
|
||||
|
||||
use std::io::prelude::*;
|
||||
|
||||
use std::{fs, fmt};
|
||||
use std::{fs, fmt, process, io};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||
use std::fmt::Display;
|
||||
use regex::Regex;
|
||||
@ -49,14 +49,46 @@ struct Recipe<'a> {
|
||||
dependencies: BTreeSet<&'a str>,
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn error_from_signal<'a>(recipe: &'a str, exit_status: process::ExitStatus) -> RunError<'a> {
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
match exit_status.signal() {
|
||||
Some(signal) => RunError::Signal{recipe: recipe, signal: signal},
|
||||
None => RunError::UnknownFailure{recipe: recipe},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn error_from_signal<'a>(recipe: &'a str, exit_status: process::ExitStatus) -> RunError<'a> {
|
||||
RunError::UnknownFailure{recipe: recipe}
|
||||
}
|
||||
|
||||
impl<'a> Recipe<'a> {
|
||||
fn run(&self) -> Result<(), RunError<'a>> {
|
||||
// TODO: actually run recipes
|
||||
warn!("running {}", self.name);
|
||||
for command in &self.commands {
|
||||
let mut command = *command;
|
||||
if !command.starts_with("@") {
|
||||
warn!("{}", command);
|
||||
} else {
|
||||
command = &command[1..];
|
||||
}
|
||||
let status = process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(command)
|
||||
.status();
|
||||
try!(match status {
|
||||
Ok(exit_status) => if let Some(code) = exit_status.code() {
|
||||
if code == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RunError::Code{recipe: self.name, code: code})
|
||||
}
|
||||
} else {
|
||||
Err(error_from_signal(self.name, exit_status))
|
||||
},
|
||||
Err(io_error) => Err(RunError::IoError{recipe: self.name, io_error: io_error})
|
||||
});
|
||||
}
|
||||
// Err(RunError::Code{recipe: self.name, code: -1})
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -270,10 +302,13 @@ impl<'a> Justfile<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RunError<'a> {
|
||||
UnknownRecipes{recipes: Vec<&'a str>},
|
||||
// Signal{recipe: &'a str, signal: i32},
|
||||
Signal{recipe: &'a str, signal: i32},
|
||||
Code{recipe: &'a str, code: i32},
|
||||
UnknownFailure{recipe: &'a str},
|
||||
IoError{recipe: &'a str, io_error: io::Error},
|
||||
}
|
||||
|
||||
impl<'a> Display for RunError<'a> {
|
||||
@ -289,6 +324,19 @@ impl<'a> Display for RunError<'a> {
|
||||
&RunError::Code{recipe, code} => {
|
||||
try!(write!(f, "Recipe \"{}\" failed with code {}", recipe, code));
|
||||
},
|
||||
&RunError::Signal{recipe, signal} => {
|
||||
try!(write!(f, "Recipe \"{}\" wast terminated by signal {}", recipe, signal));
|
||||
}
|
||||
&RunError::UnknownFailure{recipe} => {
|
||||
try!(write!(f, "Recipe \"{}\" failed for an unknown reason", recipe));
|
||||
},
|
||||
&RunError::IoError{recipe, ref io_error} => {
|
||||
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::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),
|
||||
});
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
42
src/tests.rs
42
src/tests.rs
@ -1,3 +1,5 @@
|
||||
extern crate tempdir;
|
||||
|
||||
use super::{ErrorKind, Justfile};
|
||||
|
||||
fn expect_error(text: &str, line: usize, expected_error_kind: ErrorKind) {
|
||||
@ -130,3 +132,43 @@ fn first() {
|
||||
let justfile = expect_success("#hello\n#goodbye\na:\nb:\nc:\n");
|
||||
assert!(justfile.first().unwrap() == "a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_recipes() {
|
||||
match expect_success("a:\nb:\nc:").run(&["a", "x", "y", "z"]).unwrap_err() {
|
||||
super::RunError::UnknownRecipes{recipes} => assert_eq!(recipes, &["x", "y", "z"]),
|
||||
other @ _ => panic!("expected an unknown recipe error, but got: {}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_error() {
|
||||
match expect_success("fail:\n @function x { return 100; }; x").run(&["fail"]).unwrap_err() {
|
||||
super::RunError::Code{recipe, code} => {
|
||||
assert_eq!(recipe, "fail");
|
||||
assert_eq!(code, 100);
|
||||
},
|
||||
other @ _ => panic!("expected an code run error, but got: {}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_order() {
|
||||
let tmp = tempdir::TempDir::new("run_order").unwrap_or_else(|err| panic!("tmpdir: failed to create temporary directory: {}", err));
|
||||
let path = tmp.path().to_str().unwrap_or_else(|| panic!("tmpdir: path was not valid UTF-8")).to_owned();
|
||||
let text = r"
|
||||
a:
|
||||
@touch a
|
||||
|
||||
b: a
|
||||
@mv a b
|
||||
|
||||
c: b
|
||||
@mv b c
|
||||
|
||||
d: c
|
||||
@rm c
|
||||
";
|
||||
super::std::env::set_current_dir(path).expect("failed to set current directory");
|
||||
expect_success(text).run(&["a", "d"]).unwrap();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user