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);
    }
}