use super::Response;
use crate::tarantool::tlua;
use crate::tarantool::tlua::AnyLuaValue::LuaAnyString;
use crate::tarantool::tlua::{AnyLuaString, AnyLuaValue};
use crate::transport::http::Request;
use std::cell::RefCell;
use std::collections::HashMap;
use std::str::from_utf8;
const LUA_HTTP_SERVER: &str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/lua/http_server.lua"));
pub trait LuaHttpHandler {
fn handle(&self, req: Request) -> Response;
fn method(&self) -> &str;
fn path(&self) -> &str;
}
pub struct Server {
registered_routes: RefCell<Vec<String>>,
}
impl Drop for Server {
fn drop(&mut self) {
self.unregister_all();
}
}
#[allow(clippy::new_without_default)]
impl Server {
pub fn new() -> Self {
let lua = tarantool::lua_state();
lua.exec(LUA_HTTP_SERVER).unwrap();
Self {
registered_routes: RefCell::new(vec![]),
}
}
pub fn unregister_all(&self) {
let lua = tarantool::lua_state();
let delete_handler = lua
.get::<tlua::LuaFunction<_>, _>("delete_handler")
.expect("delete_handler not found");
for route_name in self.registered_routes.borrow().iter() {
delete_handler
.call_with_args::<(), _>((route_name.clone(),))
.ok();
}
self.registered_routes.borrow_mut().clear();
}
pub fn register(&self, route: Box<dyn LuaHttpHandler>) {
let lua = tarantool::lua_state();
let path = route.path().to_string();
let method = route.method().to_string();
let lua_handler = tlua::function8(
move |method: String,
path: String,
query: String,
headers: HashMap<String, String>,
stash: HashMap<String, String>,
proto: Vec<i32>,
peer: HashMap<String, AnyLuaValue>,
body: AnyLuaString| {
let proto: [i32; 2] = proto.try_into().unwrap_or_default();
let lpeer: HashMap<String, String> = peer
.into_iter()
.filter_map(|(k, v)| match v {
AnyLuaValue::LuaString(s) => Some((k, s)),
AnyLuaValue::LuaAnyString(s) => {
Some((k, from_utf8(s.as_bytes()).ok().map(|s| s.to_owned())?))
}
AnyLuaValue::LuaNumber(f) => Some((k, format!("{}", f))),
_ => None,
})
.collect();
let res = route.handle(Request {
method,
path,
query,
headers,
stash,
body: body.as_bytes().to_vec(),
proto: proto.into(),
peer: lpeer.into(),
});
(
res.status,
LuaAnyString(AnyLuaString(res.body)),
res.headers,
)
},
);
let route_name = format!("{}:{}", method, path);
lua.get::<tlua::LuaFunction<_>, _>("register_handler")
.unwrap()
.call_with_args::<(), _>((path, method, lua_handler))
.expect("register route fail");
self.registered_routes.borrow_mut().push(route_name);
}
}