#![allow(dead_code)] use anyhow::{anyhow, Result}; use crossbeam_channel::{select, unbounded, Receiver, Sender}; use std::{marker::PhantomData, thread::spawn}; struct Channel { pub rx: Receiver, pub tx: Sender, _ph: PhantomData, } fn mk_chan() -> (Channel, Channel) { let (tx_to, rx_to) = unbounded(); let (tx_fro, rx_fro) = unbounded(); ( Channel { rx: rx_fro, tx: tx_to, _ph: PhantomData, }, Channel { rx: rx_to, tx: tx_fro, _ph: PhantomData, }, ) } struct Tx { cont: Cont, _ph: PhantomData, } impl Default for Tx { fn default() -> Self { Self { cont: Cont::default(), _ph: PhantomData, } } } struct Rx { cont: Cont, _ph: PhantomData, } impl Default for Rx { fn default() -> Self { Self { cont: Cont::default(), _ph: PhantomData, } } } #[derive(Default)] struct End; impl Tx { pub fn send(self, channel: &Channel, value: u32) -> Result { channel.tx.send(value)?; Ok(self.cont) } } fn tx(_role: R, cont: C) -> Tx { Tx { cont, _ph: PhantomData, } } impl Rx { pub fn recv(self, channel: &Channel) -> Result<(u32, Cont)> { let ret = channel.rx.recv()?; Ok((ret, self.cont)) } } fn rx(_role: R, cont: C) -> Rx { Rx { cont, _ph: PhantomData, } } // The local code can use either left or right, which will // in any case consume the Choice. struct MyChoice { left: C1, right: C2, } // This node allows receiving from either R1 or R2, // depending on an external choice struct TheirChoice { left: C1, right: C2, _ph: PhantomData<(R1, R2)>, } enum ChoiceResult { Left(u32, C1), Right(u32, C2), } impl TheirChoice { pub fn recv(self, left: Channel, right: Channel) -> Result> { select! { recv(left.rx) -> msg => Ok(ChoiceResult::Left(msg?, self.left)), recv(right.rx) -> msg => Ok(ChoiceResult::Right(msg?, self.right)), } } } macro_rules! rec { ($x: ident, $t: ty) => {{ #[derive(Default)] struct $x; impl $x { pub fn rec(self) -> $t { Default::default() } } $x::default().rec() }}; } type RecRoleB = Rx Rec>; fn rec_test(ch_a: Channel, ch_b: Channel) -> Result<()> { let mut p = rec!(X, Tx>); // the above produces the same code as below, but // rust-analyzer cannot use the resulting types, so it // is useless let mut _p = { struct Rec { pub rec: Tx>, } fn new() -> Rec { Rec { rec: tx(RoleA, rx(RoleB, new)), } } new().rec }; loop { let p2 = p.send(&ch_a, 42)?; let (v, p2) = p2.recv(&ch_b)?; println!("received {v:?}"); p = p2.rec(); } } struct RoleA; struct RoleB; struct RoleC; fn fancy_building_block( to_a: &Channel, to_b: &Channel, p: Tx>, ) -> Result { Ok(p.send(to_a, 55)?.recv(to_b)?.1) } fn main() -> Result<()> { let prot_a = tx(RoleB, rx(RoleC, End)); let prot_b = rx(RoleA, tx(RoleC, End)); let prot_c = rx(RoleB, tx(RoleA, End)); let (ch_ab, ch_ba) = mk_chan::(); let (ch_ac, ch_ca) = mk_chan::(); let (ch_bc, ch_cb) = mk_chan::(); let thread_a = spawn(move || prot_a.send(&ch_ab, 42)?.recv(&ch_ac)); let thread_b = spawn(move || { let (value, p) = prot_b.recv(&ch_ba)?; p.send(&ch_bc, value) }); let thread_c = spawn(move || { let (value, p) = prot_c.recv(&ch_cb)?; p.send(&ch_ca, value) }); let (x, _end): (u32, End) = thread_a.join().map_err(|x| anyhow!("{:?}", x))??; let _end: End = thread_b.join().map_err(|x| anyhow!("{:?}", x))??; let _end: End = thread_c.join().map_err(|x| anyhow!("{:?}", x))??; println!("result: {x:?}"); Ok(()) }