diff --git a/Cargo.toml b/Cargo.toml index 395155d3..4a3a3966 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,5 @@ derive-new = "0.5.5" serde = "1.0.80" serde_json = "1.0.32" serde_derive = "1.0.80" +url = "1.7.1" hyper = "0.12.13" diff --git a/src/api_info.rs b/src/api_info.rs index 4fa8f924..40fe3e71 100644 --- a/src/api_info.rs +++ b/src/api_info.rs @@ -27,4 +27,18 @@ pub static METHOD_INFO_DEFAULTS: MethodInfo = MethodInfo { delete: None, subdirs: None, }; - + +pub fn find_method_info<'a>(root: &'a MethodInfo, components: &[&str]) -> Option<&'a MethodInfo> { + + if components.len() == 0 { return Some(root); }; + + let (dir, rest) = (components[0], &components[1..]); + + if let Some(dirmap) = root.subdirs { + if let Some(info) = dirmap.get(dir) { + return find_method_info(info, rest); + } + } + + None +} diff --git a/src/main.rs b/src/main.rs index feedcc19..95d71254 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ extern crate phf; extern crate failure; use failure::*; +use std::collections::HashMap; + #[macro_use] extern crate apitest; @@ -18,9 +20,13 @@ extern crate serde_derive; use serde_json::{json, Value}; +extern crate url; + +use url::form_urlencoded; + extern crate hyper; -use hyper::{Body, Request, Response, Server}; +use hyper::{Method, Body, Request, Response, Server, StatusCode}; use hyper::rt::Future; use hyper::service::service_fn_ok; @@ -91,6 +97,7 @@ static TEST_API_METHOD: ApiMethod = ApiMethod { description: "This is a simple test.", properties: phf_map! { "force" => Boolean!{ + optional => Some(true), description => "Test for boolean options." } }, @@ -104,21 +111,83 @@ static API3_NODES: MethodInfo = MethodInfo { ..METHOD_INFO_DEFAULTS }; -static API3: MethodInfo = MethodInfo { +static API_ROOT: MethodInfo = MethodInfo { get: Some(&TEST_API_METHOD), subdirs: Some(&phf_map!{"nodes" => &API3_NODES}), ..METHOD_INFO_DEFAULTS }; +macro_rules! http_error { + ($status:ident, $msg:expr) => {{ + let mut resp = Response::new(Body::from($msg)); + *resp.status_mut() = StatusCode::$status; + return resp; + }} +} -fn hello_world(req: Request) -> Response { +fn parse_query(query: &str) -> Value { + + println!("PARSE QUERY {}", query); + + // fixme: what about repeated parameters (arrays?) + let mut raw_param = HashMap::new(); + for (k, v) in form_urlencoded::parse(query.as_bytes()) { + println!("QUERY PARAM {} value {}", k, v); + raw_param.insert(k, v); + } + println!("QUERY HASH {:?}", raw_param); + + return json!(null); +} + +fn handle_request(req: Request) -> Response { let method = req.method(); let path = req.uri().path(); + let query = req.uri().query(); + let components: Vec<&str> = path.split('/').filter(|x| !x.is_empty()).collect(); + let comp_len = components.len(); println!("REQUEST {} {}", method, path); + println!("COMPO {:?}", components); - Response::new(Body::from("hello World!\n")) + if comp_len >= 1 && components[0] == "api3" { + println!("GOT API REQUEST"); + if comp_len >= 2 { + let format = components[1]; + if format != "json" { + http_error!(NOT_FOUND, format!("Unsupported format '{}'\n", format)) + } + + if let Some(info) = find_method_info(&API_ROOT, &components[2..]) { + println!("FOUND INFO"); + let api_method_opt = match method { + &Method::GET => info.get, + &Method::PUT => info.put, + &Method::POST => info.post, + &Method::DELETE => info.delete, + _ => None, + }; + let api_method = match api_method_opt { + Some(m) => m, + _ => http_error!(NOT_FOUND, format!("No such method '{} {}'\n", method, path)), + }; + + // handle auth + + // extract param + let param = match query { + Some(data) => parse_query(data), + None => json!({}), + }; + + } else { + http_error!(NOT_FOUND, format!("No such path '{}'\n", path)); + } + } + } + + Response::new(Body::from("RETURN WEB GUI\n")) } fn main() { @@ -132,7 +201,7 @@ fn main() { let new_svc = || { // service_fn_ok converts our function into a `Service` - service_fn_ok(hello_world) + service_fn_ok(handle_request) }; let server = Server::bind(&addr)