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
120
121
122
123
124
125
126
127
128
//! A wrapper around [`WebSocket`] that reconnects when the socket
//! drops. Uses [`Backoff`] to determine the delay between reconnects
//!
//! # Features
//!
//! * `tracing` - enables the [`tracing`] crate and logs everything it's doing
//! * `state-events` - changes the Item type of the stream to be an enum that is either a message or
//!   a
//! status change Both are enabled by default
//!
//! # Usage
//!
//! Input means stuff you want to send from this client to the server
//!
//! Outut means stuff you want to receive back from the server
//!
//! 1. Implement [`TryFrom`] for [`Message`] for your input type
//!     * The [`TryFrom::Error`] type must implement [`Debug`]
//! 1. Implement [`TryFrom<Message>`] for your output type
//!     * The [`TryFrom::Error`] type must implement [`Debug`]
//! 1. Both input and output need to implement [`Unpin`] and, if using tracing feature, [`Debug`]
//! 1. Use [`SocketBuilder`] to set the URL and configure backoff. [`get_proto_and_host`] can help
//!    constructing the URL relative to the current `window.location`
//! 1. Call [`SocketBuilder::open`] to connect the socket. The errors `open` returns are likely
//!    fatal (invalid URL, blocked port), see [`WebSocket::open`] for details. The first connect is
//!    done in the builder so it fails fast if these fatal errors occur but the same kind of error
//!    can also occur on any reconnect and be returned by the [`Socket`] [`Stream`] implementation
//! 1. The returned [`Socket`] can then be polled to get incoming messages. [`Socket::send`] can be
//!    called to send messages or [`Socket::get_sender`] can be used to get an [`UnboundedSender`].
//!    [`Socket::close`] or dropping it will drop the inner [`WebSocket`] which sends a close frame
//!    and cleans up the event handlers
//!
//! # Example
//!
//! `tests/reconnect.rs`
//! ```rust
#![doc = include_str!("../tests/reconnect.rs")]
//! ```
//! 
//! [`WebSocket`]: gloo::net::websocket::futures::WebSocket
//! [`Backoff`]: exponential_backoff::Backoff
//! [`WebSocket::open`]: gloo::net::websocket::futures::WebSocket::open
//! [`Stream`]: futures::Stream
//! [`UnboundedSender`]: futures::channel::mpsc::UnboundedSender

// TODO: Replace unbounded with a reasonable bounded channel

#![warn(missing_docs)]

use std::fmt::Debug;

use cfg_if::cfg_if;
#[doc(inline)]
/// Re-export of [`gloo::net::websocket::Message`].
pub use gloo::net::websocket::Message;

mod error;
pub use error::Error;

mod location;
pub use location::{get_proto_and_host, HttpProtocol, WebSocketProtocol};

mod event;
pub use event::Event;

mod constants;
pub use constants::{DEFAULT_BACKOFF_MAX, DEFAULT_BACKOFF_MIN, DEFAULT_MAX_RETRIES};

mod builder;
pub use builder::SocketBuilder;

mod state;
pub use state::State;

mod socket;
pub use socket::Socket;

// Plumbing for making it work with and without tracing
cfg_if! {
    if #[cfg(feature = "tracing")] {
        #[allow(unused_imports)]
        use tracing::{trace, debug, info, warn, error};

        /// Trait expressing the requirements for a socket input type
        /// You don't need to implement it directly, there is a blanked implementation for types that implement
        /// [`Unpin`], [`Debug`], <[`Message`] as [`TryFrom<Self>`]>
        pub trait SocketInput: Unpin + Debug + Sized
        where
            Message: TryFrom<Self>,
            <Message as TryFrom<Self>>::Error: Debug
        {}

        /// Trait expressing the requirements for a socket output type
        /// You don't need to implement it directly, there is a blanked implementation for types that implement
        /// [`Unpin`], [`Debug`], <`Self` as [`TryFrom<Message>`]>
        pub trait SocketOutput: Unpin + TryFrom<Message> + Debug
        where <Self as TryFrom<Message>>::Error: Debug {}

        impl<T: Unpin + Debug + Sized> SocketInput for T
        where
            Message: TryFrom<T>,
            <Message as TryFrom<T>>::Error: Debug
        {}

        impl<T: Unpin + TryFrom<Message> + Debug> SocketOutput for T
        where <T as TryFrom<Message>>::Error: Debug {}
    } else {
        mod dummy_tracing;

        pub trait SocketInput: Unpin + Sized
        where
            Message: TryFrom<Self>,
            <Message as TryFrom<Self>>::Error: Debug
        {}

        pub trait SocketOutput: Unpin + TryFrom<Message>
        where <Self as TryFrom<Message>>::Error: Debug {}

        impl<T: Unpin + Sized> SocketInput for T
        where
            Message: TryFrom<T>,
            <Message as TryFrom<T>>::Error: Debug
        {}

        impl<T: Unpin + TryFrom<Message>> SocketOutput for T
        where <T as TryFrom<Message>>::Error: Debug {}
    }
}