dstest 0.1.3

Deterministic Simulation Testing for containerised services
use std::net::{TcpStream, ToSocketAddrs};
use std::sync::Arc;
use std::time::Duration;

use mlua::{Lua, Result, Table};

use crate::bindings::context::BindingContext;

pub fn register(lua: &Lua, dstest: &Table, ctx: &BindingContext) -> Result<()> {
    let state = Arc::clone(&ctx.state);
    let config = Arc::clone(&ctx.config);

    let tcp_fn = lua.create_function(move |lua, (id, port): (String, u16)| {
        let host = {
            let state = state.lock().expect("poisoned lock");
            state
                .subject_hosts
                .get(&id)
                .cloned()
                .ok_or_else(|| mlua::Error::RuntimeError(format!("unknown subject {}", id)))?
        };

        let host_ip = host.split(':').next().unwrap_or(&host);
        let addr = format!("{}:{}", host_ip, port);

        let timeout_secs = {
            let cfg = config.lock().expect("poisoned lock");
            cfg.http_timeout_secs
        };

        let socket_addr = addr
            .to_socket_addrs()
            .map_err(|e| mlua::Error::RuntimeError(format!("invalid address: {}", e)))?
            .next()
            .ok_or_else(|| mlua::Error::RuntimeError("address resolved to nothing".to_string()))?;

        match TcpStream::connect_timeout(&socket_addr, Duration::from_secs(timeout_secs)) {
            Ok(_stream) => {
                let t = lua.create_table()?;
                t.set("connected", true)?;
                t.set("addr", addr)?;
                Ok(t)
            }
            Err(e) => {
                let t = lua.create_table()?;
                t.set("connected", false)?;
                t.set("error", e.to_string())?;
                t.set("addr", addr)?;
                Ok(t)
            }
        }
    })?;

    dstest.set("tcp", tcp_fn)?;
    Ok(())
}