aper_websocket_client/
client.rs

1use crate::typed::TypedWebsocketConnection;
2use anyhow::Result;
3use aper::{
4    connection::{ClientConnection, MessageToClient, MessageToServer},
5    AperClient, Store,
6};
7use aper_stateroom::{IntentEvent, StateProgram};
8use core::fmt::Debug;
9use std::{
10    rc::{Rc, Weak},
11    sync::Mutex,
12};
13
14pub struct AperWebSocketStateProgramClient<S>
15where
16    S: StateProgram,
17{
18    conn: Rc<Mutex<ClientConnection<S>>>,
19    store: Store,
20}
21
22impl<S> Debug for AperWebSocketStateProgramClient<S>
23where
24    S: StateProgram,
25{
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        f.debug_struct("AperWebSocketStateProgramClient").finish()
28    }
29}
30
31impl<S> AperWebSocketStateProgramClient<S>
32where
33    S: StateProgram,
34{
35    pub fn new<F>(url: &str, state_callback: F) -> Result<Self>
36    where
37        F: Fn(S, u32) + 'static,
38    {
39        // callback is called when the state changes
40        // need to create a connection
41        // connection needs to be able to call the state and message callback
42
43        // client message handler needs to have websocket connection; websocket
44        // connection needs to be able to send messages to client
45
46        let client = AperClient::<S>::new();
47        let store = client.store();
48
49        let conn = Rc::new_cyclic(|c: &Weak<Mutex<ClientConnection<S>>>| {
50            let d = c.clone();
51            let socket_message_callback = move |message: MessageToClient| {
52                let d = d.upgrade().unwrap();
53                let mut conn = d.lock().unwrap();
54                conn.receive(&message);
55            };
56
57            let wss_conn = TypedWebsocketConnection::new(url, socket_message_callback).unwrap();
58
59            let message_callback = Box::new(move |message: MessageToServer| {
60                wss_conn.send(&message);
61            });
62
63            Mutex::new(ClientConnection::new(
64                client,
65                message_callback,
66                state_callback,
67            ))
68        });
69
70        Ok(AperWebSocketStateProgramClient { conn, store })
71    }
72
73    pub fn state(&self) -> S {
74        S::attach(self.store.handle())
75    }
76
77    pub fn push_intent(&self, intent: S::T) -> Result<(), S::Error> {
78        let mut conn = self.conn.lock().unwrap();
79
80        let client = conn.client_id;
81        let intent = IntentEvent {
82            client,
83            timestamp: chrono::Utc::now(),
84            intent,
85        };
86
87        conn.apply(&intent)
88    }
89}