perspective_server/
local_client.rsuse std::ops::Deref;
use std::sync::{Arc, OnceLock};
use async_lock::{RwLock, RwLockReadGuard};
use perspective_client::*;
use crate::local_session::LocalSession;
use crate::server::{Server, ServerError, SessionHandler};
#[derive(Clone, Default)]
struct LocalClientState {
client: Arc<OnceLock<Client>>,
session: Arc<OnceLock<RwLock<Option<LocalSession>>>>,
server: Server,
}
impl SessionHandler for LocalClientState {
async fn send_response<'a>(&'a mut self, msg: &'a [u8]) -> Result<(), ServerError> {
self.get_client().handle_response(msg).await?;
Ok(())
}
}
impl ClientHandler for LocalClientState {
async fn send_request<'a>(
&'a self,
msg: &'a [u8],
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let session_lock = self.get_session().await;
let session = session_lock.as_ref().unwrap();
session.handle_request(msg).await?;
session.poll().await?;
Ok(())
}
}
impl LocalClientState {
fn get_client(&self) -> &Client {
self.client.get_or_init(|| Client::new(self.clone()))
}
async fn get_session(&self) -> RwLockReadGuard<'_, Option<LocalSession>> {
if self.session.get().is_none() {
let session = self.server.new_session(self.clone()).await;
self.session
.get_or_init(|| RwLock::new(Some(session)))
.read()
.await
} else {
self.session.get().unwrap().read().await
}
}
}
pub struct LocalClient(LocalClientState);
impl Deref for LocalClient {
type Target = Client;
fn deref(&self) -> &Self::Target {
self.0.get_client()
}
}
impl Drop for LocalClient {
fn drop(&mut self) {
if let Some(session) = self.0.session.get() {
if session.try_read().unwrap().is_some() {
tracing::error!("`Client` dropped without `Client::close`");
}
} else {
tracing::debug!("`Session` dropped before init");
}
}
}
impl LocalClient {
pub fn new(server: &Server) -> Self {
let state = LocalClientState {
server: server.clone(),
client: Arc::default(),
session: Arc::default(),
};
LocalClient(state)
}
pub async fn close(self) {
if let Some(session) = self.0.session.get() {
session.write().await.take().unwrap().close().await
} else {
tracing::debug!("`Session` dropped before init");
}
}
}