libuv 2.13.1

A safe rust wrapper for libuv
Documentation
//! To run this example, you'll need to compile cgi-tick first:
//!
//! ```bash
//! cargo build --example cgi-tick
//! cargo run --example cgi
//! ```
//!
//! Then connect to localhost:7000:
//!
//! ```bash
//! nc localhost 7000
//! ```

extern crate libuv;
use libuv::prelude::*;
use libuv::{
    exepath, ProcessHandle, ProcessOptions, StdioContainer, StdioFlags, StdioType, TcpBindFlags,
    TcpHandle,
};
use std::net::Ipv4Addr;
use std::path::PathBuf;

fn invoke_cgi_script(client: TcpHandle) -> Result<(), Box<dyn std::error::Error>> {
    let mut path: PathBuf = exepath()?.into();
    if cfg!(windows) {
        path.set_file_name("cgi-tick.exe");
    } else {
        path.set_file_name("cgi-tick");
    }

    let child_stdio = [
        Default::default(),
        StdioContainer {
            flags: StdioFlags::INHERIT_STREAM,
            data: StdioType::Stream(client.to_stream()),
        },
        Default::default(),
    ];

    let path = path.to_string_lossy().into_owned();
    let args: [&str; 1] = [&path];
    let mut options = ProcessOptions::new(&args);
    options.stdio = &child_stdio;

    let mut client_clone = client.clone();
    options.exit_cb = (move |mut handle: ProcessHandle, exit_status: i64, term_signal: i32| {
        println!(
            "Process exited with status {}, signal {}",
            exit_status, term_signal
        );
        handle.close(());
        client_clone.close(());
    })
    .into();

    client.get_loop().spawn_process(options)?;

    Ok(())
}

fn on_new_connection(mut server: StreamHandle, status: libuv::Result<u32>) {
    if let Err(e) = status {
        eprintln!("New connection error: {}", e);
        return;
    }

    if let Ok(mut client) = server.get_loop().tcp() {
        match server.accept(&mut client.to_stream()) {
            Ok(_) => {
                if let Err(e) = invoke_cgi_script(client) {
                    eprintln!("Error invoking CGI script: {}", e);
                }
            }
            Err(_) => {
                client.close(());
            }
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut r#loop = Loop::default()?;

    let mut server = r#loop.tcp()?;
    let addr = (Ipv4Addr::UNSPECIFIED, 7000).into();
    server.bind(&addr, TcpBindFlags::empty())?;
    server.listen(128, on_new_connection)?;

    r#loop.run(RunMode::Default)?;

    Ok(())
}