use crate::AwarenessRef;
use futures_util::stream::SplitSink;
use futures_util::{SinkExt, StreamExt};
use lib0::decoding::Cursor;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::spawn;
use tokio::sync::{Mutex, RwLock};
use tokio::task::JoinHandle;
use warp::ws::WebSocket;
use y_sync::awareness::Awareness;
use y_sync::sync::{DefaultProtocol, Error, Message, MessageReader, Protocol, SyncMessage};
use yrs::updates::decoder::{Decode, DecoderV1};
use yrs::updates::encoder::{Encode, Encoder, EncoderV1};
use yrs::Update;
pub struct WarpConn {
processing_loop: JoinHandle<Result<(), Error>>,
inbox: ConnInbox,
}
impl WarpConn {
pub fn new(awareness_ref: AwarenessRef, ws: WebSocket) -> Self {
Self::with_protocol(awareness_ref, ws, DefaultProtocol)
}
pub fn with_protocol<P>(awareness_ref: AwarenessRef, ws: WebSocket, protocol: P) -> Self
where
P: Protocol + Send + Sync + 'static,
{
let (sink, mut source) = ws.split();
let mut sink = Arc::new(Mutex::new(sink));
let inbox = ConnInbox(sink.clone());
let processing_loop: JoinHandle<Result<(), Error>> = spawn(async move {
let payload = {
let mut encoder = EncoderV1::new();
let awareness = awareness_ref.read().await;
protocol.start(&awareness, &mut encoder)?;
encoder.to_vec()
};
if !payload.is_empty() {
let mut s = sink.lock().await;
if let Err(e) = s.send(warp::ws::Message::binary(payload)).await {
return Err(e.to_err());
}
}
while let Some(input) = source.next().await {
match Self::process(&protocol, &awareness_ref, &mut sink, input).await {
Ok(()) => { }
Err(e) => {
return Err(e);
}
}
}
Ok(())
});
WarpConn {
processing_loop,
inbox,
}
}
pub fn inbox(&self) -> &ConnInbox {
&self.inbox
}
async fn process<P: Protocol>(
protocol: &P,
awareness: &AwarenessRef,
sink: &mut Arc<Mutex<SplitSink<WebSocket, warp::ws::Message>>>,
input: Result<warp::ws::Message, warp::Error>,
) -> Result<(), Error> {
match input {
Ok(input) => {
let mut decoder = DecoderV1::new(Cursor::new(input.as_bytes()));
let reader = MessageReader::new(&mut decoder);
for r in reader {
let msg = r?;
if let Some(reply) = handle_msg(protocol, &awareness, msg).await? {
let mut sender = sink.lock().await;
if let Err(e) = sender
.send(warp::ws::Message::binary(reply.encode_v1()))
.await
{
return Err(Error::Other(e.into()));
}
}
}
Ok(())
}
Err(e) => Err(Error::Other(e.into())),
}
}
}
impl core::future::Future for WarpConn {
type Output = Result<(), Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut self.processing_loop).poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(Err(e)) => Poll::Ready(Err(Error::Other(e.into()))),
Poll::Ready(Ok(r)) => Poll::Ready(r),
}
}
}
#[async_trait::async_trait]
pub trait Inbox {
type Item;
type Error: std::error::Error + Send + Sync;
async fn send(&mut self, item: Self::Item) -> Result<(), Self::Error>;
}
#[derive(Debug, Clone)]
pub struct ConnInbox(Arc<Mutex<SplitSink<WebSocket, warp::ws::Message>>>);
#[async_trait::async_trait]
impl Inbox for ConnInbox {
type Item = Vec<u8>;
type Error = Error;
async fn send(&mut self, item: Self::Item) -> Result<(), Self::Error> {
let mut g = self.0.lock().await;
g.send(warp::ws::Message::binary(item))
.await
.map_err(|e| e.to_err())?;
Ok(())
}
}
trait ToError {
fn to_err(self) -> Error;
}
impl ToError for warp::Error {
fn to_err(self) -> Error {
Error::Other(self.into())
}
}
async fn handle_msg<P: Protocol>(
protocol: &P,
a: &Arc<RwLock<Awareness>>,
msg: Message,
) -> Result<Option<Message>, Error> {
match msg {
Message::Sync(msg) => match msg {
SyncMessage::SyncStep1(sv) => {
let awareness = a.read().await;
protocol.handle_sync_step1(&awareness, sv)
}
SyncMessage::SyncStep2(update) => {
let mut awareness = a.write().await;
protocol.handle_sync_step2(&mut awareness, Update::decode_v1(&update)?)
}
SyncMessage::Update(update) => {
let mut awareness = a.write().await;
protocol.handle_update(&mut awareness, Update::decode_v1(&update)?)
}
},
Message::Auth(reason) => {
let awareness = a.read().await;
protocol.handle_auth(&awareness, reason)
}
Message::AwarenessQuery => {
let awareness = a.read().await;
protocol.handle_awareness_query(&awareness)
}
Message::Awareness(update) => {
let mut awareness = a.write().await;
protocol.handle_awareness_update(&mut awareness, update)
}
Message::Custom(tag, data) => {
let mut awareness = a.write().await;
protocol.missing_handle(&mut awareness, tag, data)
}
}
}