mlua 0.3.0-alpha.1

High level bindings to Lua 5.1/5.2/5.3 (including LuaJIT) with support of writing native lua modules in Rust.
Documentation
use std::net::Shutdown;
use std::rc::Rc;

use bstr::BString;
use tokio::net::{TcpListener, TcpStream};
use tokio::prelude::*;
use tokio::sync::Mutex;
use tokio::task;

use mlua::{Function, Lua, Result, Thread, UserData, UserDataMethods};

#[derive(Clone)]
struct LuaTcpListener(Option<Rc<Mutex<TcpListener>>>);

#[derive(Clone)]
struct LuaTcpStream(Rc<Mutex<TcpStream>>);

impl UserData for LuaTcpListener {
    fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
        methods.add_async_function("bind", |_, addr: String| async {
            let listener = TcpListener::bind(addr).await?;
            Ok(LuaTcpListener(Some(Rc::new(Mutex::new(listener)))))
        });

        methods.add_async_method("accept", |_, listener, ()| async {
            let (stream, _) = listener.0.unwrap().lock().await.accept().await?;
            Ok(LuaTcpStream(Rc::new(Mutex::new(stream))))
        });
    }
}

impl UserData for LuaTcpStream {
    fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
        methods.add_async_method("peer_addr", |_, stream, ()| async move {
            Ok(stream.0.lock().await.peer_addr()?.to_string())
        });

        methods.add_async_method("read", |_, stream, size: usize| async move {
            let mut buf = vec![0; size];
            let mut stream = stream.0.lock().await;
            let n = stream.read(&mut buf).await?;
            buf.truncate(n);
            Ok(BString::from(buf))
        });

        methods.add_async_method("write", |_, stream, data: BString| async move {
            let mut stream = stream.0.lock().await;
            let n = stream.write(&data).await?;
            Ok(n)
        });

        methods.add_async_method("close", |_, stream, ()| async move {
            stream.0.lock().await.shutdown(Shutdown::Both)?;
            Ok(())
        });
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let lua = Lua::new();

    let globals = lua.globals();
    globals.set("tcp", LuaTcpListener(None))?;

    globals.set(
        "spawn",
        lua.create_function(move |lua: &Lua, func: Function| {
            let fut = lua.create_thread(func)?.into_async::<_, ()>(());
            task::spawn_local(async move { fut.await.unwrap() });
            Ok(())
        })?,
    )?;

    let thread = lua
        .load(
            r#"
            coroutine.create(function ()
                local listener = tcp.bind("0.0.0.0:1234")
                print("listening on 0.0.0.0:1234")
                while true do
                    local stream = listener:accept()
                    print("connected from " .. stream:peer_addr())
                    spawn(function()
                        while true do
                            local data = stream:read(100)
                            data = data:match("^%s*(.-)%s*$") -- trim
                            print(data)
                            stream:write("got: "..data.."\n")
                            if data == "exit" then
                                stream:close()
                                break
                            end
                        end
                    end)
                end
            end)
        "#,
        )
        .eval::<Thread>()?;

    thread.into_async(()).await
}