mod prelude {
    pub use std::sync::{Arc, RwLock};
    pub use tokio::runtime::Runtime;
    pub use tokio::sync::oneshot::{channel, Sender};
    pub use tokio::time::{sleep, timeout, Duration};
}
use prelude::*;
#[macro_use]
extern crate lazy_static;
const RUNTIME_INIT: Option<Runtime> = None;
lazy_static! {
    static ref RUNTIMES: Arc<RwLock<[Option<Runtime>; 256]>> =
        Arc::new(RwLock::new([RUNTIME_INIT; 256]));
}
#[derive(Debug)]
pub struct Context {
    pub profile: u8,
    pub timeout: Duration,
}
pub fn init_runtime(profile: u8) {
    {
        let r = RUNTIMES.read().unwrap();
        if r[profile as usize].is_some() {
            return;
        }
    }
    let mut w = RUNTIMES.write().unwrap();
    if w[profile as usize].is_none() {
        w[profile as usize] = Some(Runtime::new().unwrap());
    }
}
#[macro_export]
macro_rules! go {
    (|$x:ident : Sender<$t:ty>|$y:expr) => {
        async {
            let (sender, receiver) = channel::<$t>();
            init_runtime(0);
            let rts = RUNTIMES.read().unwrap();
            let runtime = rts[0].as_ref().unwrap();
            runtime.spawn((|$x: Sender<$t>| $y)(sender));
            match receiver.await {
                Ok(v) => Ok(v),
                Err(_) => Err("unknown error"),
            }
        }
    };
    (|$x:ident : Sender<$t:ty>|$y:expr,$c:expr) => {
        async {
            let (sender, receiver) = channel::<$t>();
            init_runtime($c.profile);
            let rts = RUNTIMES.read().unwrap();
            let runtime = rts[$c.profile as usize].as_ref().unwrap();
            runtime.spawn((|$x: Sender<$t>| $y)(sender));
            match timeout($c.timeout, receiver).await {
                Err(_) => Err("timeout"),
                Ok(v) => Ok(v.unwrap()),
            }
        }
    };
}
#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    #[tokio::test]
    async fn it_works() {
        let r1 = go!(|sender: Sender<i32>| async move {
            println!("Thread id: {:?}", thread::current().id());
            if let Err(_) = sender.send(2) {
                println!("the receiver dropped");
            }
        })
        .await
        .unwrap();
        assert_eq!(r1, 2);
        let r2 = go!(
            |sender: Sender<String>| async move {
                println!("Thread id: {:?}", thread::current().id());
                if let Err(_) = sender.send("shit".to_string()) {
                    println!("the receiver dropped");
                }
            },
            Context {
                profile: 1,
                timeout: Duration::from_secs(1)
            }
        )
        .await
        .unwrap();
        assert_eq!(r2, "shit");
        let r3 = go!(|sender: Sender<()>| async move {
            println!("Thread id: {:?}", thread::current().id());
            if let Err(_) = sender.send(()) {
                println!("the receiver dropped");
            }
        })
        .await
        .unwrap();
        assert_eq!(r3, ());
    }
}