shors 0.12.6

Transport layer for cartridge + tarantool-module projects.
Documentation
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::collections::HashMap;
use std::str::from_utf8;

pub trait LuaHttpHandler {
    fn handle(&self, req: Request) -> Response;
    fn method(&self) -> &str;
    fn path(&self) -> &str;
}

/// Http server.
pub struct Server {}

#[allow(clippy::new_without_default)]
impl Server {
    pub fn new() -> Self {
        let lua = tarantool::lua_state();

        lua.exec("
            local function make_json_handler(fn)
                return function(req)
                    local data = req:read()  

                    local status, body, headers = fn(req.method, req.path, req.query, req.headers, req.tstash, req.proto, req.peer, data)

                    return {
                        status = status,
                        body = body,
                        headers = headers,
                    }
                end
            end

            function register_handler(route, method, handler)
                local httpd = assert(rawget(_G, 'pico') and rawget(_G, 'pico').httpd or require('cartridge').service_get('httpd'), 'Failed to get httpd service')
                local metrics = require('metrics')
                httpd:route(
                        { method = method, path = route },
                        metrics.http_middleware.v1(
                            make_json_handler(handler),
                            metrics.http_middleware.get_default_collector()
                        )
                )
            end
        ").unwrap();

        Self {}
    }

    /// Register http route as prepared for serve.
    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,
                )
            },
        );

        lua.get::<tlua::LuaFunction<_>, _>("register_handler")
            .unwrap()
            .call_with_args::<(), _>((path, method, lua_handler))
            .expect("register route fail");
    }
}