perspective_server/server.rs
1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors. ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use std::collections::HashMap;
14use std::error::Error;
15use std::sync::Arc;
16
17use async_lock::RwLock;
18use futures::future::BoxFuture;
19use futures::Future;
20pub use perspective_client::Session;
21
22use crate::ffi;
23use crate::local_client::LocalClient;
24use crate::local_session::LocalSession;
25
26pub type ServerError = Box<dyn Error + Send + Sync>;
27
28type SessionCallback =
29 Arc<dyn for<'a> Fn(&'a [u8]) -> BoxFuture<'a, Result<(), ServerError>> + Send + Sync>;
30
31/// Use [`SessionHandler`] to implement a callback for messages emitted from
32/// a [`Session`], to be passed to the [`Server::new_session`] constructor.
33///
34/// Alternatively, a [`Session`] can be created from a closure instead via
35/// [`Server::new_session_with_callback`].
36pub trait SessionHandler: Send + Sync {
37 /// Dispatch a message from a [`Server`] for a the [`Session`] that took
38 /// this `SessionHandler` instance as a constructor argument.
39 fn send_response<'a>(
40 &'a mut self,
41 msg: &'a [u8],
42 ) -> impl Future<Output = Result<(), ServerError>> + Send + 'a;
43}
44
45/// An instance of a Perspective server. Each [`Server`] instance is separate,
46/// and does not share [`perspective_client::Table`] (or other) data with other
47/// [`Server`]s.
48#[derive(Clone)]
49pub struct Server {
50 pub(crate) server: Arc<ffi::Server>,
51 pub(crate) callbacks: Arc<RwLock<HashMap<u32, SessionCallback>>>,
52}
53
54impl std::fmt::Debug for Server {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 let addr = std::ptr::addr_of!(self);
57 write!(f, "Server {:?}", addr)?;
58 Ok(())
59 }
60}
61
62impl Default for Server {
63 fn default() -> Self {
64 let server = Arc::new(ffi::Server::new());
65 let callbacks = Arc::default();
66 Self { server, callbacks }
67 }
68}
69
70impl Server {
71 /// An alternative method for creating a new [`Session`] for this
72 /// [`Server`], from a callback closure instead of a via a trait.
73 /// See [`Server::new_session`] for details.
74 ///
75 /// # Arguments
76 ///
77 /// - `send_response` - A function invoked by the [`Server`] when a
78 /// response message needs to be sent to the
79 /// [`perspective_client::Client`].
80 pub async fn new_session_with_callback<F>(&self, send_response: F) -> LocalSession
81 where
82 F: for<'a> Fn(&'a [u8]) -> BoxFuture<'a, Result<(), ServerError>> + 'static + Sync + Send,
83 {
84 let id = self.server.new_session();
85 let server = self.clone();
86 self.callbacks
87 .write()
88 .await
89 .insert(id, Arc::new(send_response));
90
91 LocalSession {
92 id,
93 server,
94 closed: false,
95 }
96 }
97
98 /// Create a [`Session`] for this [`Server`], suitable for exactly one
99 /// [`perspective_client::Client`] (not necessarily in this process). A
100 /// [`Session`] represents the server-side state of a single
101 /// client-to-server connection.
102 ///
103 /// # Arguments
104 ///
105 /// - `session_handler` - An implementor of [`SessionHandler`] which will be
106 /// invoked by the [`Server`] when a response message needs to be sent to
107 /// the [`Client`]. The response itself should be passed to
108 /// [`Client::handle_response`] eventually, though it may-or-may-not be in
109 /// the same process.
110 pub async fn new_session<F>(&self, session_handler: F) -> LocalSession
111 where
112 F: SessionHandler + 'static + Sync + Send + Clone,
113 {
114 self.new_session_with_callback(move |msg| {
115 let mut session_handler = session_handler.clone();
116 Box::pin(async move { session_handler.send_response(msg).await })
117 })
118 .await
119 }
120
121 pub fn new_local_client(&self) -> LocalClient {
122 LocalClient::new(self)
123 }
124}