#![feature(proc_macro_hygiene, decl_macro)]
extern crate rocket;
extern crate serde_json;
use serde_json::{to_string, from_str};
use rocket_contrib::json::{Json, JsonValue};
pub fn stdio_to_json<F: Fn(Vec<u8>) -> JsonValue>(
cmd_path : &str,
args : &[&str],
validated_json : Json<JsonValue>,
f : F
) -> JsonValue {
use std::io::Write;
use rocket_contrib::json;
let json_str = to_string(&validated_json.into_inner()).unwrap();
let command = std::process::Command::new(cmd_path)
.args(args)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn();
let mut command = match command {
Err(_) => { return json! {{ "error" : "Failed to run command!" }}},
Ok(command) => command
};
if write!(command.stdin.as_mut().unwrap(), "{}", json_str).is_err() {
return json! {{ "error" : "Could not write to pipe" }}
}
match command.wait_with_output() {
Err(_) => {
json! {{ "error" : "Couldn't wait for command" }}
},
Ok(output) => if output.status.success() {
f(output.stdout)
} else {
let error_text = match String::from_utf8(output.stderr) {
Ok(error_text) => error_text,
Err(_) => "Error text is not valid UTF-8, cannot display!".to_owned()
};
match output.status.code() {
Some(code) => json! {{
"error_code" : code,
"error" : error_text
}},
None => json! {{
"error" : error_text
}}
}
}
}
}
pub fn json_or_error(data : Vec<u8>) -> JsonValue {
use rocket_contrib::json;
match String::from_utf8(data) {
Ok(text) => {
match from_str(&text) {
Ok(json) => json,
Err(_) => json! {{ "error" : "Application output invalid JSON!" }}
}
},
Err(_) => json! {{ "error" : "Application did not output valid UTF-8" }}
}
}
pub fn json_named(name : &'static str) -> impl Fn(Vec<u8>) -> JsonValue {
use rocket_contrib::json;
move |data : Vec<u8>| {
match String::from_utf8(data) {
Ok(text) => json ! {{ name : text }},
Err(_) => json! {{ "error" : "Application did not output valid UTF-8" }}
}
}
}
#[macro_export]
macro_rules! stdio_handler {
(let $name:ident = $path:literal -> wrap $cmd_path:expr, $args:expr ; ) => {
#[post($path, data="<validated_json>")]
pub fn $name(
validated_json : rocket_contrib::json::Json<rocket_contrib::json::JsonValue>)
-> rocket_contrib::json::JsonValue {
petri::stdio_to_json(
$cmd_path,
&($args),
validated_json,
petri::json_or_error
)
}
};
(
let $name:ident = $path:literal ->
read $attrib:literal from $cmd_path:expr, $args:expr ;
) => {
#[post($path, data="<validated_json>")]
pub fn $name(
validated_json : rocket_contrib::json::Json<rocket_contrib::json::JsonValue>)
-> rocket_contrib::json::JsonValue {
petri::stdio_to_json(
$cmd_path,
&($args),
validated_json,
petri::json_named($attrib)
)
}
};
() => {}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}