chorus_lib 0.5.0

Choreographic Programming in Rust
Documentation
use chorus_lib::{
    core::{
        ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, MultiplyLocated,
        Projector,
    },
    transport::local::{LocalTransport, LocalTransportChannelBuilder},
};
use rand::Rng;
use serde::{Deserialize, Serialize};

type Query = String;

#[derive(Serialize, Deserialize)]
enum Choice {
    Alice,
    Bob,
}

#[derive(ChoreographyLocation)]
struct Alice;

#[derive(ChoreographyLocation)]
struct Bob;

#[derive(ChoreographyLocation)]
struct Carol;

struct MainChoreography;

impl Choreography for MainChoreography {
    type L = LocationSet!(Alice, Bob, Carol);
    fn run(self, op: &impl ChoreoOp<Self::L>) {
        let choice = op.locally(Alice, |_| {
            let mut rng = rand::thread_rng();
            let choice: bool = rng.gen();
            if choice {
                Choice::Alice
            } else {
                Choice::Bob
            }
        });
        let choice_and_query = op.conclave(ChooseQueryChoreography {
            alice_choice: choice,
        });
        let query_at_alice = op.locally(Alice, |un| {
            let query = un.unwrap(&choice_and_query);
            String::from(un.unwrap(&query.1))
        });
        let query_at_carol = op.comm(Alice, Carol, &query_at_alice);
        let response_at_carol = op.locally(Carol, |un| {
            let query = un.unwrap(&query_at_carol);
            println!("Carol received query: {}", query);
            let r = format!("Carol's response to {}", query);
            return r;
        });
        let response = op.broadcast(Carol, response_at_carol);
        op.conclave(TerminalChoreography {
            choice_and_query,
            response,
        });
    }
}

struct ChoiceAndQuery(
    MultiplyLocated<Choice, LocationSet!(Alice, Bob)>,
    Located<Query, Alice>,
);

struct ChooseQueryChoreography {
    alice_choice: Located<Choice, Alice>,
}

impl Choreography<ChoiceAndQuery> for ChooseQueryChoreography {
    type L = LocationSet!(Alice, Bob);
    fn run(self, op: &impl ChoreoOp<Self::L>) -> ChoiceAndQuery {
        let choice = op.broadcast(Alice, self.alice_choice);
        let query = match choice {
            Choice::Alice => op.locally(Alice, |_| "Alice's query".to_string()),
            Choice::Bob => {
                let bob_query = op.locally(Bob, |_| "Bob's query".to_string());
                op.comm(Bob, Alice, &bob_query)
            }
        };
        return ChoiceAndQuery(op.unnaked(choice), query);
    }
}

struct TerminalChoreography {
    choice_and_query: MultiplyLocated<ChoiceAndQuery, LocationSet!(Alice, Bob)>,
    response: String,
}

impl Choreography for TerminalChoreography {
    type L = LocationSet!(Alice, Bob);
    fn run(self, op: &impl ChoreoOp<Self::L>) {
        let ChoiceAndQuery(choice, _) = op.naked(self.choice_and_query);
        match op.naked(choice) {
            Choice::Alice => {
                op.locally(Alice, |_| {
                    println!("Alice received response: {}", self.response);
                });
            }
            Choice::Bob => {
                op.locally(Bob, |_| {
                    println!("Bob received response: {}", self.response);
                });
            }
        }
    }
}

fn main() {
    let mut handles = Vec::new();
    let transport_channel = LocalTransportChannelBuilder::new()
        .with(Alice)
        .with(Bob)
        .with(Carol)
        .build();
    {
        let transport = LocalTransport::new(Alice, transport_channel.clone());
        handles.push(std::thread::spawn(move || {
            let p = Projector::new(Alice, transport);
            p.epp_and_run(MainChoreography);
        }));
    }
    {
        let transport = LocalTransport::new(Bob, transport_channel.clone());
        handles.push(std::thread::spawn(move || {
            let p = Projector::new(Bob, transport);
            p.epp_and_run(MainChoreography);
        }));
    }
    {
        let transport = LocalTransport::new(Carol, transport_channel.clone());
        handles.push(std::thread::spawn(move || {
            let p = Projector::new(Carol, transport);
            p.epp_and_run(MainChoreography);
        }));
    }
    for handle in handles {
        handle.join().unwrap();
    }
}