summaryrefslogtreecommitdiff
path: root/src/script.rs
blob: 369fbf80adf1c70f019ca3766237407e061c6ada (plain)
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
use std::{env, collections::HashMap, fs::read_to_string, process::Command, result::Result, error::Error};

use crate::http::{response::Response, method::Method};

pub struct Script {
    path: String
}

impl Script {
    fn new(path: String) -> Self {
        Self { path }
    }

    pub fn run(&self, body: Option<&String>) -> Result<Response, std::io::Error> {
        let mut res = Response::new();

        let mut command = Command::new(&self.path);
        if let Some(body) = body {
            command.args([body]);
        }

        let output = match command.output() {
            Ok(output) => output,
            Err(err) => return Err(err)
        };

        let status = output.status.code().unwrap_or(500) as u16;

        let response = String::from_utf8_lossy(&output.stdout).into_owned();
        let mut lines = response.split('\n').into_iter();

        res.headers.serialize(&mut lines);

        let body: String = lines.collect::<Vec<&str>>().join("\n");
        if body.len() > 0 {
            res.body = Some(body);
        }

        res.status = status;

        Ok(res)
    }
}

pub struct Config {
    map: HashMap<String, Script>
}

impl Config {

    fn key(method: &Method, route: &str) -> String {
        format!("{}:{}", method.deserialize(), route)
    }

    pub fn load() -> Result<Self, Box<dyn Error>> {

        let config_path = env::var("CONFIG_PATH").unwrap_or_else(|_| String::from("config"));
        let config = read_to_string(&config_path)?;

        let mut map = HashMap::new();
        let lines = config.split("\n").into_iter();

        for (i, line) in lines.enumerate() {
            let mut parts = line.split_whitespace();

            let Some(method_str) = parts.next() else { continue };
            let method = Method::serialize(&method_str)
                .ok_or(format!("config line {i} has invalid http method: {method_str}"))?;

            let route = parts.next()
                .ok_or(format!("config line {i} missing http route"))?;

            let script = parts.next()
                .ok_or(format!{"config line {i} missing script path"})?;

            let key = Self::key(&method, route);
            let value = Script::new(script.to_owned());

            println!("adding script {script} for {} {route}", method.deserialize());
            map.insert(key, value);
        }

        if map.len() < 1 {
            return Err("cannot run server, config is empty".into())
        }

        Ok(Self { map })
    }

    pub fn get(&self, method: &Method, route: &str) -> Option<&Script> {
        self.map.get(&Self::key(method, route))
    }

}