1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::{ChannelClient, ChannelEvent};
use serde::Serialize;
use std::ops::{Deref, DerefMut};
use trillium_websockets::WebSocketConn;

/**
A ChannelConn is a wrapper around a [`WebSocketConn`] that also
contains a [`ChannelClient`]

It that provides convenient access to functions from the ChannelClient
held in the WebSocketConn's StateSet, and dereferences to the
`WebSocketConn` for other functionality.
*/
#[derive(Debug)]
pub struct ChannelConn<'a> {
    pub(crate) conn: &'a mut WebSocketConn,
}

macro_rules! channel_client {
    ($conn:expr) => {
        match $conn.client() {
            Some(client) => client,
            None => {
                log::error!("could not unwrap client on {}:{}", file!(), line!());
                return;
            }
        }
    };
}

impl ChannelConn<'_> {
    /**
    Borrow the channel client
    */
    pub fn client(&self) -> Option<&ChannelClient> {
        self.state()
    }

    /**
    Borrow the websocket conn
    */
    pub fn conn(&self) -> &WebSocketConn {
        self
    }

    /**
    Send a [`ChannelEvent`] to all connected clients. Note that
    these messages will only reach clients that subscribe to the
    event's topic.
    */
    pub fn broadcast(&self, event: impl Into<ChannelEvent>) {
        channel_client!(self).broadcast(event);
    }

    /**
    Send a [`ChannelEvent`] to this specific client. Note that
    this message will only be received if the client subscribes to
    the event's topic.
    */
    pub async fn send_event(&self, event: impl Into<ChannelEvent>) {
        channel_client!(self).send_event(event).await;
    }

    /**
    Send an ok reply in reference to the provided ChannelEvent
    with the provided response payload.

    Note that this sets the event as `"phx_reply"` and the payload as
    `{"status": "ok", "response": response }`, as well as setting the
    reference field.
    */
    pub async fn reply_ok(&self, event: &ChannelEvent, response: &impl Serialize) {
        channel_client!(self).reply_ok(event, response).await;
    }

    /**
    Send an error reply in reference to the provided ChannelEvent
    with the provided response payload.

    Note that this sets the event as `"phx_error"` as well as setting
    the reference field.
    */
    pub async fn reply_error(&self, event: &ChannelEvent, error: &impl Serialize) {
        channel_client!(self).reply_error(event, error).await;
    }

    /**
    Join a topic, sending an ok reply with the provided optional
    value. This sends an ok reply to the client as well as adding the
    topic to the client's subscriptions.
    */
    pub async fn allow_join(&self, event: &ChannelEvent, value: &impl Serialize) {
        channel_client!(self).allow_join(event, value).await;
    }

    /**
    Leave a topic as requested by the provided channel event,
    including the optional payload. This sends an ok reply to the
    client as well as removing the channel from the client's
    subscriptions.
    */
    pub async fn allow_leave(&self, event: &ChannelEvent, payload: &impl Serialize) {
        channel_client!(self).allow_leave(event, payload).await;
    }
}

impl Deref for ChannelConn<'_> {
    type Target = WebSocketConn;

    fn deref(&self) -> &Self::Target {
        &*self.conn
    }
}

impl DerefMut for ChannelConn<'_> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.conn
    }
}