use iron::prelude::*;
use iron::status;
use iron::headers::{ContentType, Headers};
use iron::typemap::TypeMap;
use router::Router;
use indradb::Datastore;
use util::SimpleError;
use common::ProxyTransaction;
use std::error::Error as StdError;
use core::str::FromStr;
use iron::modifiers::Header as HeaderModifier;
use iron::mime::{Attr, Mime, SubLevel, TopLevel, Value};
use iron::request::Body;
use std::io;
use std::io::Read;
use serde_json::value::Value as JsonValue;
use serde_json;
use serde::{Deserialize, Serialize};
use statics;
use std::fs::File;
use std::path::Path;
use regex;
lazy_static! {
static ref SCRIPT_NAME_VALIDATOR: regex::Regex = regex::Regex::new(r"^[\w-_]+(\.lua)?$").unwrap();
}
pub fn create_iron_error(status_code: status::Status, err: String) -> IronError {
let body = serde_json::to_string(&json!({ "error": err })).unwrap();
let json_content_type_modifier = HeaderModifier(ContentType(get_json_mime()));
let modifiers = (status_code, json_content_type_modifier, body);
IronError::new(SimpleError::new(err), modifiers)
}
pub fn get_json_mime() -> Mime {
Mime(
TopLevel::Application,
SubLevel::Json,
vec![(Attr::Charset, Value::Utf8)],
)
}
pub fn to_response<T: Serialize>(status_code: status::Status, body: &T) -> Response {
let mut hs = Headers::new();
hs.set(ContentType(get_json_mime()));
Response {
status: Some(status_code),
headers: hs,
extensions: TypeMap::new(),
body: Some(Box::new(serde_json::to_string(&body).unwrap())),
}
}
pub fn get_url_param<T: FromStr>(req: &Request, name: &str) -> Result<T, IronError> {
let s = req.extensions.get::<Router>().unwrap().find(name).unwrap();
T::from_str(s).map_err(|_| {
create_iron_error(
status::BadRequest,
format!("Invalid value for URL param {}", name),
)
})
}
pub fn get_json_obj_value<T>(
json: &serde_json::Map<String, JsonValue>,
name: &str,
) -> Result<T, IronError>
where
for<'a> T: Deserialize<'a>,
{
if let Some(obj) = json.get(name) {
Ok(serde_json::from_value::<T>(obj.clone()).map_err(|_| {
create_iron_error(status::BadRequest, format!("Invalid type for `{}`", name))
})?)
} else {
Err(create_iron_error(
status::BadRequest,
format!("Missing `{}`", name),
))
}
}
pub fn get_transaction() -> Result<ProxyTransaction, IronError> {
Ok(statics::DATASTORE.transaction().map_err(|err| {
create_iron_error(
status::InternalServerError,
format!("Could not create datastore transaction: {}", err),
)
})?)
}
pub fn read_json<T>(body: &mut Body) -> Result<Option<T>, IronError>
where
for<'a> T: Deserialize<'a>,
{
let mut payload = String::new();
let read_result: Result<usize, io::Error> = body.read_to_string(&mut payload);
if let Err(err) = read_result {
return Err(create_iron_error(
status::BadRequest,
format!("Could not read JSON body: {}", err),
));
}
if payload.is_empty() {
Ok(None)
} else {
Ok(Some(serde_json::from_str::<T>(&payload[..]).map_err(
|err| {
create_iron_error(
status::BadRequest,
format!("Could not parse JSON payload: {}", err.description()),
)
},
)?))
}
}
pub fn get_script_file(name: String) -> Result<(String, String), IronError> {
if !SCRIPT_NAME_VALIDATOR.is_match(&name[..]) {
return Err(create_iron_error(
status::BadRequest,
"Invalid script name".to_string(),
));
}
let path = Path::new(&statics::SCRIPT_ROOT[..]).join(name);
let path_str = match path.to_str() {
Some(path_str) => path_str,
None => {
return Err(create_iron_error(
status::InternalServerError,
"Could not stringify script path".to_string(),
))
}
};
match File::open(&path) {
Ok(mut file) => {
let mut contents = String::new();
match file.read_to_string(&mut contents) {
Ok(_) => Ok((path_str.to_string(), contents)),
Err(_) => Err(create_iron_error(
status::NotFound,
"Could not read script".to_string(),
)),
}
}
Err(_) => Err(create_iron_error(
status::NotFound,
"Could not load script".to_string(),
)),
}
}