branching/
branching.rs

1use par::{
2    exchange::{Recv, Send},
3    runtimes::tokio::fork,
4    Dual, Session,
5};
6use std::{collections::HashMap, sync::Arc, time::Duration};
7
8struct Amount(i64);
9struct Money(i64);
10struct InsufficientFunds;
11
12enum Operation {
13    CheckBalance(Send<Amount>),
14    Withdraw(Recv<Amount, Send<Result<Money, InsufficientFunds>>>),
15}
16
17struct Account(String);
18struct InvalidAccount;
19
20type ATM = Send<Account, Recv<Result<Send<Operation>, InvalidAccount>>>;
21type Client = Dual<ATM>; // Recv<Account, Send<Result<Send<Operation>, InvalidAccount>>>
22
23fn check_balance(number: String) -> Client {
24    fork(|atm: ATM| async move {
25        let atm = atm.send(Account(number.clone()));
26        let Ok(atm) = atm.recv1().await else {
27            return println!("Invalid account: {}", number);
28        };
29        let Amount(funds) = atm.choose(Operation::CheckBalance).recv1().await;
30        println!("{} has {}", number, funds);
31    })
32}
33
34fn withdraw(number: String, Amount(requested): Amount) -> Client {
35    fork(|atm: ATM| async move {
36        let Ok(atm) = atm.send(Account(number.clone())).recv1().await else {
37            return println!("Invalid account: {}", number);
38        };
39        let response = atm
40            .choose(Operation::Withdraw)
41            .send(Amount(requested))
42            .recv1()
43            .await;
44        match response {
45            Ok(Money(withdrawn)) => println!("{} withdrawn from {}", withdrawn, number),
46            Err(InsufficientFunds) => println!(
47                "{} has insufficient funds to withdraw {}",
48                number, requested
49            ),
50        }
51    })
52}
53
54fn boot_atm(accounts: Arc<HashMap<String, Money>>) -> ATM {
55    fork(|client: Client| async move {
56        let (Account(number), client) = client.recv().await;
57        let Some(&Money(funds)) = accounts.get(&number) else {
58            return client.send1(Err(InvalidAccount));
59        };
60        match client.choose(Ok).recv1().await {
61            Operation::CheckBalance(client) => client.send1(Amount(funds)),
62            Operation::Withdraw(client) => {
63                let (Amount(requested), client) = client.recv().await;
64                if funds >= requested {
65                    client.send1(Ok(Money(requested)));
66                } else {
67                    client.send1(Err(InsufficientFunds));
68                }
69            }
70        }
71    })
72}
73
74#[tokio::main]
75async fn main() {
76    let accounts = Arc::new(HashMap::from([
77        ("Alice".to_string(), Money(1000)),
78        ("Bob".to_string(), Money(700)),
79        ("Cyril".to_string(), Money(5500)),
80    ]));
81
82    let atm = boot_atm(Arc::clone(&accounts));
83    let client = withdraw("Cyril".to_string(), Amount(2500));
84
85    atm.link(client);
86
87    boot_atm(Arc::clone(&accounts)).link(check_balance("Alice".to_string()));
88    boot_atm(Arc::clone(&accounts)).link(withdraw("Bob".to_string(), Amount(1000)));
89    boot_atm(Arc::clone(&accounts)).link(withdraw("Dylan".to_string(), Amount(20)));
90
91    tokio::time::sleep(Duration::from_secs(1)).await;
92}