signalrs_client_custom_auth/hub/mod.rs
1//! Client-side hub
2
3pub mod arguments;
4pub mod error;
5mod functions;
6pub(crate) mod invocation;
7
8use self::{
9 error::{HubError, MalformedRequest},
10 functions::{Handler, HandlerWrapper, HubMethod},
11 invocation::HubInvocation,
12};
13use super::messages::ClientMessage;
14use crate::protocol::MessageType;
15use serde::Deserialize;
16use std::collections::HashMap;
17use tracing::*;
18
19/// Client-side hub
20///
21/// [`Hub`] can be called by the server. It currently supports only value-like arguments - ones that can be deserialized from a single message.
22/// There are also stream-like arguments - ones that server can stream to client asynchronously, but they are not supported yet.
23/// ```rust, no_run
24/// use signalrs_client::SignalRClient;
25/// use signalrs_client::hub::Hub;
26///
27/// #[tokio::main]
28/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
29/// let hub = Hub::default().method("Send", print);
30///
31/// let client = SignalRClient::builder("localhost")
32/// .use_port(8080)
33/// .use_hub("echo")
34/// .with_client_hub(hub)
35/// .build()
36/// .await?;
37/// # Ok(())
38/// }
39///
40/// // Hub methods need to be async
41/// async fn print(message: String) {
42/// println!("{message}");
43/// }
44/// ```
45#[derive(Default)]
46pub struct Hub {
47 methods: HashMap<String, Box<dyn HubMethod + Send + Sync + 'static>>,
48}
49
50impl Hub {
51 /// Embeds a new method in a hub under `name`.
52 ///
53 /// Any server calls to a method of `name` will be routed to function pointed to by `method`.
54 /// Only functions returning `()` and `async` are allowed.
55 /// Up to 13 arguments are supported. They will be extracted from server call.
56 /// In case extraction fails, error will be logged.
57 ///
58 /// All primitive arguments should be usable out of the box. In case custom one needs to be used see [`HubArgument`](signalrs_derive::HubArgument).
59 /// Value-like hub arguments need to implement [`Deserialize`](serde::Deserialize) as well.
60 ///
61 /// # Example
62 /// ```rust,no_run
63 /// use serde::Deserialize;
64 /// use signalrs_derive::HubArgument;
65 /// # use signalrs_client::hub::Hub;
66 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
67 /// # let hub = Hub::default().method("Send", print);
68 /// # Ok(())
69 /// # }
70 /// # async fn print(_message: Data) {
71 /// # // do nothing
72 /// # }
73 ///
74 /// #[derive(Deserialize, HubArgument)]
75 /// struct Data {
76 /// f1: i32,
77 /// f2: String
78 /// }
79 /// ```
80 pub fn method<M, Args>(mut self, name: impl ToString, method: M) -> Self
81 where
82 M: Handler<Args> + Send + Sync + Clone + 'static,
83 Args: Send + Sync + 'static,
84 {
85 if self
86 .methods
87 .insert(name.to_string(), Box::new(HandlerWrapper::<M, Args>::from(method)))
88 .is_some()
89 {
90 warn!("overwritten method {}", name.to_string())
91 }
92
93 self
94 }
95
96 pub(crate) fn call(&self, message: ClientMessage) -> Result<(), HubError> {
97 let RoutingData {
98 message_type,
99 target,
100 } = message
101 .deserialize()
102 .map_err(|error| -> MalformedRequest { error.into() })?;
103
104 match message_type {
105 MessageType::Invocation => self.invocation(target, message),
106 x => self.unsupported(x),
107 }
108 }
109
110 fn invocation(&self, target: Option<String>, message: ClientMessage) -> Result<(), HubError> {
111 let target = target.ok_or_else(|| HubError::Unprocessable {
112 message: "Target of invocation missing in request".into(),
113 })?;
114
115 let method = self
116 .methods
117 .get(&target)
118 .ok_or_else(|| HubError::Unprocessable {
119 message: format!("target {} not found", target),
120 })?;
121
122 method.call(HubInvocation::new(message)?)
123 }
124
125 fn unsupported(&self, message_type: MessageType) -> Result<(), HubError> {
126 Err(HubError::Unsupported {
127 message: format!("{message_type} not supported by client-side hub"),
128 })
129 }
130}
131
132#[derive(Deserialize)]
133struct RoutingData {
134 #[serde(rename = "type")]
135 message_type: MessageType,
136 target: Option<String>,
137}