config document kv store

This commit is contained in:
appflowy 2021-12-22 18:53:52 +08:00
parent eb601932ea
commit cb4398eab0
29 changed files with 503 additions and 238 deletions

View File

@ -2,7 +2,5 @@
CREATE TABLE IF NOT EXISTS doc_table(
id uuid NOT NULL,
PRIMARY KEY (id),
-- data bytea NOT NULL DEFAULT '',
data TEXT NOT NULL DEFAULT '',
rev_id bigint NOT NULL DEFAULT 0
);

View File

@ -42,7 +42,7 @@ impl Application {
pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io::Error> {
let domain = domain();
let secret: String = secret();
actix_rt::spawn(period_check(app_ctx.pg_pool.clone()));
actix_rt::spawn(period_check(app_ctx.persistence.pg_pool()));
let server = HttpServer::new(move || {
App::new()
@ -54,16 +54,17 @@ pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io
.service(ws_scope())
.service(user_scope())
.app_data(app_ctx.ws_server.clone())
.app_data(app_ctx.pg_pool.clone())
.app_data(app_ctx.persistence.clone())
.app_data(Data::new(app_ctx.persistence.pg_pool()))
.app_data(app_ctx.ws_receivers.clone())
.app_data(app_ctx.document_mng.clone())
})
.listen(listener)?
.run();
Ok(server)
}
async fn period_check(_pool: Data<PgPool>) {
#[allow(dead_code)]
async fn period_check(_pool: PgPool) {
let mut i = interval(Duration::from_secs(60));
loop {
i.tick().await;

View File

@ -1,10 +1,11 @@
use crate::services::{
document::manager::DocumentManager,
kv_store::{KVStore, PostgresKV},
web_socket::{WSServer, WebSocketReceivers},
};
use actix::Addr;
use actix_web::web::Data;
use crate::services::document::{controller::make_document_ws_receiver, persistence::DocumentKVPersistence};
use lib_ws::WSModule;
use sqlx::PgPool;
use std::sync::Arc;
@ -12,30 +13,41 @@ use std::sync::Arc;
#[derive(Clone)]
pub struct AppContext {
pub ws_server: Data<Addr<WSServer>>,
pub pg_pool: Data<PgPool>,
pub persistence: Data<Arc<FlowyPersistence>>,
pub ws_receivers: Data<WebSocketReceivers>,
pub document_mng: Data<Arc<DocumentManager>>,
pub kv_store: Data<Arc<dyn KVStore>>,
}
impl AppContext {
pub fn new(ws_server: Addr<WSServer>, db_pool: PgPool) -> Self {
pub fn new(ws_server: Addr<WSServer>, pg_pool: PgPool) -> Self {
let ws_server = Data::new(ws_server);
let pg_pool = Data::new(db_pool);
let mut ws_receivers = WebSocketReceivers::new();
let document_mng = Arc::new(DocumentManager::new(pg_pool.clone()));
ws_receivers.set(WSModule::Doc, document_mng.clone());
let kv_store = Arc::new(PostgresKV {
pg_pool: pg_pool.clone(),
});
let kv_store = make_document_kv_store(pg_pool.clone());
let persistence = Arc::new(FlowyPersistence { pg_pool, kv_store });
let document_ws_receiver = make_document_ws_receiver(persistence.clone());
ws_receivers.set(WSModule::Doc, document_ws_receiver);
AppContext {
ws_server,
pg_pool,
persistence: Data::new(persistence),
ws_receivers: Data::new(ws_receivers),
document_mng: Data::new(document_mng),
kv_store: Data::new(kv_store),
}
}
}
fn make_document_kv_store(pg_pool: PgPool) -> Arc<DocumentKVPersistence> {
let kv_impl = Arc::new(PostgresKV { pg_pool });
Arc::new(DocumentKVPersistence::new(kv_impl))
}
#[derive(Clone)]
pub struct FlowyPersistence {
pg_pool: PgPool,
kv_store: Arc<DocumentKVPersistence>,
}
impl FlowyPersistence {
pub fn pg_pool(&self) -> PgPool { self.pg_pool.clone() }
pub fn kv_store(&self) -> Arc<DocumentKVPersistence> { self.kv_store.clone() }
}

View File

@ -1,20 +0,0 @@
use flowy_collaboration::protobuf::Doc;
pub(crate) const DOC_TABLE: &str = "doc_table";
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct DocTable {
pub(crate) id: uuid::Uuid,
pub(crate) data: String,
pub(crate) rev_id: i64,
}
impl std::convert::From<DocTable> for Doc {
fn from(table: DocTable) -> Self {
let mut doc = Doc::new();
doc.set_id(table.id.to_string());
doc.set_data(table.data);
doc.set_rev_id(table.rev_id);
doc
}
}

View File

@ -1,4 +1,3 @@
pub mod doc;
pub mod logged_user;
pub mod token;
pub mod user;

View File

@ -1,8 +1,9 @@
use crate::services::document::{create_doc_with_transaction, delete_doc};
use crate::{
entities::logged_user::LoggedUser,
services::core::{trash::read_trash_ids, view::persistence::*},
services::{
core::{trash::read_trash_ids, view::persistence::*},
document::persistence::{create_doc_with_transaction, delete_doc},
},
util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use backend_service::errors::{invalid_params, ServerError};

View File

@ -1,9 +1,6 @@
use crate::{
entities::logged_user::LoggedUser,
services::{
core::view::{create_view, delete_view, persistence::check_view_ids, read_view, update_view},
document::manager::DocumentManager,
},
services::core::view::{create_view, delete_view, persistence::check_view_ids, read_view, update_view},
util::serde_ext::parse_from_payload,
};
use actix_web::{
@ -20,13 +17,8 @@ use flowy_core_data_model::{
protobuf::{CreateViewParams, QueryViewRequest, UpdateViewParams, ViewIdentifier},
};
use sqlx::PgPool;
use std::sync::Arc;
pub async fn create_handler(
payload: Payload,
pool: Data<PgPool>,
_doc_biz: Data<Arc<DocumentManager>>,
) -> Result<HttpResponse, ServerError> {
pub async fn create_handler(payload: Payload, pool: Data<PgPool>) -> Result<HttpResponse, ServerError> {
let params: CreateViewParams = parse_from_payload(payload).await?;
let mut transaction = pool
.begin()

View File

@ -1,46 +1,53 @@
use crate::services::{
document::{
create_doc,
read_doc,
update_doc,
persistence::{create_doc, read_doc, update_doc},
ws_actor::{DocumentWebSocketActor, WSActorMessage},
},
web_socket::{WSClientData, WebSocketReceiver},
};
use actix_web::web::Data;
use backend_service::errors::ServerError;
use crate::context::FlowyPersistence;
use flowy_collaboration::{
core::sync::{ServerDocManager, ServerDocPersistence},
core::sync::{DocumentPersistence, ServerDocumentManager},
entities::doc::Doc,
errors::CollaborateError,
protobuf::{CreateDocParams, DocIdentifier, UpdateDocParams},
};
use lib_infra::future::FutureResultSend;
use lib_ot::{revision::Revision, rich_text::RichTextDelta};
use sqlx::PgPool;
use std::{convert::TryInto, sync::Arc};
use tokio::sync::{mpsc, oneshot};
pub struct DocumentManager {
ws_sender: mpsc::Sender<WSActorMessage>,
pg_pool: Data<PgPool>,
pub fn make_document_ws_receiver(persistence: Arc<FlowyPersistence>) -> Arc<DocumentWebSocketReceiver> {
let document_persistence = Arc::new(DocumentPersistenceImpl(persistence.clone()));
let document_manager = Arc::new(ServerDocumentManager::new(document_persistence));
let (ws_sender, rx) = tokio::sync::mpsc::channel(100);
let actor = DocumentWebSocketActor::new(rx, document_manager);
tokio::task::spawn(actor.run());
Arc::new(DocumentWebSocketReceiver::new(persistence, ws_sender))
}
impl DocumentManager {
pub fn new(pg_pool: Data<PgPool>) -> Self {
let inner = Arc::new(ServerDocManager::new(Arc::new(DocPersistenceImpl(pg_pool.clone()))));
let (ws_sender, rx) = mpsc::channel(100);
let actor = DocumentWebSocketActor::new(rx, inner);
tokio::task::spawn(actor.run());
Self { ws_sender, pg_pool }
pub struct DocumentWebSocketReceiver {
ws_sender: mpsc::Sender<WSActorMessage>,
persistence: Arc<FlowyPersistence>,
}
impl DocumentWebSocketReceiver {
pub fn new(persistence: Arc<FlowyPersistence>, ws_sender: mpsc::Sender<WSActorMessage>) -> Self {
Self { ws_sender, persistence }
}
}
impl WebSocketReceiver for DocumentManager {
impl WebSocketReceiver for DocumentWebSocketReceiver {
fn receive(&self, data: WSClientData) {
let (ret, rx) = oneshot::channel();
let sender = self.ws_sender.clone();
let pool = self.pg_pool.clone();
let pool = self.persistence.pg_pool();
actix_rt::spawn(async move {
let msg = WSActorMessage::ClientData {
@ -60,10 +67,10 @@ impl WebSocketReceiver for DocumentManager {
}
}
struct DocPersistenceImpl(Data<PgPool>);
impl ServerDocPersistence for DocPersistenceImpl {
struct DocumentPersistenceImpl(Arc<FlowyPersistence>);
impl DocumentPersistence for DocumentPersistenceImpl {
fn update_doc(&self, doc_id: &str, rev_id: i64, delta: RichTextDelta) -> FutureResultSend<(), CollaborateError> {
let pg_pool = self.0.clone();
let pg_pool = self.0.pg_pool();
let mut params = UpdateDocParams::new();
let doc_json = delta.to_json();
params.set_doc_id(doc_id.to_string());
@ -71,7 +78,7 @@ impl ServerDocPersistence for DocPersistenceImpl {
params.set_rev_id(rev_id);
FutureResultSend::new(async move {
let _ = update_doc(pg_pool.get_ref(), params)
let _ = update_doc(&pg_pool, params)
.await
.map_err(server_error_to_collaborate_error)?;
Ok(())
@ -83,9 +90,9 @@ impl ServerDocPersistence for DocPersistenceImpl {
doc_id: doc_id.to_string(),
..Default::default()
};
let pg_pool = self.0.clone();
let pg_pool = self.0.pg_pool();
FutureResultSend::new(async move {
let mut pb_doc = read_doc(pg_pool.get_ref(), params)
let mut pb_doc = read_doc(&pg_pool, params)
.await
.map_err(server_error_to_collaborate_error)?;
let doc = (&mut pb_doc)
@ -96,7 +103,7 @@ impl ServerDocPersistence for DocPersistenceImpl {
}
fn create_doc(&self, revision: Revision) -> FutureResultSend<Doc, CollaborateError> {
let pg_pool = self.0.clone();
let pg_pool = self.0.pg_pool();
FutureResultSend::new(async move {
let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
let doc_json = delta.to_json();
@ -108,7 +115,7 @@ impl ServerDocPersistence for DocPersistenceImpl {
cached_size: Default::default(),
};
let _ = create_doc(pg_pool.get_ref(), params)
let _ = create_doc(&pg_pool, params)
.await
.map_err(server_error_to_collaborate_error)?;
let doc: Doc = revision.try_into()?;

View File

@ -1,9 +1,6 @@
#![allow(clippy::module_inception)]
pub(crate) use crud::*;
pub use router::*;
pub mod crud;
pub mod manager;
pub mod router;
mod ws_actor;
pub(crate) mod controller;
pub(crate) mod persistence;
pub(crate) mod router;
pub(crate) mod ws_actor;

View File

@ -0,0 +1,48 @@
use crate::{services::kv_store::KVStore, util::serde_ext::parse_from_bytes};
use backend_service::errors::ServerError;
use bytes::Bytes;
use lib_ot::protobuf::{RepeatedRevision, Revision};
use protobuf::Message;
use std::sync::Arc;
pub struct DocumentKVPersistence {
inner: Arc<dyn KVStore>,
}
impl std::ops::Deref for DocumentKVPersistence {
type Target = Arc<dyn KVStore>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl std::ops::DerefMut for DocumentKVPersistence {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
impl DocumentKVPersistence {
pub(crate) fn new(kv_store: Arc<dyn KVStore>) -> Self { DocumentKVPersistence { inner: kv_store } }
pub(crate) async fn set_revision(&self, revision: Revision) -> Result<(), ServerError> {
let key = revision.rev_id.to_string();
let bytes = revision.write_to_bytes()?;
let _ = self.inner.set(&key, Bytes::from(bytes)).await?;
Ok(())
}
pub(crate) async fn batch_get_revisions(&self, rev_ids: Vec<i64>) -> Result<RepeatedRevision, ServerError> {
let keys = rev_ids
.into_iter()
.map(|rev_id| rev_id.to_string())
.collect::<Vec<String>>();
let items = self.inner.batch_get(keys).await?;
let revisions = items
.into_iter()
.filter_map(|kv| parse_from_bytes::<Revision>(&kv.value).ok())
.collect::<Vec<Revision>>();
let mut repeated_revision = RepeatedRevision::new();
repeated_revision.set_items(revisions.into());
Ok(repeated_revision)
}
}

View File

@ -0,0 +1,5 @@
mod kv;
mod postgres;
pub use kv::*;
pub use postgres::*;

View File

@ -1,20 +1,29 @@
use crate::{
entities::doc::{DocTable, DOC_TABLE},
services::kv_store::KVStore,
util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use anyhow::Context;
use backend_service::errors::ServerError;
use flowy_collaboration::protobuf::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams};
use protobuf::Message;
use sqlx::{postgres::PgArguments, PgPool, Postgres};
use uuid::Uuid;
const DOC_TABLE: &str = "doc_table";
#[tracing::instrument(level = "debug", skip(transaction), err)]
pub(crate) async fn create_doc_with_transaction(
transaction: &mut DBTransaction<'_>,
params: CreateDocParams,
// kv_store: Data<Arc<dyn KVStore>>,
) -> Result<(), ServerError> {
let uuid = Uuid::parse_str(&params.id)?;
let (sql, args) = NewDocSqlBuilder::new(uuid).data(params.data).build()?;
let (sql, args) = SqlBuilder::create(DOC_TABLE)
.add_field_with_arg("id", uuid)
.add_field_with_arg("rev_id", 0)
.build()?;
// TODO kv
let _ = sqlx::query_with(&sql, args)
.execute(transaction)
.await
@ -51,18 +60,20 @@ pub(crate) async fn read_doc(pool: &PgPool, params: DocIdentifier) -> Result<Doc
let (sql, args) = builder.build()?;
// TODO: benchmark the speed of different documents with different size
let doc: Doc = sqlx::query_as_with::<Postgres, DocTable, PgArguments>(&sql, args)
let _table = sqlx::query_as_with::<Postgres, DocTable, PgArguments>(&sql, args)
.fetch_one(&mut transaction)
.await
.map_err(map_sqlx_error)?
.into();
.map_err(map_sqlx_error)?;
transaction
.commit()
.await
.context("Failed to commit SQL transaction to read document.")?;
// TODO: kv
panic!("")
Ok(doc)
// transaction
// .commit()
// .await
// .context("Failed to commit SQL transaction to read document.")?;
//
// Ok(doc)
}
#[tracing::instrument(level = "debug", skip(pool, params), fields(delta), err)]
@ -107,32 +118,17 @@ pub(crate) async fn delete_doc(transaction: &mut DBTransaction<'_>, doc_id: Uuid
Ok(())
}
pub struct NewDocSqlBuilder {
table: DocTable,
#[derive(Debug, Clone, sqlx::FromRow)]
struct DocTable {
id: uuid::Uuid,
rev_id: i64,
}
impl NewDocSqlBuilder {
pub fn new(id: Uuid) -> Self {
let table = DocTable {
id,
data: "".to_owned(),
rev_id: 0,
};
Self { table }
}
pub fn data(mut self, data: String) -> Self {
self.table.data = data;
self
}
pub fn build(self) -> Result<(String, PgArguments), ServerError> {
let (sql, args) = SqlBuilder::create(DOC_TABLE)
.add_field_with_arg("id", self.table.id)
.add_field_with_arg("data", self.table.data)
.add_field_with_arg("rev_id", self.table.rev_id)
.build()?;
Ok((sql, args))
}
}
// impl std::convert::From<DocTable> for Doc {
// fn from(table: DocTable) -> Self {
// let mut doc = Doc::new();
// doc.set_id(table.id.to_string());
// doc.set_rev_id(table.rev_id);
// doc
// }
// }

View File

@ -1,10 +1,11 @@
use crate::services::document::{create_doc, read_doc, update_doc};
use crate::{
services::document::persistence::{create_doc, read_doc, update_doc},
util::serde_ext::parse_from_payload,
};
use actix_web::{
web::{Data, Payload},
HttpResponse,
};
use crate::util::serde_ext::parse_from_payload;
use backend_service::{errors::ServerError, response::FlowyResponse};
use flowy_collaboration::protobuf::{CreateDocParams, DocIdentifier, UpdateDocParams};
use sqlx::PgPool;

View File

@ -1,16 +1,16 @@
use crate::{
services::{
document::update_doc,
document::persistence::update_doc,
web_socket::{entities::Socket, WSClientData, WSMessageAdaptor, WSUser},
},
util::serde_ext::{md5, parse_from_bytes},
};
use actix_rt::task::spawn_blocking;
use actix_web::web::Data;
use async_stream::stream;
use backend_service::errors::{internal_error, Result, ServerError};
use flowy_collaboration::{
core::sync::{RevisionUser, ServerDocManager, SyncResponse},
core::sync::{RevisionUser, ServerDocumentManager, SyncResponse},
protobuf::{DocumentWSData, DocumentWSDataType, NewDocumentUser, UpdateDocParams},
};
use futures::stream::StreamExt;
@ -22,18 +22,18 @@ use tokio::sync::{mpsc, oneshot};
pub enum WSActorMessage {
ClientData {
client_data: WSClientData,
pool: Data<PgPool>,
pool: PgPool,
ret: oneshot::Sender<Result<()>>,
},
}
pub struct DocumentWebSocketActor {
receiver: Option<mpsc::Receiver<WSActorMessage>>,
doc_manager: Arc<ServerDocManager>,
doc_manager: Arc<ServerDocumentManager>,
}
impl DocumentWebSocketActor {
pub fn new(receiver: mpsc::Receiver<WSActorMessage>, manager: Arc<ServerDocManager>) -> Self {
pub fn new(receiver: mpsc::Receiver<WSActorMessage>, manager: Arc<ServerDocumentManager>) -> Self {
Self {
receiver: Some(receiver),
doc_manager: manager,
@ -66,7 +66,7 @@ impl DocumentWebSocketActor {
}
}
async fn handle_client_data(&self, client_data: WSClientData, pg_pool: Data<PgPool>) -> Result<()> {
async fn handle_client_data(&self, client_data: WSClientData, pg_pool: PgPool) -> Result<()> {
let WSClientData { user, socket, data } = client_data;
let document_data = spawn_blocking(move || {
let document_data: DocumentWSData = parse_from_bytes(&data)?;
@ -151,7 +151,7 @@ fn verify_md5(revision: &Revision) -> Result<()> {
pub struct ServerDocUser {
pub user: Arc<WSUser>,
pub(crate) socket: Socket,
pub pg_pool: Data<PgPool>,
pub pg_pool: PgPool,
}
impl RevisionUser for ServerDocUser {
@ -182,7 +182,7 @@ impl RevisionUser for ServerDocUser {
params.set_doc_id(doc_id);
params.set_data(doc_json);
params.set_rev_id(rev_id);
match update_doc(pg_pool.get_ref(), params).await {
match update_doc(&pg_pool, params).await {
Ok(_) => {},
Err(e) => log::error!("{}", e),
}

View File

@ -2,7 +2,7 @@ use crate::{
services::kv_store::{KVStore, KeyValue},
util::sqlx_ext::{map_sqlx_error, SqlBuilder},
};
use actix_web::web::Data;
use anyhow::Context;
use backend_service::errors::ServerError;
use bytes::Bytes;
@ -13,7 +13,7 @@ use sqlx::{postgres::PgArguments, Error, PgPool, Postgres, Row};
const KV_TABLE: &str = "kv_table";
pub(crate) struct PostgresKV {
pub(crate) pg_pool: Data<PgPool>,
pub(crate) pg_pool: PgPool,
}
impl KVStore for PostgresKV {

View File

@ -29,7 +29,9 @@ impl std::default::Default for WebSocketReceivers {
impl WebSocketReceivers {
pub fn new() -> Self { WebSocketReceivers::default() }
pub fn set(&mut self, source: WSModule, handler: Arc<dyn WebSocketReceiver>) { self.inner.insert(source, handler); }
pub fn set(&mut self, source: WSModule, receiver: Arc<dyn WebSocketReceiver>) {
self.inner.insert(source, receiver);
}
pub fn get(&self, source: &WSModule) -> Option<Arc<dyn WebSocketReceiver>> { self.inner.get(source).cloned() }
}

View File

@ -5,7 +5,7 @@ use std::str;
#[actix_rt::test]
async fn kv_set_test() {
let server = spawn_server().await;
let kv = server.app_ctx.kv_store.clone();
let kv = server.app_ctx.persistence.kv_store();
let s1 = "123".to_string();
let key = "1";
@ -18,7 +18,7 @@ async fn kv_set_test() {
#[actix_rt::test]
async fn kv_delete_test() {
let server = spawn_server().await;
let kv = server.app_ctx.kv_store.clone();
let kv = server.app_ctx.persistence.kv_store();
let s1 = "123".to_string();
let key = "1";
@ -30,7 +30,7 @@ async fn kv_delete_test() {
#[actix_rt::test]
async fn kv_batch_set_test() {
let server = spawn_server().await;
let kv = server.app_ctx.kv_store.clone();
let kv = server.app_ctx.persistence.kv_store();
let kvs = vec![
KeyValue {
key: "1".to_string(),

View File

@ -145,6 +145,47 @@ class Revision extends $pb.GeneratedMessage {
void clearUserId() => clearField(7);
}
class RepeatedRevision extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedRevision', createEmptyInstance: create)
..pc<Revision>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: Revision.create)
..hasRequiredFields = false
;
RepeatedRevision._() : super();
factory RepeatedRevision({
$core.Iterable<Revision>? items,
}) {
final _result = create();
if (items != null) {
_result.items.addAll(items);
}
return _result;
}
factory RepeatedRevision.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory RepeatedRevision.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
RepeatedRevision clone() => RepeatedRevision()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
RepeatedRevision copyWith(void Function(RepeatedRevision) updates) => super.copyWith((message) => updates(message as RepeatedRevision)) as RepeatedRevision; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static RepeatedRevision create() => RepeatedRevision._();
RepeatedRevision createEmptyInstance() => create();
static $pb.PbList<RepeatedRevision> createRepeated() => $pb.PbList<RepeatedRevision>();
@$core.pragma('dart2js:noInline')
static RepeatedRevision getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RepeatedRevision>(create);
static RepeatedRevision? _defaultInstance;
@$pb.TagNumber(1)
$core.List<Revision> get items => $_getList(0);
}
class RevId extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RevId', createEmptyInstance: create)
..aInt64(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'value')

View File

@ -26,11 +26,11 @@ class RevType extends $pb.ProtobufEnum {
class RevState extends $pb.ProtobufEnum {
static const RevState StateLocal = RevState._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'StateLocal');
static const RevState Acked = RevState._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Acked');
static const RevState Ack = RevState._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Ack');
static const $core.List<RevState> values = <RevState> [
StateLocal,
Acked,
Ack,
];
static final $core.Map<$core.int, RevState> _byValue = $pb.ProtobufEnum.initByValue(values);

View File

@ -24,12 +24,12 @@ const RevState$json = const {
'1': 'RevState',
'2': const [
const {'1': 'StateLocal', '2': 0},
const {'1': 'Acked', '2': 1},
const {'1': 'Ack', '2': 1},
],
};
/// Descriptor for `RevState`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List revStateDescriptor = $convert.base64Decode('CghSZXZTdGF0ZRIOCgpTdGF0ZUxvY2FsEAASCQoFQWNrZWQQAQ==');
final $typed_data.Uint8List revStateDescriptor = $convert.base64Decode('CghSZXZTdGF0ZRIOCgpTdGF0ZUxvY2FsEAASBwoDQWNrEAE=');
@$core.Deprecated('Use revisionDescriptor instead')
const Revision$json = const {
'1': 'Revision',
@ -46,6 +46,16 @@ const Revision$json = const {
/// Descriptor for `Revision`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List revisionDescriptor = $convert.base64Decode('CghSZXZpc2lvbhIeCgtiYXNlX3Jldl9pZBgBIAEoA1IJYmFzZVJldklkEhUKBnJldl9pZBgCIAEoA1IFcmV2SWQSHQoKZGVsdGFfZGF0YRgDIAEoDFIJZGVsdGFEYXRhEhAKA21kNRgEIAEoCVIDbWQ1EhUKBmRvY19pZBgFIAEoCVIFZG9jSWQSGAoCdHkYBiABKA4yCC5SZXZUeXBlUgJ0eRIXCgd1c2VyX2lkGAcgASgJUgZ1c2VySWQ=');
@$core.Deprecated('Use repeatedRevisionDescriptor instead')
const RepeatedRevision$json = const {
'1': 'RepeatedRevision',
'2': const [
const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.Revision', '10': 'items'},
],
};
/// Descriptor for `RepeatedRevision`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List repeatedRevisionDescriptor = $convert.base64Decode('ChBSZXBlYXRlZFJldmlzaW9uEh8KBWl0ZW1zGAEgAygLMgkuUmV2aXNpb25SBWl0ZW1z');
@$core.Deprecated('Use revIdDescriptor instead')
const RevId$json = const {
'1': 'RevId',

View File

@ -72,7 +72,7 @@ impl RevisionCache {
let rev_id = revision.rev_id;
let record = RevisionRecord {
revision,
state: RevState::Acked,
state: RevState::Ack,
};
self.memory_cache.add_revision(&record).await;
let _ = self.latest_rev_id.fetch_update(SeqCst, SeqCst, |_e| Some(rev_id));
@ -170,7 +170,7 @@ pub struct RevisionRecord {
}
impl RevisionRecord {
pub fn ack(&mut self) { self.state = RevState::Acked; }
pub fn ack(&mut self) { self.state = RevState::Ack; }
}
struct RevisionSyncSeq {

View File

@ -145,7 +145,7 @@ impl RevisionLoader {
Ok(_) => {},
Err(e) => tracing::error!("{}", e),
},
RevState::Acked => {},
RevState::Ack => {},
}
}
revisions = records.into_iter().map(|record| record.revision).collect::<_>();

View File

@ -50,7 +50,7 @@ impl std::convert::From<RevTableState> for RevState {
fn from(s: RevTableState) -> Self {
match s {
RevTableState::Local => RevState::StateLocal,
RevTableState::Acked => RevState::Acked,
RevTableState::Acked => RevState::Ack,
}
}
}
@ -59,7 +59,7 @@ impl std::convert::From<RevState> for RevTableState {
fn from(s: RevState) -> Self {
match s {
RevState::StateLocal => RevTableState::Local,
RevState::Acked => RevTableState::Acked,
RevState::Ack => RevTableState::Acked,
}
}
}

View File

@ -2,7 +2,7 @@ use crate::services::ws::{FlowyError, FlowyWebSocket, FlowyWsSender, WSConnectSt
use bytes::Bytes;
use dashmap::DashMap;
use flowy_collaboration::{
core::sync::{RevisionUser, ServerDocManager, ServerDocPersistence, SyncResponse},
core::sync::{DocumentPersistence, RevisionUser, ServerDocumentManager, SyncResponse},
entities::{
doc::Doc,
ws::{DocumentWSData, DocumentWSDataBuilder, DocumentWSDataType, NewDocumentUser},
@ -96,13 +96,13 @@ lazy_static! {
}
struct MockDocServer {
pub manager: Arc<ServerDocManager>,
pub manager: Arc<ServerDocumentManager>,
}
impl std::default::Default for MockDocServer {
fn default() -> Self {
let persistence = Arc::new(MockDocServerPersistence::default());
let manager = Arc::new(ServerDocManager::new(persistence));
let manager = Arc::new(ServerDocumentManager::new(persistence));
MockDocServer { manager }
}
}
@ -160,7 +160,7 @@ impl std::default::Default for MockDocServerPersistence {
}
}
impl ServerDocPersistence for MockDocServerPersistence {
impl DocumentPersistence for MockDocServerPersistence {
fn update_doc(&self, _doc_id: &str, _rev_id: i64, _delta: RichTextDelta) -> FutureResultSend<(), CollaborateError> {
unimplemented!()
}

View File

@ -17,40 +17,19 @@ use tokio::{
task::spawn_blocking,
};
pub trait ServerDocPersistence: Send + Sync {
pub trait DocumentPersistence: Send + Sync {
fn update_doc(&self, doc_id: &str, rev_id: i64, delta: RichTextDelta) -> FutureResultSend<(), CollaborateError>;
fn read_doc(&self, doc_id: &str) -> FutureResultSend<Doc, CollaborateError>;
fn create_doc(&self, revision: Revision) -> FutureResultSend<Doc, CollaborateError>;
}
#[rustfmt::skip]
// ┌─────────────────┐
// │ServerDocManager │
// └─────────────────┘
// │ 1
// ▼ n
// ┌───────────────┐
// │ OpenDocHandle │
// └───────────────┘
// │
// ▼
// ┌──────────────────┐
// │ DocCommandQueue │
// └──────────────────┘
// │ ┌──────────────────────┐ ┌────────────┐
// ▼ ┌────▶│ RevisionSynchronizer │────▶│ Document │
// ┌────────────────┐ │ └──────────────────────┘ └────────────┘
// │ServerDocEditor │─────┤
// └────────────────┘ │ ┌────────┐ ┌────────────┐
// └────▶│ Users │◆──────│RevisionUser│
// └────────┘ └────────────┘
pub struct ServerDocManager {
pub struct ServerDocumentManager {
open_doc_map: DashMap<String, Arc<OpenDocHandle>>,
persistence: Arc<dyn ServerDocPersistence>,
persistence: Arc<dyn DocumentPersistence>,
}
impl ServerDocManager {
pub fn new(persistence: Arc<dyn ServerDocPersistence>) -> Self {
impl ServerDocumentManager {
pub fn new(persistence: Arc<dyn DocumentPersistence>) -> Self {
Self {
open_doc_map: DashMap::new(),
persistence,

View File

@ -75,6 +75,7 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
| "WSError"
| "WSMessage"
| "Revision"
| "RepeatedRevision"
| "RevId"
| "RevisionRange"
=> TypeCategory::Protobuf,

View File

@ -409,6 +409,172 @@ impl ::protobuf::reflect::ProtobufValue for Revision {
}
}
#[derive(PartialEq,Clone,Default)]
pub struct RepeatedRevision {
// message fields
pub items: ::protobuf::RepeatedField<Revision>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a RepeatedRevision {
fn default() -> &'a RepeatedRevision {
<RepeatedRevision as ::protobuf::Message>::default_instance()
}
}
impl RepeatedRevision {
pub fn new() -> RepeatedRevision {
::std::default::Default::default()
}
// repeated .Revision items = 1;
pub fn get_items(&self) -> &[Revision] {
&self.items
}
pub fn clear_items(&mut self) {
self.items.clear();
}
// Param is passed by value, moved
pub fn set_items(&mut self, v: ::protobuf::RepeatedField<Revision>) {
self.items = v;
}
// Mutable pointer to the field.
pub fn mut_items(&mut self) -> &mut ::protobuf::RepeatedField<Revision> {
&mut self.items
}
// Take field
pub fn take_items(&mut self) -> ::protobuf::RepeatedField<Revision> {
::std::mem::replace(&mut self.items, ::protobuf::RepeatedField::new())
}
}
impl ::protobuf::Message for RepeatedRevision {
fn is_initialized(&self) -> bool {
for v in &self.items {
if !v.is_initialized() {
return false;
}
};
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.items)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
for value in &self.items {
let len = value.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
};
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
for v in &self.items {
os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_raw_varint32(v.get_cached_size())?;
v.write_to_with_cached_sizes(os)?;
};
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> RepeatedRevision {
RepeatedRevision::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<Revision>>(
"items",
|m: &RepeatedRevision| { &m.items },
|m: &mut RepeatedRevision| { &mut m.items },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<RepeatedRevision>(
"RepeatedRevision",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static RepeatedRevision {
static instance: ::protobuf::rt::LazyV2<RepeatedRevision> = ::protobuf::rt::LazyV2::INIT;
instance.get(RepeatedRevision::new)
}
}
impl ::protobuf::Clear for RepeatedRevision {
fn clear(&mut self) {
self.items.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for RepeatedRevision {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for RepeatedRevision {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct RevId {
// message fields
@ -843,7 +1009,7 @@ impl ::protobuf::reflect::ProtobufValue for RevType {
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum RevState {
StateLocal = 0,
Acked = 1,
Ack = 1,
}
impl ::protobuf::ProtobufEnum for RevState {
@ -854,7 +1020,7 @@ impl ::protobuf::ProtobufEnum for RevState {
fn from_i32(value: i32) -> ::std::option::Option<RevState> {
match value {
0 => ::std::option::Option::Some(RevState::StateLocal),
1 => ::std::option::Option::Some(RevState::Acked),
1 => ::std::option::Option::Some(RevState::Ack),
_ => ::std::option::Option::None
}
}
@ -862,7 +1028,7 @@ impl ::protobuf::ProtobufEnum for RevState {
fn values() -> &'static [Self] {
static values: &'static [RevState] = &[
RevState::StateLocal,
RevState::Acked,
RevState::Ack,
];
values
}
@ -896,57 +1062,63 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x05revId\x12\x1d\n\ndelta_data\x18\x03\x20\x01(\x0cR\tdeltaData\x12\x10\
\n\x03md5\x18\x04\x20\x01(\tR\x03md5\x12\x15\n\x06doc_id\x18\x05\x20\x01\
(\tR\x05docId\x12\x18\n\x02ty\x18\x06\x20\x01(\x0e2\x08.RevTypeR\x02ty\
\x12\x17\n\x07user_id\x18\x07\x20\x01(\tR\x06userId\"\x1d\n\x05RevId\x12\
\x14\n\x05value\x18\x01\x20\x01(\x03R\x05value\"N\n\rRevisionRange\x12\
\x15\n\x06doc_id\x18\x01\x20\x01(\tR\x05docId\x12\x14\n\x05start\x18\x02\
\x20\x01(\x03R\x05start\x12\x10\n\x03end\x18\x03\x20\x01(\x03R\x03end*\
\x20\n\x07RevType\x12\t\n\x05Local\x10\0\x12\n\n\x06Remote\x10\x01*%\n\
\x08RevState\x12\x0e\n\nStateLocal\x10\0\x12\t\n\x05Acked\x10\x01J\x8b\
\x07\n\x06\x12\x04\0\0\x1a\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
\x04\0\x12\x04\x02\0\n\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x10\n\x0b\
\n\x04\x04\0\x02\0\x12\x03\x03\x04\x1a\n\x0c\n\x05\x04\0\x02\0\x05\x12\
\x03\x03\x04\t\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\n\x15\n\x0c\n\x05\
\x04\0\x02\0\x03\x12\x03\x03\x18\x19\n\x0b\n\x04\x04\0\x02\x01\x12\x03\
\x04\x04\x15\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\t\n\x0c\n\x05\
\x04\0\x02\x01\x01\x12\x03\x04\n\x10\n\x0c\n\x05\x04\0\x02\x01\x03\x12\
\x03\x04\x13\x14\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x19\n\x0c\n\
\x05\x04\0\x02\x02\x05\x12\x03\x05\x04\t\n\x0c\n\x05\x04\0\x02\x02\x01\
\x12\x03\x05\n\x14\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x17\x18\n\
\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x04\x13\n\x0c\n\x05\x04\0\x02\x03\
\x05\x12\x03\x06\x04\n\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06\x0b\x0e\
\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x06\x11\x12\n\x0b\n\x04\x04\0\x02\
\x04\x12\x03\x07\x04\x16\n\x0c\n\x05\x04\0\x02\x04\x05\x12\x03\x07\x04\n\
\n\x0c\n\x05\x04\0\x02\x04\x01\x12\x03\x07\x0b\x11\n\x0c\n\x05\x04\0\x02\
\x04\x03\x12\x03\x07\x14\x15\n\x0b\n\x04\x04\0\x02\x05\x12\x03\x08\x04\
\x13\n\x0c\n\x05\x04\0\x02\x05\x06\x12\x03\x08\x04\x0b\n\x0c\n\x05\x04\0\
\x02\x05\x01\x12\x03\x08\x0c\x0e\n\x0c\n\x05\x04\0\x02\x05\x03\x12\x03\
\x08\x11\x12\n\x0b\n\x04\x04\0\x02\x06\x12\x03\t\x04\x17\n\x0c\n\x05\x04\
\0\x02\x06\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\0\x02\x06\x01\x12\x03\t\
\x0b\x12\n\x0c\n\x05\x04\0\x02\x06\x03\x12\x03\t\x15\x16\n\n\n\x02\x04\
\x01\x12\x04\x0b\0\r\x01\n\n\n\x03\x04\x01\x01\x12\x03\x0b\x08\r\n\x0b\n\
\x04\x04\x01\x02\0\x12\x03\x0c\x04\x14\n\x0c\n\x05\x04\x01\x02\0\x05\x12\
\x03\x0c\x04\t\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x0c\n\x0f\n\x0c\n\
\x05\x04\x01\x02\0\x03\x12\x03\x0c\x12\x13\n\n\n\x02\x04\x02\x12\x04\x0e\
\0\x12\x01\n\n\n\x03\x04\x02\x01\x12\x03\x0e\x08\x15\n\x0b\n\x04\x04\x02\
\x02\0\x12\x03\x0f\x04\x16\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0f\x04\
\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x0f\x0b\x11\n\x0c\n\x05\x04\x02\
\x02\0\x03\x12\x03\x0f\x14\x15\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\x10\
\x04\x14\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\x03\x10\x04\t\n\x0c\n\x05\
\x04\x02\x02\x01\x01\x12\x03\x10\n\x0f\n\x0c\n\x05\x04\x02\x02\x01\x03\
\x12\x03\x10\x12\x13\n\x0b\n\x04\x04\x02\x02\x02\x12\x03\x11\x04\x12\n\
\x0c\n\x05\x04\x02\x02\x02\x05\x12\x03\x11\x04\t\n\x0c\n\x05\x04\x02\x02\
\x02\x01\x12\x03\x11\n\r\n\x0c\n\x05\x04\x02\x02\x02\x03\x12\x03\x11\x10\
\x11\n\n\n\x02\x05\0\x12\x04\x13\0\x16\x01\n\n\n\x03\x05\0\x01\x12\x03\
\x13\x05\x0c\n\x0b\n\x04\x05\0\x02\0\x12\x03\x14\x04\x0e\n\x0c\n\x05\x05\
\0\x02\0\x01\x12\x03\x14\x04\t\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x14\
\x0c\r\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x15\x04\x0f\n\x0c\n\x05\x05\0\
\x02\x01\x01\x12\x03\x15\x04\n\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x15\
\r\x0e\n\n\n\x02\x05\x01\x12\x04\x17\0\x1a\x01\n\n\n\x03\x05\x01\x01\x12\
\x03\x17\x05\r\n\x0b\n\x04\x05\x01\x02\0\x12\x03\x18\x04\x13\n\x0c\n\x05\
\x05\x01\x02\0\x01\x12\x03\x18\x04\x0e\n\x0c\n\x05\x05\x01\x02\0\x02\x12\
\x03\x18\x11\x12\n\x0b\n\x04\x05\x01\x02\x01\x12\x03\x19\x04\x0e\n\x0c\n\
\x05\x05\x01\x02\x01\x01\x12\x03\x19\x04\t\n\x0c\n\x05\x05\x01\x02\x01\
\x02\x12\x03\x19\x0c\rb\x06proto3\
\x12\x17\n\x07user_id\x18\x07\x20\x01(\tR\x06userId\"3\n\x10RepeatedRevi\
sion\x12\x1f\n\x05items\x18\x01\x20\x03(\x0b2\t.RevisionR\x05items\"\x1d\
\n\x05RevId\x12\x14\n\x05value\x18\x01\x20\x01(\x03R\x05value\"N\n\rRevi\
sionRange\x12\x15\n\x06doc_id\x18\x01\x20\x01(\tR\x05docId\x12\x14\n\x05\
start\x18\x02\x20\x01(\x03R\x05start\x12\x10\n\x03end\x18\x03\x20\x01(\
\x03R\x03end*\x20\n\x07RevType\x12\t\n\x05Local\x10\0\x12\n\n\x06Remote\
\x10\x01*#\n\x08RevState\x12\x0e\n\nStateLocal\x10\0\x12\x07\n\x03Ack\
\x10\x01J\xe8\x07\n\x06\x12\x04\0\0\x1d\x01\n\x08\n\x01\x0c\x12\x03\0\0\
\x12\n\n\n\x02\x04\0\x12\x04\x02\0\n\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\
\x08\x10\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x1a\n\x0c\n\x05\x04\0\
\x02\0\x05\x12\x03\x03\x04\t\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\n\
\x15\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x18\x19\n\x0b\n\x04\x04\0\
\x02\x01\x12\x03\x04\x04\x15\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\
\x04\t\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\n\x10\n\x0c\n\x05\x04\0\
\x02\x01\x03\x12\x03\x04\x13\x14\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\
\x04\x19\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x04\t\n\x0c\n\x05\x04\
\0\x02\x02\x01\x12\x03\x05\n\x14\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\
\x05\x17\x18\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x04\x13\n\x0c\n\x05\
\x04\0\x02\x03\x05\x12\x03\x06\x04\n\n\x0c\n\x05\x04\0\x02\x03\x01\x12\
\x03\x06\x0b\x0e\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x06\x11\x12\n\x0b\
\n\x04\x04\0\x02\x04\x12\x03\x07\x04\x16\n\x0c\n\x05\x04\0\x02\x04\x05\
\x12\x03\x07\x04\n\n\x0c\n\x05\x04\0\x02\x04\x01\x12\x03\x07\x0b\x11\n\
\x0c\n\x05\x04\0\x02\x04\x03\x12\x03\x07\x14\x15\n\x0b\n\x04\x04\0\x02\
\x05\x12\x03\x08\x04\x13\n\x0c\n\x05\x04\0\x02\x05\x06\x12\x03\x08\x04\
\x0b\n\x0c\n\x05\x04\0\x02\x05\x01\x12\x03\x08\x0c\x0e\n\x0c\n\x05\x04\0\
\x02\x05\x03\x12\x03\x08\x11\x12\n\x0b\n\x04\x04\0\x02\x06\x12\x03\t\x04\
\x17\n\x0c\n\x05\x04\0\x02\x06\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\0\x02\
\x06\x01\x12\x03\t\x0b\x12\n\x0c\n\x05\x04\0\x02\x06\x03\x12\x03\t\x15\
\x16\n\n\n\x02\x04\x01\x12\x04\x0b\0\r\x01\n\n\n\x03\x04\x01\x01\x12\x03\
\x0b\x08\x18\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x0c\x04\x20\n\x0c\n\x05\
\x04\x01\x02\0\x04\x12\x03\x0c\x04\x0c\n\x0c\n\x05\x04\x01\x02\0\x06\x12\
\x03\x0c\r\x15\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x0c\x16\x1b\n\x0c\n\
\x05\x04\x01\x02\0\x03\x12\x03\x0c\x1e\x1f\n\n\n\x02\x04\x02\x12\x04\x0e\
\0\x10\x01\n\n\n\x03\x04\x02\x01\x12\x03\x0e\x08\r\n\x0b\n\x04\x04\x02\
\x02\0\x12\x03\x0f\x04\x14\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0f\x04\
\t\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x0f\n\x0f\n\x0c\n\x05\x04\x02\
\x02\0\x03\x12\x03\x0f\x12\x13\n\n\n\x02\x04\x03\x12\x04\x11\0\x15\x01\n\
\n\n\x03\x04\x03\x01\x12\x03\x11\x08\x15\n\x0b\n\x04\x04\x03\x02\0\x12\
\x03\x12\x04\x16\n\x0c\n\x05\x04\x03\x02\0\x05\x12\x03\x12\x04\n\n\x0c\n\
\x05\x04\x03\x02\0\x01\x12\x03\x12\x0b\x11\n\x0c\n\x05\x04\x03\x02\0\x03\
\x12\x03\x12\x14\x15\n\x0b\n\x04\x04\x03\x02\x01\x12\x03\x13\x04\x14\n\
\x0c\n\x05\x04\x03\x02\x01\x05\x12\x03\x13\x04\t\n\x0c\n\x05\x04\x03\x02\
\x01\x01\x12\x03\x13\n\x0f\n\x0c\n\x05\x04\x03\x02\x01\x03\x12\x03\x13\
\x12\x13\n\x0b\n\x04\x04\x03\x02\x02\x12\x03\x14\x04\x12\n\x0c\n\x05\x04\
\x03\x02\x02\x05\x12\x03\x14\x04\t\n\x0c\n\x05\x04\x03\x02\x02\x01\x12\
\x03\x14\n\r\n\x0c\n\x05\x04\x03\x02\x02\x03\x12\x03\x14\x10\x11\n\n\n\
\x02\x05\0\x12\x04\x16\0\x19\x01\n\n\n\x03\x05\0\x01\x12\x03\x16\x05\x0c\
\n\x0b\n\x04\x05\0\x02\0\x12\x03\x17\x04\x0e\n\x0c\n\x05\x05\0\x02\0\x01\
\x12\x03\x17\x04\t\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x17\x0c\r\n\x0b\n\
\x04\x05\0\x02\x01\x12\x03\x18\x04\x0f\n\x0c\n\x05\x05\0\x02\x01\x01\x12\
\x03\x18\x04\n\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x18\r\x0e\n\n\n\x02\
\x05\x01\x12\x04\x1a\0\x1d\x01\n\n\n\x03\x05\x01\x01\x12\x03\x1a\x05\r\n\
\x0b\n\x04\x05\x01\x02\0\x12\x03\x1b\x04\x13\n\x0c\n\x05\x05\x01\x02\0\
\x01\x12\x03\x1b\x04\x0e\n\x0c\n\x05\x05\x01\x02\0\x02\x12\x03\x1b\x11\
\x12\n\x0b\n\x04\x05\x01\x02\x01\x12\x03\x1c\x04\x0c\n\x0c\n\x05\x05\x01\
\x02\x01\x01\x12\x03\x1c\x04\x07\n\x0c\n\x05\x05\x01\x02\x01\x02\x12\x03\
\x1c\n\x0bb\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -9,6 +9,9 @@ message Revision {
RevType ty = 6;
string user_id = 7;
}
message RepeatedRevision {
repeated Revision items = 1;
}
message RevId {
int64 value = 1;
}
@ -23,5 +26,5 @@ enum RevType {
}
enum RevState {
StateLocal = 0;
Acked = 1;
Ack = 1;
}

View File

@ -92,6 +92,26 @@ impl Revision {
}
}
#[derive(PartialEq, Debug, Default, ProtoBuf, Clone)]
pub struct RepeatedRevision {
#[pb(index = 1)]
pub items: Vec<Revision>,
}
impl std::ops::Deref for RepeatedRevision {
type Target = Vec<Revision>;
fn deref(&self) -> &Self::Target { &self.items }
}
impl std::ops::DerefMut for RepeatedRevision {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.items }
}
impl RepeatedRevision {
pub fn into_inner(self) -> Vec<Revision> { self.items }
}
#[derive(Clone, Debug, ProtoBuf, Default)]
pub struct RevId {
#[pb(index = 1)]
@ -167,5 +187,5 @@ pub fn md5<T: AsRef<[u8]>>(data: T) -> String {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum RevState {
StateLocal = 0,
Acked = 1,
Ack = 1,
}