cleanup futures

This commit is contained in:
Dietmar Maurer 2018-11-10 10:00:48 +01:00
parent c819ec8dde
commit 260c1ee8c6
2 changed files with 89 additions and 96 deletions

View File

@ -3,9 +3,40 @@ use std::collections::HashMap;
use serde_json::{json, Value}; use serde_json::{json, Value};
use url::form_urlencoded; use url::form_urlencoded;
use regex::Regex; use regex::Regex;
use std::fmt;
pub type PropertyMap = HashMap<&'static str, Jss>; pub type PropertyMap = HashMap<&'static str, Jss>;
#[derive(Debug, Fail)]
pub struct ParameterError {
error_list: Vec<Error>,
}
impl ParameterError {
fn new() -> Self {
Self { error_list: vec![] }
}
fn push(&mut self, value: Error) {
self.error_list.push(value);
}
fn len(&self) -> usize {
self.error_list.len()
}
}
impl fmt::Display for ParameterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let msg = self.error_list.iter().fold(String::from(""), |acc, item| {
acc + &item.to_string() + "\n"
});
write!(f, "{}", msg)
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct JssBoolean { pub struct JssBoolean {
pub description: &'static str, pub description: &'static str,
@ -200,13 +231,13 @@ fn parse_simple_value(value_str: &str, schema: &Jss) -> Result<Value, Error> {
Ok(value) Ok(value)
} }
pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &Jss, test_required: bool) -> Result<Value, Vec<Error>> { pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &Jss, test_required: bool) -> Result<Value, ParameterError> {
println!("QUERY Strings {:?}", data); println!("QUERY Strings {:?}", data);
let mut params = json!({}); let mut params = json!({});
let mut errors: Vec<Error> = Vec::new(); let mut errors = ParameterError::new();
match schema { match schema {
Jss::Object(JssObject { properties, additional_properties, .. }) => { Jss::Object(JssObject { properties, additional_properties, .. }) => {
@ -282,14 +313,14 @@ pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &Jss, test_
} }
if (errors.len() > 0) { if errors.len() > 0 {
Err(errors) Err(errors)
} else { } else {
Ok(params) Ok(params)
} }
} }
pub fn parse_query_string(query: &str, schema: &Jss, test_required: bool) -> Result<Value, Vec<Error>> { pub fn parse_query_string(query: &str, schema: &Jss, test_required: bool) -> Result<Value, ParameterError> {
let param_list: Vec<(String, String)> = let param_list: Vec<(String, String)> =
form_urlencoded::parse(query.as_bytes()).into_owned().collect(); form_urlencoded::parse(query.as_bytes()).into_owned().collect();

View File

@ -10,7 +10,7 @@ use apitest::api_info::*;
use apitest::json_schema::*; use apitest::json_schema::*;
//use serde_derive::{Serialize, Deserialize}; //use serde_derive::{Serialize, Deserialize};
use serde_json::{json, Value}; use serde_json::{json};
//use hyper::body::Payload; //use hyper::body::Payload;
use hyper::http::request::Parts; use hyper::http::request::Parts;
@ -20,19 +20,19 @@ use hyper::service::service_fn;
use futures::future; use futures::future;
type BoxFut = Box<Future<Item = Response<Body>, Error = hyper::Error> + Send>; type BoxFut = Box<Future<Item = Response<Body>, Error = failure::Error> + Send>;
macro_rules! http_error { macro_rules! error_response {
($status:ident, $msg:expr) => {{ ($status:ident, $msg:expr) => {{
let mut resp = Response::new(Body::from($msg)); let mut resp = Response::new(Body::from($msg));
*resp.status_mut() = StatusCode::$status; *resp.status_mut() = StatusCode::$status;
return resp resp
}} }}
} }
macro_rules! http_error_future { macro_rules! http_error_future {
($status:ident, $msg:expr) => {{ ($status:ident, $msg:expr) => {{
let mut resp = Response::new(Body::from($msg)); let resp = error_response!($status, $msg);
*resp.status_mut() = StatusCode::$status;
return Box::new(futures::future::ok(resp)); return Box::new(futures::future::ok(resp));
}} }}
} }
@ -41,16 +41,13 @@ fn handle_api_request<'a>(
info: &'a ApiMethod, info: &'a ApiMethod,
parts: Parts, parts: Parts,
req_body: Body, req_body: Body,
) -> Box<Future<Item = Response<Body>, Error = hyper::Error> + Send + 'a> ) -> Box<Future<Item = Response<Body>, Error = failure::Error> + Send + 'a>
{ {
let resp = req_body.concat2()
.map_err(|err| format_err!("Promlems reading request body: {}", err))
.and_then(move |body| {
let entire_body = req_body.concat2(); let bytes = String::from_utf8(body.to_vec())?; // why copy??
let resp = entire_body.map(move |body| {
let bytes = match String::from_utf8(body.to_vec()) { // why copy??
Ok(v) => v,
Err(err) => http_error!(NOT_FOUND, err.to_string()),
};
println!("GOT BODY {:?}", bytes); println!("GOT BODY {:?}", bytes);
@ -58,25 +55,13 @@ fn handle_api_request<'a>(
let mut params = json!({}); let mut params = json!({});
let format_error_list = |error_list: Vec<Error>| {
error_list.iter().fold(String::from(""), |acc, item| {
acc + &item.to_string() + "\n"
})
};
if bytes.len() > 0 { if bytes.len() > 0 {
params = match parse_query_string(&bytes, &info.parameters, true) { params = parse_query_string(&bytes, &info.parameters, true)?;
Ok(value) => value,
Err(error_list) => http_error!(NOT_FOUND, format_error_list(error_list)),
};
test_required = false; test_required = false;
} }
if let Some(query_str) = parts.uri.query() { if let Some(query_str) = parts.uri.query() {
let query_params = match parse_query_string(query_str, &info.parameters, test_required) { let query_params = parse_query_string(query_str, &info.parameters, test_required)?;
Ok(value) => value,
Err(error_list) => http_error!(NOT_FOUND, format_error_list(error_list)),
};
for (k, v) in query_params.as_object().unwrap() { for (k, v) in query_params.as_object().unwrap() {
params[k] = v.clone(); // fixme: why clone()?? params[k] = v.clone(); // fixme: why clone()??
@ -85,18 +70,23 @@ fn handle_api_request<'a>(
println!("GOT PARAMS {}", params); println!("GOT PARAMS {}", params);
let res: Value = match (info.handler)(params, info) { let res = (info.handler)(params, info)?;
Ok(res) => res,
Err(err) => http_error!(NOT_FOUND, format!("Method returned error '{}'\n", err)),
};
let json_str = res.to_string(); Ok(res)
Response::builder() }).then(|result| {
match result {
Ok(ref value) => {
let json_str = value.to_string();
Ok(Response::builder()
.status(200) .status(200)
.header("ContentType", "application/json") .header("ContentType", "application/json")
.body(Body::from(json_str)) .body(Body::from(json_str))
.unwrap() // fixme: really? .unwrap()) // fixme: really?
}
Err(err) => Ok(error_response!(NOT_FOUND, err.to_string()))
}
}); });
Box::new(resp) Box::new(resp)
@ -139,43 +129,8 @@ fn handle_request(req: Request<Body>) -> BoxFut {
// fixme: handle auth // fixme: handle auth
return handle_api_request(api_method, parts, body);
let res = handle_api_request(api_method, parts, body);
return res;
/*
// extract param
let param = match query {
Some(data) => {
match parse_query_string(data, &api_method.parameters, true) {
Ok(query) => query,
Err(ref error_list) => {
let msg = error_list.iter().fold(String::from(""), |acc, item| {
acc + &item.to_string() + "\n"
});
http_error_future!(BAD_REQUEST, msg);
}
}
}
None => json!({}),
};
/*if body.is_end_stream() {
println!("NO BODY");
}*/
match (api_method.handler)(param, &api_method) {
Ok(res) => {
let json_str = res.to_string();
return Response::new(Body::from(json_str));
}
Err(err) => {
http_error_future!(NOT_FOUND, format!("Method returned error '{}'\n", err));
}
}
*/
} else { } else {
http_error_future!(NOT_FOUND, format!("No such path '{}'\n", path)); http_error_future!(NOT_FOUND, format!("No such path '{}'\n", path));
} }
@ -195,8 +150,15 @@ fn main() {
let addr = ([127, 0, 0, 1], 8007).into(); let addr = ([127, 0, 0, 1], 8007).into();
let new_svc = || { let new_svc = || {
// service_fn_ok converts our function into a `Service` service_fn(|req| {
service_fn(handle_request) // clumsy way to convert failure::Error to Response
handle_request(req).then(|result| -> Result<Response<Body>, String> {
match result {
Ok(res) => Ok(res),
Err(err) => Ok(error_response!(NOT_FOUND, err.to_string())),
}
})
})
}; };
let server = Server::bind(&addr) let server = Server::bind(&addr)