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 { 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::>().join("\n"); if body.len() > 0 { res.body = Some(body); } res.status = status; Ok(res) } } pub struct Config { map: HashMap } impl Config { fn key(method: &Method, route: &str) -> String { format!("{}:{}", method.deserialize(), route) } pub fn load() -> Result> { 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)) } }