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
/// A trait describing how to encode/decode a websocket message. This is provided to allow users to
/// use their own websocket library instead of the default one.
///
/// Typically the `Input` and `Output` message types will be the same, but they're defined
/// separately to allow for flexibility (e.g., if the underlying websocket client uses distinct
/// types for sending vs receiving, like validating UTF-8 only for outgoing messages).
///
/// # Example
///
/// ```
/// use vtubestudio::codec::MessageCodec;
///
/// // Custom websocket message type
/// pub enum Message {
///     Text(String),
///     Binary(Vec<u8>),
///     Ping(Vec<u8>),
///     Pong(Vec<u8>),
///     Close,
/// }
///
/// #[derive(Debug, Clone)]
/// pub struct MyCustomMessageCodec;
///
/// impl MessageCodec for MyCustomMessageCodec {
///     type Input = Message;
///     type Output = Message;
///     type Error = std::convert::Infallible;
///
///     fn decode(msg: Self::Input) -> Result<Option<String>, Self::Error> {
///         Ok(match msg {
///             Message::Text(s) => Some(s),
///             _ => None,
///         })
///     }
///
///     fn encode(text: String) -> Self::Output {
///         Message::Text(text)
///     }
/// }
/// ```
pub trait MessageCodec {
    /// The underlying incoming message type.
    type Input;

    /// The underlying outgoing message type.
    type Output;

    /// Error type returned on decode failure.
    type Error;

    /// Decodes a websocket text message. `None` values are ignored (E.g., for disregarding ping
    /// messages).
    fn decode(msg: Self::Input) -> Result<Option<String>, Self::Error>;

    /// Converts a string into a websocket text message.
    fn encode(text: String) -> Self::Output;
}

crate::cfg_feature! {
    #![feature = "tokio-tungstenite"]

    /// A codec describing how to encode/decode
    /// [`tungstenite::Message`](::tokio_tungstenite::tungstenite::Message)s.
    #[derive(Debug, Clone)]
    pub struct TungsteniteCodec;
}

#[cfg(feature = "tokio-tungstenite")]
mod tungstenite {
    use super::*;

    use std::convert::Infallible;
    use tokio_tungstenite::tungstenite;

    impl MessageCodec for TungsteniteCodec {
        type Input = tungstenite::Message;
        type Output = tungstenite::Message;
        type Error = Infallible;

        fn decode(msg: Self::Input) -> Result<Option<String>, Self::Error> {
            Ok(match msg {
                Self::Input::Text(s) => Some(s),
                _ => None,
            })
        }

        fn encode(text: String) -> Self::Output {
            Self::Output::Text(text)
        }
    }
}