#[allow(unused_macros)] macro_rules! main { (@bmain) => {{ use std::sync::mpsc; let (snd, rcv) = mpsc::channel(); let srv = std::thread::spawn(move || server::main(snd)); let _ = rcv.recv(); if let Err(e) = client::main() { eprintln!("Client exited early with error: {:#}", e); } if let Err(e) = srv.join().expect("server thread panicked") { eprintln!("Server exited early with error: {:#}", e); } Ok(()) }}; () => { mod client; mod server; fn main() -> anyhow::Result<()> { main!(@bmain) } }; ($($pred:tt)*) => { #[cfg(all($($pred)*))] mod client; #[cfg(all($($pred)*))] mod server; #[cfg(all($($pred)*))] fn main() -> anyhow::Result<()> { main!(@bmain); } #[cfg(not(all($($pred)*)))] fn main() -> anyhow::Result<()> { eprintln!("not supported on this platform"); Ok(()) } }; } #[allow(unused_macros)] macro_rules! tokio_main { (@bmain) => {{ use tokio::sync::oneshot; let (snd, rcv) = oneshot::channel(); let a = async { if let Err(e) = main_a(snd).await { eprintln!("Server exited early with error: {:#}", e); } }; let b = async { if rcv.await.is_ok() { if let Err(e) = main_b().await { eprintln!("Client exited early with error: {:#}", e); } } }; tokio::join!(a, b); Ok(()) }}; () => { mod client; mod server; #[tokio::main(flavor = "current_thread")] async fn main() -> anyhow::Result<()> { tokio_main!(@bmain) } }; (nomod $($pred:tt)*) => { #[cfg(all($($pred)*))] #[tokio::main(flavor = "current_thread")] async fn main() -> anyhow::Result<()> { tokio_main!(@bmain) } #[cfg(not(all($($pred)*)))] #[tokio::main(flavor = "current_thread")] async fn main() -> anyhow::Result<()> { eprintln!("not supported on this platform or feature set"); Ok(()) } }; ($($pred:tt)*) => { #[cfg(all($($pred)*))] mod client; #[cfg(all($($pred)*))] mod server; #[cfg(all($($pred)*))] use {server::main as main_a, client::main as main_b}; tokio_main!(nomod $($pred)*); }; }