use crate::{ entities::{ doc::{Doc, Revision}, ws::{WsDataType, WsDocumentData}, }, errors::{internal_error, DocError}, services::{ doc::Document, ws::{WsDocumentHandler, WsDocumentSender}, }, sql_tables::doc::{OpTable, OpTableSql}, }; use bytes::Bytes; use flowy_database::ConnectionPool; use flowy_ot::core::Delta; use parking_lot::RwLock; use std::{ convert::TryInto, sync::{ atomic::{AtomicI64, Ordering::SeqCst}, Arc, }, }; pub(crate) struct EditDocContext { pub(crate) id: DocId, pub(crate) rev_counter: RevCounter, document: RwLock, ws: Arc, op_sql: Arc, } impl EditDocContext { pub(crate) fn new(doc: Doc, ws: Arc, op_sql: Arc) -> Result { let id: DocId = doc.id.into(); let rev_counter = RevCounter::new(doc.revision); let delta: Delta = doc.data.try_into()?; let document = RwLock::new(Document::from_delta(delta)); Ok(Self { id, rev_counter, document, ws, op_sql, }) } pub(crate) fn doc(&self) -> Doc { Doc { id: self.id.clone().into(), data: self.document.read().to_bytes(), revision: self.rev_counter.value(), } } pub(crate) fn apply_delta(&self, data: Bytes, pool: Arc) -> Result<(), DocError> { let mut guard = self.document.write(); let base_rev_id = self.rev_counter.value(); let rev_id = self.rev_counter.next(); let _ = guard.apply_delta(data.clone())?; let json = guard.to_json(); drop(guard); // Opti: it is necessary to save the rev if send success? let md5 = format!("{:x}", md5::compute(json)); let revision = Revision::new(base_rev_id, rev_id, data.to_vec(), md5, self.id.clone().into()); self.save_revision(revision.clone(), pool.clone()); match self.ws.send(revision.into()) { Ok(_) => { // TODO: remove the rev if send success // let _ = self.delete_revision(rev_id, pool)?; }, Err(e) => { log::error!("Send delta failed: {:?}", e); }, } Ok(()) } } impl EditDocContext { fn save_revision(&self, revision: Revision, pool: Arc) -> Result<(), DocError> { let conn = &*pool.get().map_err(internal_error)?; conn.immediate_transaction::<_, DocError, _>(|| { let op_table: OpTable = revision.into(); let _ = self.op_sql.create_op_table(op_table, conn)?; Ok(()) })?; Ok(()) } fn delete_revision(&self, rev_id: i64, pool: Arc) -> Result<(), DocError> { let conn = &*pool.get().map_err(internal_error)?; conn.immediate_transaction::<_, DocError, _>(|| { let _ = self.op_sql.delete_op_table(rev_id, conn)?; Ok(()) })?; Ok(()) } } impl WsDocumentHandler for EditDocContext { fn receive(&self, data: WsDocumentData) { match data.ty { WsDataType::Delta => {}, WsDataType::Command => {}, } } } #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct DocId(pub(crate) String); impl AsRef for DocId { fn as_ref(&self) -> &str { &self.0 } } impl std::convert::From for DocId where T: ToString, { fn from(s: T) -> Self { DocId(s.to_string()) } } impl std::convert::Into for DocId { fn into(self) -> String { self.0.clone() } } #[derive(Debug)] pub struct RevCounter(pub AtomicI64); impl RevCounter { pub fn new(n: i64) -> Self { Self(AtomicI64::new(n)) } pub fn next(&self) -> i64 { let _ = self.0.fetch_add(1, SeqCst); self.value() } pub fn value(&self) -> i64 { self.0.load(SeqCst) } }