1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
#![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 { petrichor::stdio_to_json( $cmd_path, &($args), validated_json, petrichor::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 { petrichor::stdio_to_json( $cmd_path, &($args), validated_json, petrichor::json_named($attrib) ) } }; () => {} } #[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } }