add shared-lib folder

This commit is contained in:
appflowy
2021-11-20 10:52:39 +08:00
parent be23b4cc5a
commit 8e994a954e
217 changed files with 92 additions and 57 deletions

View File

@ -1,25 +1,17 @@
[workspace]
members = [
"lib-dispatch",
"lib-log",
"lib-sqlite",
"lib-infra",
"flowy-sdk",
"dart-ffi",
"lib-log",
"flowy-user",
"flowy-user-infra",
"flowy-ast",
"flowy-derive",
"flowy-test",
"lib-sqlite",
"flowy-database",
"lib-infra",
"flowy-workspace",
"flowy-workspace-infra",
"dart-notify",
"flowy-document",
"flowy-document-infra",
"lib-ot",
"lib-ws",
"backend-service",
]
exclude = ["../backend"]

View File

@ -1,30 +0,0 @@
[package]
name = "backend-service"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
flowy-workspace-infra = { path = "../flowy-workspace-infra" }
flowy-user-infra = { path = "../flowy-user-infra" }
log = "0.4.14"
lazy_static = "1.4.0"
tokio = { version = "1", features = ["rt"] }
anyhow = "1.0"
thiserror = "1.0.24"
bytes = { version = "1.0", features = ["serde"]}
reqwest = "0.11"
hyper = "0.14"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_repr = "0.1"
uuid = { version = "0.8", features = ["v4"] }
protobuf = {version = "2.18.0"}
derive_more = {version = "0.99", features = ["display"]}
tracing = { version = "0.1", features = ["log"] }
actix-web = {version = "4.0.0-beta.8", optional = true}
[features]
http_server = ["actix-web"]

View File

@ -1,53 +0,0 @@
pub const HOST: &'static str = "localhost:8000";
pub const HTTP_SCHEMA: &'static str = "http";
pub const WS_SCHEMA: &'static str = "ws";
pub const HEADER_TOKEN: &'static str = "token";
#[derive(Debug, Clone)]
pub struct ServerConfig {
http_schema: String,
host: String,
ws_schema: String,
}
impl std::default::Default for ServerConfig {
fn default() -> Self {
ServerConfig {
http_schema: HTTP_SCHEMA.to_string(),
host: HOST.to_string(),
ws_schema: WS_SCHEMA.to_string(),
}
}
}
impl ServerConfig {
pub fn new(host: &str, http_schema: &str, ws_schema: &str) -> Self {
Self {
http_schema: http_schema.to_owned(),
host: host.to_owned(),
ws_schema: ws_schema.to_owned(),
}
}
fn scheme(&self) -> String { format!("{}://", self.http_schema) }
pub fn sign_up_url(&self) -> String { format!("{}{}/api/register", self.scheme(), self.host) }
pub fn sign_in_url(&self) -> String { format!("{}{}/api/auth", self.scheme(), self.host) }
pub fn sign_out_url(&self) -> String { format!("{}{}/api/auth", self.scheme(), self.host) }
pub fn user_profile_url(&self) -> String { format!("{}{}/api/user", self.scheme(), self.host) }
pub fn workspace_url(&self) -> String { format!("{}{}/api/workspace", self.scheme(), self.host) }
pub fn app_url(&self) -> String { format!("{}{}/api/app", self.scheme(), self.host) }
pub fn view_url(&self) -> String { format!("{}{}/api/view", self.scheme(), self.host) }
pub fn doc_url(&self) -> String { format!("{}{}/api/doc", self.scheme(), self.host) }
pub fn trash_url(&self) -> String { format!("{}{}/api/trash", self.scheme(), self.host) }
pub fn ws_addr(&self) -> String { format!("{}://{}/ws", self.ws_schema, self.host) }
}

View File

@ -1,123 +0,0 @@
use bytes::Bytes;
use serde::{Deserialize, Serialize, __private::Formatter};
use serde_repr::*;
use std::{fmt, fmt::Debug};
use crate::response::FlowyResponse;
pub type Result<T> = std::result::Result<T, ServerError>;
#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone)]
pub struct ServerError {
pub code: ErrorCode,
pub msg: String,
}
macro_rules! static_error {
($name:ident, $status:expr) => {
#[allow(non_snake_case, missing_docs)]
pub fn $name() -> ServerError {
ServerError {
code: $status,
msg: format!("{}", $status),
}
}
};
}
impl ServerError {
static_error!(internal, ErrorCode::InternalError);
static_error!(http, ErrorCode::HttpError);
static_error!(payload_none, ErrorCode::PayloadUnexpectedNone);
static_error!(unauthorized, ErrorCode::UserUnauthorized);
static_error!(password_not_match, ErrorCode::PasswordNotMatch);
static_error!(params_invalid, ErrorCode::ParamsInvalid);
static_error!(connect_timeout, ErrorCode::ConnectTimeout);
static_error!(connect_close, ErrorCode::ConnectClose);
static_error!(connect_cancel, ErrorCode::ConnectCancel);
static_error!(connect_refused, ErrorCode::ConnectRefused);
static_error!(record_not_found, ErrorCode::RecordNotFound);
pub fn new(msg: String, code: ErrorCode) -> Self { Self { code, msg } }
pub fn context<T: Debug>(mut self, error: T) -> Self {
self.msg = format!("{:?}", error);
self
}
pub fn is_record_not_found(&self) -> bool { self.code == ErrorCode::RecordNotFound }
pub fn is_unauthorized(&self) -> bool { self.code == ErrorCode::UserUnauthorized }
}
pub fn internal_error<T>(e: T) -> ServerError
where
T: std::fmt::Debug,
{
ServerError::internal().context(e)
}
pub fn invalid_params<T: Debug>(e: T) -> ServerError { ServerError::params_invalid().context(e) }
impl std::fmt::Display for ServerError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let msg = format!("{:?}:{}", self.code, self.msg);
f.write_str(&msg)
}
}
impl std::convert::From<&ServerError> for FlowyResponse {
fn from(error: &ServerError) -> Self {
FlowyResponse {
data: Bytes::from(vec![]),
error: Some(error.clone()),
}
}
}
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone, derive_more::Display)]
#[repr(u16)]
pub enum ErrorCode {
#[display(fmt = "Unauthorized")]
UserUnauthorized = 1,
#[display(fmt = "Payload too large")]
PayloadOverflow = 2,
#[display(fmt = "Payload deserialize failed")]
PayloadSerdeFail = 3,
#[display(fmt = "Unexpected empty payload")]
PayloadUnexpectedNone = 4,
#[display(fmt = "Params is invalid")]
ParamsInvalid = 5,
#[display(fmt = "Protobuf serde error")]
ProtobufError = 10,
#[display(fmt = "Json serde Error")]
SerdeError = 11,
#[display(fmt = "Email address already exists")]
EmailAlreadyExists = 50,
#[display(fmt = "Username and password do not match")]
PasswordNotMatch = 51,
#[display(fmt = "Connect refused")]
ConnectRefused = 100,
#[display(fmt = "Connection timeout")]
ConnectTimeout = 101,
#[display(fmt = "Connection closed")]
ConnectClose = 102,
#[display(fmt = "Connection canceled")]
ConnectCancel = 103,
#[display(fmt = "Sql error")]
SqlError = 200,
#[display(fmt = "Record not found")]
RecordNotFound = 201,
#[display(fmt = "Http request error")]
HttpError = 300,
#[display(fmt = "Internal error")]
InternalError = 1000,
}

View File

@ -1,7 +0,0 @@
pub mod config;
pub mod errors;
pub mod middleware;
pub mod request;
pub mod response;
pub mod user_request;
pub mod workspace_request;

View File

@ -1,39 +0,0 @@
use crate::{request::ResponseMiddleware, response::FlowyResponse};
use lazy_static::lazy_static;
use std::sync::Arc;
use tokio::sync::broadcast;
lazy_static! {
pub static ref BACKEND_API_MIDDLEWARE: Arc<WorkspaceMiddleware> = Arc::new(WorkspaceMiddleware::new());
}
pub struct WorkspaceMiddleware {
invalid_token_sender: broadcast::Sender<String>,
}
impl WorkspaceMiddleware {
fn new() -> Self {
let (sender, _) = broadcast::channel(10);
WorkspaceMiddleware {
invalid_token_sender: sender,
}
}
pub fn invalid_token_subscribe(&self) -> broadcast::Receiver<String> { self.invalid_token_sender.subscribe() }
}
impl ResponseMiddleware for WorkspaceMiddleware {
fn receive_response(&self, token: &Option<String>, response: &FlowyResponse) {
if let Some(error) = &response.error {
if error.is_unauthorized() {
log::error!("user is unauthorized");
match token {
None => {},
Some(token) => match self.invalid_token_sender.send(token.clone()) {
Ok(_) => {},
Err(e) => log::error!("{:?}", e),
},
}
}
}
}
}

View File

@ -1,3 +0,0 @@
mod request;
pub use request::*;

View File

@ -1,205 +0,0 @@
use crate::{config::HEADER_TOKEN, errors::ServerError, response::FlowyResponse};
use bytes::Bytes;
use hyper::http;
use protobuf::ProtobufError;
use reqwest::{header::HeaderMap, Client, Method, Response};
use std::{
convert::{TryFrom, TryInto},
sync::Arc,
time::Duration,
};
use tokio::sync::oneshot;
pub trait ResponseMiddleware {
fn receive_response(&self, token: &Option<String>, response: &FlowyResponse);
}
pub struct HttpRequestBuilder {
url: String,
body: Option<Bytes>,
response: Option<Bytes>,
headers: HeaderMap,
method: Method,
middleware: Vec<Arc<dyn ResponseMiddleware + Send + Sync>>,
}
impl HttpRequestBuilder {
pub fn new() -> Self {
Self {
url: "".to_owned(),
body: None,
response: None,
headers: HeaderMap::new(),
method: Method::GET,
middleware: Vec::new(),
}
}
pub fn middleware<T>(mut self, middleware: Arc<T>) -> Self
where
T: 'static + ResponseMiddleware + Send + Sync,
{
self.middleware.push(middleware);
self
}
pub fn get(mut self, url: &str) -> Self {
self.url = url.to_owned();
self.method = Method::GET;
self
}
pub fn post(mut self, url: &str) -> Self {
self.url = url.to_owned();
self.method = Method::POST;
self
}
pub fn patch(mut self, url: &str) -> Self {
self.url = url.to_owned();
self.method = Method::PATCH;
self
}
pub fn delete(mut self, url: &str) -> Self {
self.url = url.to_owned();
self.method = Method::DELETE;
self
}
pub fn header(mut self, key: &'static str, value: &str) -> Self {
self.headers.insert(key, value.parse().unwrap());
self
}
pub fn protobuf<T>(self, body: T) -> Result<Self, ServerError>
where
T: TryInto<Bytes, Error = ProtobufError>,
{
let body: Bytes = body.try_into()?;
self.bytes(body)
}
pub fn bytes(mut self, body: Bytes) -> Result<Self, ServerError> {
self.body = Some(body);
Ok(self)
}
pub async fn send(self) -> Result<(), ServerError> {
let _ = self.inner_send().await?;
Ok(())
}
pub async fn response<T>(self) -> Result<T, ServerError>
where
T: TryFrom<Bytes, Error = ProtobufError>,
{
let builder = self.inner_send().await?;
match builder.response {
None => Err(unexpected_empty_payload(&builder.url)),
Some(data) => Ok(T::try_from(data)?),
}
}
pub async fn option_response<T>(self) -> Result<Option<T>, ServerError>
where
T: TryFrom<Bytes, Error = ProtobufError>,
{
let result = self.inner_send().await;
match result {
Ok(builder) => match builder.response {
None => Err(unexpected_empty_payload(&builder.url)),
Some(data) => Ok(Some(T::try_from(data)?)),
},
Err(error) => match error.is_record_not_found() {
true => Ok(None),
false => Err(error),
},
}
}
fn token(&self) -> Option<String> {
match self.headers.get(HEADER_TOKEN) {
None => None,
Some(header) => match header.to_str() {
Ok(val) => Some(val.to_owned()),
Err(_) => None,
},
}
}
async fn inner_send(mut self) -> Result<Self, ServerError> {
let (tx, rx) = oneshot::channel::<Result<Response, _>>();
let url = self.url.clone();
let body = self.body.take();
let method = self.method.clone();
let headers = self.headers.clone();
// reqwest client is not 'Sync' by channel is.
tokio::spawn(async move {
let client = default_client();
let mut builder = client.request(method.clone(), url).headers(headers);
if let Some(body) = body {
builder = builder.body(body);
}
let response = builder.send().await;
let _ = tx.send(response);
});
let response = rx.await??;
tracing::trace!("Http Response: {:?}", response);
let flowy_response = flowy_response_from(response).await?;
let token = self.token();
self.middleware.iter().for_each(|middleware| {
middleware.receive_response(&token, &flowy_response);
});
match flowy_response.error {
None => {
self.response = Some(flowy_response.data);
Ok(self)
},
Some(error) => Err(error),
}
}
}
fn unexpected_empty_payload(url: &str) -> ServerError {
let msg = format!("Request: {} receives unexpected empty payload", url);
ServerError::payload_none().context(msg)
}
async fn flowy_response_from(original: Response) -> Result<FlowyResponse, ServerError> {
let bytes = original.bytes().await?;
let response: FlowyResponse = serde_json::from_slice(&bytes)?;
Ok(response)
}
#[allow(dead_code)]
async fn get_response_data(original: Response) -> Result<Bytes, ServerError> {
if original.status() == http::StatusCode::OK {
let bytes = original.bytes().await?;
let response: FlowyResponse = serde_json::from_slice(&bytes)?;
match response.error {
None => Ok(response.data),
Some(error) => Err(error),
}
} else {
Err(ServerError::http().context(original))
}
}
fn default_client() -> Client {
let result = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(500))
.timeout(Duration::from_secs(5))
.build();
match result {
Ok(client) => client,
Err(e) => {
log::error!("Create reqwest client failed: {}", e);
reqwest::Client::new()
},
}
}

View File

@ -1,6 +0,0 @@
mod response;
#[cfg(feature = "http_server")]
mod response_http;
pub use response::*;

View File

@ -1,86 +0,0 @@
use crate::errors::{ErrorCode, ServerError};
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use std::{convert::TryInto, error::Error, fmt::Debug};
use tokio::sync::oneshot::error::RecvError;
#[derive(Debug, Serialize, Deserialize)]
pub struct FlowyResponse {
pub data: Bytes,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<ServerError>,
}
impl FlowyResponse {
pub fn new(data: Bytes, error: Option<ServerError>) -> Self { FlowyResponse { data, error } }
pub fn success() -> Self { Self::new(Bytes::new(), None) }
pub fn data<T: TryInto<Bytes, Error = protobuf::ProtobufError>>(mut self, data: T) -> Result<Self, ServerError> {
let bytes: Bytes = data.try_into()?;
self.data = bytes;
Ok(self)
}
pub fn pb<T: ::protobuf::Message>(mut self, data: T) -> Result<Self, ServerError> {
let bytes: Bytes = Bytes::from(data.write_to_bytes()?);
self.data = bytes;
Ok(self)
}
}
impl std::convert::From<protobuf::ProtobufError> for ServerError {
fn from(e: protobuf::ProtobufError) -> Self { ServerError::internal().context(e) }
}
impl std::convert::From<RecvError> for ServerError {
fn from(error: RecvError) -> Self { ServerError::internal().context(error) }
}
impl std::convert::From<serde_json::Error> for ServerError {
fn from(e: serde_json::Error) -> Self { ServerError::internal().context(e) }
}
impl std::convert::From<anyhow::Error> for ServerError {
fn from(error: anyhow::Error) -> Self { ServerError::internal().context(error) }
}
impl std::convert::From<reqwest::Error> for ServerError {
fn from(error: reqwest::Error) -> Self {
if error.is_timeout() {
return ServerError::connect_timeout().context(error);
}
if error.is_request() {
let hyper_error: Option<&hyper::Error> = error.source().unwrap().downcast_ref();
return match hyper_error {
None => ServerError::connect_refused().context(error),
Some(hyper_error) => {
let mut code = ErrorCode::InternalError;
let msg = format!("{}", error);
if hyper_error.is_closed() {
code = ErrorCode::ConnectClose;
}
if hyper_error.is_connect() {
code = ErrorCode::ConnectRefused;
}
if hyper_error.is_canceled() {
code = ErrorCode::ConnectCancel;
}
if hyper_error.is_timeout() {}
ServerError { code, msg }
},
};
}
ServerError::internal().context(error)
}
}
impl std::convert::From<uuid::Error> for ServerError {
fn from(e: uuid::Error) -> Self { ServerError::internal().context(e) }
}

View File

@ -1,24 +0,0 @@
use crate::response::*;
use actix_web::{error::ResponseError, HttpResponse};
use crate::errors::ServerError;
use actix_web::body::AnyBody;
impl ResponseError for ServerError {
fn error_response(&self) -> HttpResponse {
let response: FlowyResponse = self.into();
response.into()
}
}
impl std::convert::Into<HttpResponse> for FlowyResponse {
fn into(self) -> HttpResponse { HttpResponse::Ok().json(self) }
}
impl std::convert::Into<AnyBody> for FlowyResponse {
fn into(self) -> AnyBody {
match serde_json::to_string(&self) {
Ok(body) => AnyBody::from(body),
Err(_) => AnyBody::Empty,
}
}
}

View File

@ -1,52 +0,0 @@
use crate::{config::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
use flowy_user_infra::entities::prelude::*;
pub(crate) fn request_builder() -> HttpRequestBuilder {
HttpRequestBuilder::new().middleware(crate::middleware::BACKEND_API_MIDDLEWARE.clone())
}
pub async fn user_sign_up_request(params: SignUpParams, url: &str) -> Result<SignUpResponse, ServerError> {
let response = request_builder()
.post(&url.to_owned())
.protobuf(params)?
.response()
.await?;
Ok(response)
}
pub async fn user_sign_in_request(params: SignInParams, url: &str) -> Result<SignInResponse, ServerError> {
let response = request_builder()
.post(&url.to_owned())
.protobuf(params)?
.response()
.await?;
Ok(response)
}
pub async fn user_sign_out_request(token: &str, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.send()
.await?;
Ok(())
}
pub async fn get_user_profile_request(token: &str, url: &str) -> Result<UserProfile, ServerError> {
let user_profile = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.response()
.await?;
Ok(user_profile)
}
pub async fn update_user_profile_request(token: &str, params: UpdateUserParams, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}

View File

@ -1,172 +0,0 @@
use crate::{config::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
use flowy_workspace_infra::entities::prelude::*;
pub(crate) fn request_builder() -> HttpRequestBuilder {
HttpRequestBuilder::new().middleware(crate::middleware::BACKEND_API_MIDDLEWARE.clone())
}
pub async fn create_workspace_request(
token: &str,
params: CreateWorkspaceParams,
url: &str,
) -> Result<Workspace, ServerError> {
let workspace = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response()
.await?;
Ok(workspace)
}
pub async fn read_workspaces_request(
token: &str,
params: WorkspaceIdentifier,
url: &str,
) -> Result<RepeatedWorkspace, ServerError> {
let repeated_workspace = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response::<RepeatedWorkspace>()
.await?;
Ok(repeated_workspace)
}
pub async fn update_workspace_request(
token: &str,
params: UpdateWorkspaceParams,
url: &str,
) -> Result<(), ServerError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_workspace_request(token: &str, params: WorkspaceIdentifier, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.delete(url)
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
// App
pub async fn create_app_request(token: &str, params: CreateAppParams, url: &str) -> Result<App, ServerError> {
let app = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response()
.await?;
Ok(app)
}
pub async fn read_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<Option<App>, ServerError> {
let app = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.option_response()
.await?;
Ok(app)
}
pub async fn update_app_request(token: &str, params: UpdateAppParams, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
// View
pub async fn create_view_request(token: &str, params: CreateViewParams, url: &str) -> Result<View, ServerError> {
let view = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response()
.await?;
Ok(view)
}
pub async fn read_view_request(token: &str, params: ViewIdentifier, url: &str) -> Result<Option<View>, ServerError> {
let view = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.option_response()
.await?;
Ok(view)
}
pub async fn update_view_request(token: &str, params: UpdateViewParams, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_view_request(token: &str, params: ViewIdentifiers, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn create_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn read_trash_request(token: &str, url: &str) -> Result<RepeatedTrash, ServerError> {
let repeated_trash = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.response::<RepeatedTrash>()
.await?;
Ok(repeated_trash)
}

View File

@ -26,9 +26,10 @@ parking_lot = "0.11"
lib-dispatch = {path = "../lib-dispatch" }
flowy-sdk = {path = "../flowy-sdk"}
flowy-derive = {path = "../flowy-derive"}
dart-notify = {path = "../dart-notify" }
backend-service = { path = "../backend-service" }
flowy-derive = {path = "../../../shared-lib/flowy-derive" }
backend-service = { path = "../../../shared-lib/backend-service" }
[features]

View File

@ -12,7 +12,7 @@ allo-isolate = {version = "^0.1", features = ["catch-unwind",]}
log = "0.4.14"
bytes = { version = "1.0" }
flowy-derive = {path = "../flowy-derive"}
flowy-derive = {path = "../../../shared-lib/flowy-derive" }
lib-dispatch = {path = "../lib-dispatch" }
[features]

View File

@ -1,11 +0,0 @@
[package]
name = "flowy-ast"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
syn = { version = "1.0.60", features = ["extra-traits", "parsing", "derive", "full"]}
quote = "1.0"
proc-macro2 = "1.0"

View File

@ -1,232 +0,0 @@
use crate::{attr, ty_ext::*, AttrsContainer, Ctxt};
use syn::{self, punctuated::Punctuated};
pub struct ASTContainer<'a> {
/// The struct or enum name (without generics).
pub ident: syn::Ident,
/// Attributes on the structure.
pub attrs: AttrsContainer,
/// The contents of the struct or enum.
pub data: ASTData<'a>,
}
impl<'a> ASTContainer<'a> {
pub fn from_ast(cx: &Ctxt, ast: &'a syn::DeriveInput) -> Option<ASTContainer<'a>> {
let attrs = AttrsContainer::from_ast(cx, ast);
// syn::DeriveInput
// 1. syn::DataUnion
// 2. syn::DataStruct
// 3. syn::DataEnum
let data = match &ast.data {
syn::Data::Struct(data) => {
// https://docs.rs/syn/1.0.48/syn/struct.DataStruct.html
let (style, fields) = struct_from_ast(cx, &data.fields);
ASTData::Struct(style, fields)
},
syn::Data::Union(_) => {
cx.error_spanned_by(ast, "Does not support derive for unions");
return None;
},
syn::Data::Enum(data) => {
// https://docs.rs/syn/1.0.48/syn/struct.DataEnum.html
ASTData::Enum(enum_from_ast(cx, &ast.ident, &data.variants, &ast.attrs))
},
};
let ident = ast.ident.clone();
let item = ASTContainer { ident, attrs, data };
Some(item)
}
}
pub enum ASTData<'a> {
Struct(ASTStyle, Vec<ASTField<'a>>),
Enum(Vec<ASTEnumVariant<'a>>),
}
impl<'a> ASTData<'a> {
pub fn all_fields(&'a self) -> Box<dyn Iterator<Item = &'a ASTField<'a>> + 'a> {
match self {
ASTData::Enum(variants) => Box::new(variants.iter().flat_map(|variant| variant.fields.iter())),
ASTData::Struct(_, fields) => Box::new(fields.iter()),
}
}
pub fn all_variants(&'a self) -> Box<dyn Iterator<Item = &'a attr::ASTEnumAttrVariant> + 'a> {
match self {
ASTData::Enum(variants) => {
let iter = variants.iter().map(|variant| &variant.attrs);
Box::new(iter)
},
ASTData::Struct(_, fields) => {
let iter = fields.iter().flat_map(|_| None);
Box::new(iter)
},
}
}
pub fn all_idents(&'a self) -> Box<dyn Iterator<Item = &'a syn::Ident> + 'a> {
match self {
ASTData::Enum(variants) => Box::new(variants.iter().map(|v| &v.ident)),
ASTData::Struct(_, fields) => {
let iter = fields.iter().flat_map(|f| match &f.member {
syn::Member::Named(ident) => Some(ident),
_ => None,
});
Box::new(iter)
},
}
}
}
/// A variant of an enum.
pub struct ASTEnumVariant<'a> {
pub ident: syn::Ident,
pub attrs: attr::ASTEnumAttrVariant,
pub style: ASTStyle,
pub fields: Vec<ASTField<'a>>,
pub original: &'a syn::Variant,
}
impl<'a> ASTEnumVariant<'a> {
pub fn name(&self) -> String { self.ident.to_string() }
}
pub enum BracketCategory {
Other,
Opt,
Vec,
Map((String, String)),
}
pub struct ASTField<'a> {
pub member: syn::Member,
pub attrs: attr::ASTAttrField,
pub ty: &'a syn::Type,
pub original: &'a syn::Field,
pub bracket_ty: Option<syn::Ident>,
pub bracket_inner_ty: Option<syn::Ident>,
pub bracket_category: Option<BracketCategory>,
}
impl<'a> ASTField<'a> {
pub fn new(cx: &Ctxt, field: &'a syn::Field, index: usize) -> Self {
let mut bracket_inner_ty = None;
let mut bracket_ty = None;
let mut bracket_category = Some(BracketCategory::Other);
match parse_ty(cx, &field.ty) {
Some(inner) => {
match inner.primitive_ty {
PrimitiveTy::Map(map_info) => {
bracket_category = Some(BracketCategory::Map((map_info.key.clone(), map_info.value.clone())))
},
PrimitiveTy::Vec => {
bracket_category = Some(BracketCategory::Vec);
},
PrimitiveTy::Opt => {
bracket_category = Some(BracketCategory::Opt);
},
PrimitiveTy::Other => {
bracket_category = Some(BracketCategory::Other);
},
}
match *inner.bracket_ty_info {
Some(bracketed_inner_ty) => {
bracket_inner_ty = Some(bracketed_inner_ty.ident.clone());
bracket_ty = Some(inner.ident.clone());
},
None => {
bracket_ty = Some(inner.ident.clone());
},
}
},
None => {
cx.error_spanned_by(&field.ty, "fail to get the ty inner type");
},
}
ASTField {
member: match &field.ident {
Some(ident) => syn::Member::Named(ident.clone()),
None => syn::Member::Unnamed(index.into()),
},
attrs: attr::ASTAttrField::from_ast(cx, index, field),
ty: &field.ty,
original: field,
bracket_ty,
bracket_inner_ty,
bracket_category,
}
}
pub fn ty_as_str(&self) -> String {
match self.bracket_inner_ty {
Some(ref ty) => ty.to_string(),
None => self.bracket_ty.as_ref().unwrap().clone().to_string(),
}
}
#[allow(dead_code)]
pub fn name(&self) -> Option<syn::Ident> {
if let syn::Member::Named(ident) = &self.member {
return Some(ident.clone());
} else {
None
}
}
pub fn is_option(&self) -> bool { attr::is_option(&self.ty) }
}
#[derive(Copy, Clone)]
pub enum ASTStyle {
Struct,
/// Many unnamed fields.
Tuple,
/// One unnamed field.
NewType,
/// No fields.
Unit,
}
pub fn struct_from_ast<'a>(cx: &Ctxt, fields: &'a syn::Fields) -> (ASTStyle, Vec<ASTField<'a>>) {
match fields {
syn::Fields::Named(fields) => (ASTStyle::Struct, fields_from_ast(cx, &fields.named)),
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
(ASTStyle::NewType, fields_from_ast(cx, &fields.unnamed))
},
syn::Fields::Unnamed(fields) => (ASTStyle::Tuple, fields_from_ast(cx, &fields.unnamed)),
syn::Fields::Unit => (ASTStyle::Unit, Vec::new()),
}
}
pub fn enum_from_ast<'a>(
cx: &Ctxt,
ident: &syn::Ident,
variants: &'a Punctuated<syn::Variant, Token![,]>,
enum_attrs: &Vec<syn::Attribute>,
) -> Vec<ASTEnumVariant<'a>> {
variants
.iter()
.flat_map(|variant| {
let attrs = attr::ASTEnumAttrVariant::from_ast(cx, ident, variant, enum_attrs);
let (style, fields) = struct_from_ast(cx, &variant.fields);
Some(ASTEnumVariant {
ident: variant.ident.clone(),
attrs,
style,
fields,
original: variant,
})
})
.collect()
}
fn fields_from_ast<'a>(cx: &Ctxt, fields: &'a Punctuated<syn::Field, Token![,]>) -> Vec<ASTField<'a>> {
fields
.iter()
.enumerate()
.map(|(index, field)| ASTField::new(cx, field, index))
.collect()
}

View File

@ -1,491 +0,0 @@
use crate::{symbol::*, Ctxt};
use quote::ToTokens;
use syn::{
self,
parse::{self, Parse},
Meta::{List, NameValue, Path},
NestedMeta::{Lit, Meta},
};
use proc_macro2::{Group, Span, TokenStream, TokenTree};
#[allow(dead_code)]
pub struct AttrsContainer {
name: String,
pb_struct_type: Option<syn::Type>,
pb_enum_type: Option<syn::Type>,
}
impl AttrsContainer {
/// Extract out the `#[pb(...)]` attributes from an item.
pub fn from_ast(cx: &Ctxt, item: &syn::DeriveInput) -> Self {
let mut pb_struct_type = ASTAttr::none(cx, PB_STRUCT);
let mut pb_enum_type = ASTAttr::none(cx, PB_ENUM);
for meta_item in item.attrs.iter().flat_map(|attr| get_meta_items(cx, attr)).flatten() {
match &meta_item {
// Parse `#[pb(struct = "Type")]
Meta(NameValue(m)) if m.path == PB_STRUCT => {
if let Ok(into_ty) = parse_lit_into_ty(cx, PB_STRUCT, &m.lit) {
pb_struct_type.set_opt(&m.path, Some(into_ty));
}
},
// Parse `#[pb(enum = "Type")]
Meta(NameValue(m)) if m.path == PB_ENUM => {
if let Ok(into_ty) = parse_lit_into_ty(cx, PB_ENUM, &m.lit) {
pb_enum_type.set_opt(&m.path, Some(into_ty));
}
},
Meta(meta_item) => {
let path = meta_item.path().into_token_stream().to_string().replace(' ', "");
cx.error_spanned_by(meta_item.path(), format!("unknown pb container attribute `{}`", path));
},
Lit(lit) => {
cx.error_spanned_by(lit, "unexpected literal in pb container attribute");
},
}
}
match &item.data {
syn::Data::Struct(_) => {
pb_struct_type.set_if_none(default_pb_type(&cx, &item.ident));
},
syn::Data::Enum(_) => {
pb_enum_type.set_if_none(default_pb_type(&cx, &item.ident));
},
_ => {},
}
AttrsContainer {
name: item.ident.to_string(),
pb_struct_type: pb_struct_type.get(),
pb_enum_type: pb_enum_type.get(),
}
}
pub fn pb_struct_type(&self) -> Option<&syn::Type> { self.pb_struct_type.as_ref() }
pub fn pb_enum_type(&self) -> Option<&syn::Type> { self.pb_enum_type.as_ref() }
}
struct ASTAttr<'c, T> {
cx: &'c Ctxt,
name: Symbol,
tokens: TokenStream,
value: Option<T>,
}
impl<'c, T> ASTAttr<'c, T> {
fn none(cx: &'c Ctxt, name: Symbol) -> Self {
ASTAttr {
cx,
name,
tokens: TokenStream::new(),
value: None,
}
}
fn set<A: ToTokens>(&mut self, obj: A, value: T) {
let tokens = obj.into_token_stream();
if self.value.is_some() {
self.cx
.error_spanned_by(tokens, format!("duplicate attribute `{}`", self.name));
} else {
self.tokens = tokens;
self.value = Some(value);
}
}
fn set_opt<A: ToTokens>(&mut self, obj: A, value: Option<T>) {
if let Some(value) = value {
self.set(obj, value);
}
}
fn set_if_none(&mut self, value: T) {
if self.value.is_none() {
self.value = Some(value);
}
}
fn get(self) -> Option<T> { self.value }
#[allow(dead_code)]
fn get_with_tokens(self) -> Option<(TokenStream, T)> {
match self.value {
Some(v) => Some((self.tokens, v)),
None => None,
}
}
}
pub struct ASTAttrField {
#[allow(dead_code)]
name: String,
pb_index: Option<syn::LitInt>,
pb_one_of: bool,
skip_serializing: bool,
skip_deserializing: bool,
serialize_with: Option<syn::ExprPath>,
deserialize_with: Option<syn::ExprPath>,
}
impl ASTAttrField {
/// Extract out the `#[pb(...)]` attributes from a struct field.
pub fn from_ast(cx: &Ctxt, index: usize, field: &syn::Field) -> Self {
let mut pb_index = ASTAttr::none(cx, PB_INDEX);
let mut pb_one_of = BoolAttr::none(cx, PB_ONE_OF);
let mut serialize_with = ASTAttr::none(cx, SERIALIZE_WITH);
let mut skip_serializing = BoolAttr::none(cx, SKIP_SERIALIZING);
let mut deserialize_with = ASTAttr::none(cx, DESERIALIZE_WITH);
let mut skip_deserializing = BoolAttr::none(cx, SKIP_DESERIALIZING);
let ident = match &field.ident {
Some(ident) => ident.to_string(),
None => index.to_string(),
};
for meta_item in field.attrs.iter().flat_map(|attr| get_meta_items(cx, attr)).flatten() {
match &meta_item {
// Parse `#[pb(skip)]`
Meta(Path(word)) if word == SKIP => {
skip_serializing.set_true(word);
skip_deserializing.set_true(word);
},
// Parse '#[pb(index = x)]'
Meta(NameValue(m)) if m.path == PB_INDEX => {
if let syn::Lit::Int(lit) = &m.lit {
pb_index.set(&m.path, lit.clone());
}
},
// Parse `#[pb(one_of)]`
Meta(Path(path)) if path == PB_ONE_OF => {
pb_one_of.set_true(path);
},
// Parse `#[pb(serialize_with = "...")]`
Meta(NameValue(m)) if m.path == SERIALIZE_WITH => {
if let Ok(path) = parse_lit_into_expr_path(cx, SERIALIZE_WITH, &m.lit) {
serialize_with.set(&m.path, path);
}
},
// Parse `#[pb(deserialize_with = "...")]`
Meta(NameValue(m)) if m.path == DESERIALIZE_WITH => {
if let Ok(path) = parse_lit_into_expr_path(cx, DESERIALIZE_WITH, &m.lit) {
deserialize_with.set(&m.path, path);
}
},
Meta(meta_item) => {
let path = meta_item.path().into_token_stream().to_string().replace(' ', "");
cx.error_spanned_by(meta_item.path(), format!("unknown field attribute `{}`", path));
},
Lit(lit) => {
cx.error_spanned_by(lit, "unexpected literal in pb field attribute");
},
}
}
ASTAttrField {
name: ident.to_string().clone(),
pb_index: pb_index.get(),
pb_one_of: pb_one_of.get(),
skip_serializing: skip_serializing.get(),
skip_deserializing: skip_deserializing.get(),
serialize_with: serialize_with.get(),
deserialize_with: deserialize_with.get(),
}
}
#[allow(dead_code)]
pub fn pb_index(&self) -> Option<String> {
match self.pb_index {
Some(ref lit) => Some(lit.base10_digits().to_string()),
None => None,
}
}
pub fn is_one_of(&self) -> bool { self.pb_one_of }
pub fn serialize_with(&self) -> Option<&syn::ExprPath> { self.serialize_with.as_ref() }
pub fn deserialize_with(&self) -> Option<&syn::ExprPath> { self.deserialize_with.as_ref() }
pub fn skip_serializing(&self) -> bool { self.skip_serializing }
pub fn skip_deserializing(&self) -> bool { self.skip_deserializing }
}
pub enum Default {
/// Field must always be specified because it does not have a default.
None,
/// The default is given by `std::default::Default::default()`.
Default,
/// The default is given by this function.
Path(syn::ExprPath),
}
#[derive(Debug, Clone)]
pub struct EventAttrs {
input: Option<syn::Path>,
output: Option<syn::Path>,
error_ty: Option<String>,
pub ignore: bool,
}
#[derive(Debug, Clone)]
pub struct ASTEnumAttrVariant {
pub enum_name: String,
pub enum_item_name: String,
pub value: String,
pub event_attrs: EventAttrs,
}
impl ASTEnumAttrVariant {
pub fn from_ast(ctxt: &Ctxt, ident: &syn::Ident, variant: &syn::Variant, enum_attrs: &Vec<syn::Attribute>) -> Self {
let enum_item_name = variant.ident.to_string();
let enum_name = ident.to_string();
let mut value = String::new();
if variant.discriminant.is_some() {
match variant.discriminant.as_ref().unwrap().1 {
syn::Expr::Lit(ref expr_list) => {
let lit_int = if let syn::Lit::Int(ref int_value) = expr_list.lit {
int_value
} else {
unimplemented!()
};
value = lit_int.base10_digits().to_string();
},
_ => {},
}
}
let event_attrs = get_event_attrs_from(ctxt, &variant.attrs, enum_attrs);
ASTEnumAttrVariant {
enum_name,
enum_item_name,
value,
event_attrs,
}
}
pub fn event_input(&self) -> Option<syn::Path> { self.event_attrs.input.clone() }
pub fn event_output(&self) -> Option<syn::Path> { self.event_attrs.output.clone() }
pub fn event_error(&self) -> String { self.event_attrs.error_ty.as_ref().unwrap().clone() }
}
fn get_event_attrs_from(
ctxt: &Ctxt,
variant_attrs: &Vec<syn::Attribute>,
enum_attrs: &Vec<syn::Attribute>,
) -> EventAttrs {
let mut event_attrs = EventAttrs {
input: None,
output: None,
error_ty: None,
ignore: false,
};
enum_attrs
.iter()
.filter(|attr| attr.path.segments.iter().find(|s| s.ident == EVENT_ERR).is_some())
.for_each(|attr| {
if let Ok(NameValue(named_value)) = attr.parse_meta() {
if let syn::Lit::Str(s) = named_value.lit {
event_attrs.error_ty = Some(s.value());
} else {
eprintln!("{} should not be empty", EVENT_ERR);
}
} else {
eprintln!("❌ Can not find any {} on attr: {:#?}", EVENT_ERR, attr);
}
});
let mut extract_event_attr = |attr: &syn::Attribute, meta_item: &syn::NestedMeta| match &meta_item {
Meta(NameValue(name_value)) => {
if name_value.path == EVENT_INPUT {
if let syn::Lit::Str(s) = &name_value.lit {
let input_type = parse_lit_str(s)
.map_err(|_| {
ctxt.error_spanned_by(s, format!("failed to parse request deserializer {:?}", s.value()))
})
.unwrap();
event_attrs.input = Some(input_type);
}
}
if name_value.path == EVENT_OUTPUT {
if let syn::Lit::Str(s) = &name_value.lit {
let output_type = parse_lit_str(s)
.map_err(|_| {
ctxt.error_spanned_by(s, format!("failed to parse response deserializer {:?}", s.value()))
})
.unwrap();
event_attrs.output = Some(output_type);
}
}
},
Meta(Path(word)) => {
if word == EVENT_IGNORE && attr.path == EVENT {
event_attrs.ignore = true;
}
},
Lit(s) => ctxt.error_spanned_by(s, "unexpected attribute"),
_ => ctxt.error_spanned_by(meta_item, "unexpected attribute"),
};
let attr_meta_items_info = variant_attrs
.iter()
.flat_map(|attr| match get_meta_items(ctxt, attr) {
Ok(items) => Some((attr, items)),
Err(_) => None,
})
.collect::<Vec<(&syn::Attribute, Vec<syn::NestedMeta>)>>();
for (attr, nested_metas) in attr_meta_items_info {
nested_metas
.iter()
.for_each(|meta_item| extract_event_attr(attr, meta_item))
}
// eprintln!("😁{:#?}", event_attrs);
event_attrs
}
pub fn get_meta_items(cx: &Ctxt, attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
if attr.path != PB_ATTRS && attr.path != EVENT {
return Ok(Vec::new());
}
// http://strymon.systems.ethz.ch/typename/syn/enum.Meta.html
match attr.parse_meta() {
Ok(List(meta)) => Ok(meta.nested.into_iter().collect()),
Ok(other) => {
cx.error_spanned_by(other, "expected #[pb(...)] or or #[event(...)]");
Err(())
},
Err(err) => {
cx.error_spanned_by(attr, "attribute must be str, e.g. #[pb(xx = \"xxx\")]");
cx.syn_error(err);
Err(())
},
}
}
fn parse_lit_into_expr_path(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::ExprPath, ()> {
let string = get_lit_str(cx, attr_name, lit)?;
parse_lit_str(string).map_err(|_| cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value())))
}
fn get_lit_str<'a>(cx: &Ctxt, attr_name: Symbol, lit: &'a syn::Lit) -> Result<&'a syn::LitStr, ()> {
if let syn::Lit::Str(lit) = lit {
Ok(lit)
} else {
cx.error_spanned_by(
lit,
format!(
"expected pb {} attribute to be a string: `{} = \"...\"`",
attr_name, attr_name
),
);
Err(())
}
}
fn parse_lit_into_ty(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::Type, ()> {
let string = get_lit_str(cx, attr_name, lit)?;
parse_lit_str(string).map_err(|_| {
cx.error_spanned_by(
lit,
format!("failed to parse type: {} = {:?}", attr_name, string.value()),
)
})
}
pub fn parse_lit_str<T>(s: &syn::LitStr) -> parse::Result<T>
where
T: Parse,
{
let tokens = spanned_tokens(s)?;
syn::parse2(tokens)
}
fn spanned_tokens(s: &syn::LitStr) -> parse::Result<TokenStream> {
let stream = syn::parse_str(&s.value())?;
Ok(respan_token_stream(stream, s.span()))
}
fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream {
stream.into_iter().map(|token| respan_token_tree(token, span)).collect()
}
fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree {
if let TokenTree::Group(g) = &mut token {
*g = Group::new(g.delimiter(), respan_token_stream(g.stream(), span));
}
token.set_span(span);
token
}
fn default_pb_type(ctxt: &Ctxt, ident: &syn::Ident) -> syn::Type {
let take_ident = format!("{}", ident.to_string());
let lit_str = syn::LitStr::new(&take_ident, ident.span());
if let Ok(tokens) = spanned_tokens(&lit_str) {
if let Ok(pb_struct_ty) = syn::parse2(tokens) {
return pb_struct_ty;
}
}
ctxt.error_spanned_by(ident, format!("❌ Can't find {} protobuf struct", take_ident));
panic!()
}
#[allow(dead_code)]
pub fn is_option(ty: &syn::Type) -> bool {
let path = match ungroup(ty) {
syn::Type::Path(ty) => &ty.path,
_ => {
return false;
},
};
let seg = match path.segments.last() {
Some(seg) => seg,
None => {
return false;
},
};
let args = match &seg.arguments {
syn::PathArguments::AngleBracketed(bracketed) => &bracketed.args,
_ => {
return false;
},
};
seg.ident == "Option" && args.len() == 1
}
#[allow(dead_code)]
pub fn ungroup(mut ty: &syn::Type) -> &syn::Type {
while let syn::Type::Group(group) = ty {
ty = &group.elem;
}
ty
}
struct BoolAttr<'c>(ASTAttr<'c, ()>);
impl<'c> BoolAttr<'c> {
fn none(cx: &'c Ctxt, name: Symbol) -> Self { BoolAttr(ASTAttr::none(cx, name)) }
fn set_true<A: ToTokens>(&mut self, obj: A) { self.0.set(obj, ()); }
fn get(&self) -> bool { self.0.value.is_some() }
}

View File

@ -1,42 +0,0 @@
use quote::ToTokens;
use std::{cell::RefCell, fmt::Display, thread};
use syn;
#[derive(Default)]
pub struct Ctxt {
errors: RefCell<Option<Vec<syn::Error>>>,
}
impl Ctxt {
pub fn new() -> Self {
Ctxt {
errors: RefCell::new(Some(Vec::new())),
}
}
pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) {
self.errors
.borrow_mut()
.as_mut()
.unwrap()
.push(syn::Error::new_spanned(obj.into_token_stream(), msg));
}
pub fn syn_error(&self, err: syn::Error) { self.errors.borrow_mut().as_mut().unwrap().push(err); }
pub fn check(self) -> Result<(), Vec<syn::Error>> {
let errors = self.errors.borrow_mut().take().unwrap();
match errors.len() {
0 => Ok(()),
_ => Err(errors),
}
}
}
impl Drop for Ctxt {
fn drop(&mut self) {
if !thread::panicking() && self.errors.borrow().is_some() {
panic!("forgot to check for errors");
}
}
}

View File

@ -1,38 +0,0 @@
use crate::ASTEnumAttrVariant;
pub struct EventASTContext {
pub event: syn::Ident,
pub event_ty: syn::Ident,
pub event_request_struct: syn::Ident,
pub event_input: Option<syn::Path>,
pub event_output: Option<syn::Path>,
pub event_error: String,
}
impl EventASTContext {
pub fn from(variant: &ASTEnumAttrVariant) -> EventASTContext {
let command_name = variant.enum_item_name.clone();
if command_name.is_empty() {
panic!("Invalid command name: {}", variant.enum_item_name);
}
let event = format_ident!("{}", &command_name);
let splits = command_name.split("_").collect::<Vec<&str>>();
let event_ty = format_ident!("{}", variant.enum_name);
let event_request_struct = format_ident!("{}Event", &splits.join(""));
let event_input = variant.event_input();
let event_output = variant.event_output();
let event_error = variant.event_error();
EventASTContext {
event,
event_ty,
event_request_struct,
event_input,
event_output,
event_error,
}
}
}

View File

@ -1,17 +0,0 @@
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;
mod ast;
mod attr;
mod ctxt;
pub mod event_ast;
pub mod symbol;
pub mod ty_ext;
pub use self::{symbol::*, ty_ext::*};
pub use ast::*;
pub use attr::*;
pub use ctxt::Ctxt;

View File

@ -1,41 +0,0 @@
use std::fmt::{self, Display};
use syn::{Ident, Path};
#[derive(Copy, Clone)]
pub struct Symbol(&'static str);
pub const PB_ATTRS: Symbol = Symbol("pb");
pub const SKIP: Symbol = Symbol("skip"); //#[pb(skip)]
pub const PB_INDEX: Symbol = Symbol("index"); //#[pb(index = "1")]
pub const PB_ONE_OF: Symbol = Symbol("one_of"); //#[pb(one_of)]
pub const DESERIALIZE_WITH: Symbol = Symbol("deserialize_with");
pub const SKIP_DESERIALIZING: Symbol = Symbol("skip_deserializing");
pub const SERIALIZE_WITH: Symbol = Symbol("serialize_with"); //#[pb(serialize_with = "...")]
pub const SKIP_SERIALIZING: Symbol = Symbol("skip_serializing"); //#[pb(skip_serializing)]
pub const PB_STRUCT: Symbol = Symbol("struct"); //#[pb(struct="some struct")]
pub const PB_ENUM: Symbol = Symbol("enum"); //#[pb(enum="some enum")]
pub const EVENT_INPUT: Symbol = Symbol("input");
pub const EVENT_OUTPUT: Symbol = Symbol("output");
pub const EVENT_IGNORE: Symbol = Symbol("ignore");
pub const EVENT: Symbol = Symbol("event");
pub const EVENT_ERR: Symbol = Symbol("event_err");
impl PartialEq<Symbol> for Ident {
fn eq(&self, word: &Symbol) -> bool { self == word.0 }
}
impl<'a> PartialEq<Symbol> for &'a Ident {
fn eq(&self, word: &Symbol) -> bool { *self == word.0 }
}
impl PartialEq<Symbol> for Path {
fn eq(&self, word: &Symbol) -> bool { self.is_ident(word.0) }
}
impl<'a> PartialEq<Symbol> for &'a Path {
fn eq(&self, word: &Symbol) -> bool { self.is_ident(word.0) }
}
impl Display for Symbol {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str(self.0) }
}

View File

@ -1,150 +0,0 @@
use crate::Ctxt;
use syn::{self, AngleBracketedGenericArguments, PathSegment};
#[derive(Eq, PartialEq, Debug)]
pub enum PrimitiveTy {
Map(MapInfo),
Vec,
Opt,
Other,
}
#[derive(Debug)]
pub struct TyInfo<'a> {
pub ident: &'a syn::Ident,
pub ty: &'a syn::Type,
pub primitive_ty: PrimitiveTy,
pub bracket_ty_info: Box<Option<TyInfo<'a>>>,
}
#[derive(Debug, Eq, PartialEq)]
pub struct MapInfo {
pub key: String,
pub value: String,
}
impl MapInfo {
fn new(key: String, value: String) -> Self { MapInfo { key, value } }
}
impl<'a> TyInfo<'a> {
#[allow(dead_code)]
pub fn bracketed_ident(&'a self) -> &'a syn::Ident {
match self.bracket_ty_info.as_ref() {
Some(b_ty) => b_ty.ident,
None => {
panic!()
},
}
}
}
pub fn parse_ty<'a>(ctxt: &Ctxt, ty: &'a syn::Type) -> Option<TyInfo<'a>> {
// Type -> TypePath -> Path -> PathSegment -> PathArguments ->
// AngleBracketedGenericArguments -> GenericArgument -> Type.
if let syn::Type::Path(ref p) = ty {
if p.path.segments.len() != 1 {
return None;
}
let seg = match p.path.segments.last() {
Some(seg) => seg,
None => return None,
};
let _is_option = seg.ident == "Option";
return if let syn::PathArguments::AngleBracketed(ref bracketed) = seg.arguments {
match seg.ident.to_string().as_ref() {
"HashMap" => generate_hashmap_ty_info(ctxt, ty, seg, bracketed),
"Vec" => generate_vec_ty_info(ctxt, seg, bracketed),
"Option" => generate_option_ty_info(ctxt, ty, seg, bracketed),
_ => {
panic!("Unsupported ty {}", seg.ident.to_string())
},
}
} else {
return Some(TyInfo {
ident: &seg.ident,
ty,
primitive_ty: PrimitiveTy::Other,
bracket_ty_info: Box::new(None),
});
};
}
ctxt.error_spanned_by(ty, format!("Unsupported inner type, get inner type fail"));
None
}
fn parse_bracketed(bracketed: &AngleBracketedGenericArguments) -> Vec<&syn::Type> {
bracketed
.args
.iter()
.flat_map(|arg| {
if let syn::GenericArgument::Type(ref ty_in_bracket) = arg {
Some(ty_in_bracket)
} else {
None
}
})
.collect::<Vec<&syn::Type>>()
}
pub fn generate_hashmap_ty_info<'a>(
ctxt: &Ctxt,
ty: &'a syn::Type,
path_segment: &'a PathSegment,
bracketed: &'a AngleBracketedGenericArguments,
) -> Option<TyInfo<'a>> {
// The args of map must greater than 2
if bracketed.args.len() != 2 {
return None;
}
let types = parse_bracketed(bracketed);
let key = parse_ty(ctxt, types[0]).unwrap().ident.to_string();
let value = parse_ty(ctxt, types[1]).unwrap().ident.to_string();
let bracket_ty_info = Box::new(parse_ty(ctxt, &types[1]));
return Some(TyInfo {
ident: &path_segment.ident,
ty,
primitive_ty: PrimitiveTy::Map(MapInfo::new(key, value)),
bracket_ty_info,
});
}
fn generate_option_ty_info<'a>(
ctxt: &Ctxt,
ty: &'a syn::Type,
path_segment: &'a PathSegment,
bracketed: &'a AngleBracketedGenericArguments,
) -> Option<TyInfo<'a>> {
assert_eq!(path_segment.ident.to_string(), "Option".to_string());
let types = parse_bracketed(bracketed);
let bracket_ty_info = Box::new(parse_ty(ctxt, &types[0]));
return Some(TyInfo {
ident: &path_segment.ident,
ty,
primitive_ty: PrimitiveTy::Opt,
bracket_ty_info,
});
}
fn generate_vec_ty_info<'a>(
ctxt: &Ctxt,
path_segment: &'a PathSegment,
bracketed: &'a AngleBracketedGenericArguments,
) -> Option<TyInfo<'a>> {
if bracketed.args.len() != 1 {
return None;
}
if let syn::GenericArgument::Type(ref bracketed_type) = bracketed.args.first().unwrap() {
let bracketed_ty_info = Box::new(parse_ty(ctxt, &bracketed_type));
return Some(TyInfo {
ident: &path_segment.ident,
ty: bracketed_type,
primitive_ty: PrimitiveTy::Vec,
bracket_ty_info: bracketed_ty_info,
});
}
return None;
}

View File

@ -1,2 +0,0 @@
/target
Cargo.lock

View File

@ -1,25 +0,0 @@
[package]
name = "flowy-derive"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
name = "flowy_derive"
[[test]]
name = "tests"
path = "tests/progress.rs"
[dependencies]
syn = { version = "1.0.60", features = ["extra-traits", "visit"] }
quote = "1.0"
proc-macro2 = "1.0"
flowy-ast = { path = "../flowy-ast" }
[dev-dependencies]
tokio = { version = "1", features = ["full"] }
trybuild = "1.0.40"
log = "0.4.11"

View File

@ -1,41 +0,0 @@
use proc_macro2::TokenStream;
// #[proc_macro_derive(DartEvent, attributes(event_ty))]
pub fn expand_enum_derive(_input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
Ok(TokenStream::default())
}
// use flowy_ast::{ASTContainer, Ctxt};
// use proc_macro2::TokenStream;
//
// // #[proc_macro_derive(DartEvent, attributes(event_ty))]
// pub fn expand_enum_derive(input: &syn::DeriveInput) -> Result<TokenStream,
// Vec<syn::Error>> { let ctxt = Ctxt::new();
// let cont = match ASTContainer::from_ast(&ctxt, input) {
// Some(cont) => cont,
// None => return Err(ctxt.check().unwrap_err()),
// };
//
// let enum_ident = &cont.ident;
// let pb_enum = cont.attrs.pb_enum_type().unwrap();
//
// let build_display_pb_enum = cont.data.all_idents().map(|i| {
// let a = format_ident!("{}", i.to_string());
// let token_stream: TokenStream = quote! {
// #enum_ident::#i => f.write_str(&#a)?,
// };
// token_stream
// });
//
// ctxt.check()?;
//
// Ok(quote! {
// impl std::fmt::Display for #enum_ident {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
// { match self {
// #(#build_display_pb_enum)*
// }
// }
// }
// })
// }

View File

@ -1,99 +0,0 @@
pub enum TypeCategory {
Array,
Map,
Str,
Protobuf,
Bytes,
Enum,
Opt,
Primitive,
}
// auto generate, do not edit
pub fn category_from_str(type_str: &str) -> TypeCategory {
match type_str {
"Vec" => TypeCategory::Array,
"HashMap" => TypeCategory::Map,
"u8" => TypeCategory::Bytes,
"String" => TypeCategory::Str,
"QueryAppRequest"
| "AppIdentifier"
| "CreateAppRequest"
| "ColorStyle"
| "CreateAppParams"
| "App"
| "RepeatedApp"
| "UpdateAppRequest"
| "UpdateAppParams"
| "UpdateWorkspaceRequest"
| "UpdateWorkspaceParams"
| "CurrentWorkspaceSetting"
| "CreateWorkspaceRequest"
| "CreateWorkspaceParams"
| "Workspace"
| "RepeatedWorkspace"
| "QueryWorkspaceRequest"
| "WorkspaceIdentifier"
| "TrashIdentifiers"
| "TrashIdentifier"
| "Trash"
| "RepeatedTrash"
| "UpdateViewRequest"
| "UpdateViewParams"
| "QueryViewRequest"
| "ViewIdentifier"
| "ViewIdentifiers"
| "CreateViewRequest"
| "CreateViewParams"
| "View"
| "RepeatedView"
| "ExportRequest"
| "ExportData"
| "CreateDocParams"
| "Doc"
| "UpdateDocParams"
| "DocDelta"
| "NewDocUser"
| "DocIdentifier"
| "RevId"
| "Revision"
| "RevisionRange"
| "WsDocumentData"
| "KeyValue"
| "WorkspaceError"
| "WsError"
| "WsMessage"
| "SignInRequest"
| "SignInParams"
| "SignInResponse"
| "SignUpRequest"
| "SignUpParams"
| "SignUpResponse"
| "UserToken"
| "UserProfile"
| "UpdateUserRequest"
| "UpdateUserParams"
| "DocError"
| "FFIRequest"
| "FFIResponse"
| "SubscribeObject"
| "UserError"
=> TypeCategory::Protobuf,
"TrashType"
| "ViewType"
| "ExportType"
| "ErrorCode"
| "RevType"
| "WsDataType"
| "WorkspaceEvent"
| "WorkspaceNotification"
| "WsModule"
| "DocObservable"
| "FFIStatusCode"
| "UserEvent"
| "UserNotification"
=> TypeCategory::Enum,
"Option" => TypeCategory::Opt,
_ => TypeCategory::Primitive,
}
}

View File

@ -1,3 +0,0 @@
mod derive_cache;
pub use derive_cache::*;

View File

@ -1,43 +0,0 @@
// https://docs.rs/syn/1.0.48/syn/struct.DeriveInput.html
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[macro_use]
extern crate quote;
mod dart_event;
mod derive_cache;
mod proto_buf;
// Inspired by https://serde.rs/attributes.html
#[proc_macro_derive(ProtoBuf, attributes(pb))]
pub fn derive_proto_buf(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
proto_buf::expand_derive(&input)
.unwrap_or_else(to_compile_errors)
.into()
}
#[proc_macro_derive(ProtoBuf_Enum, attributes(pb))]
pub fn derive_proto_buf_enum(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
proto_buf::expand_enum_derive(&input)
.unwrap_or_else(to_compile_errors)
.into()
}
#[proc_macro_derive(Flowy_Event, attributes(event, event_err))]
pub fn derive_dart_event(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
dart_event::expand_enum_derive(&input)
.unwrap_or_else(to_compile_errors)
.into()
}
fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
let compile_errors = errors.iter().map(syn::Error::to_compile_error);
quote!(#(#compile_errors)*)
}

View File

@ -1,221 +0,0 @@
use crate::{derive_cache::TypeCategory, proto_buf::util::*};
use flowy_ast::*;
use proc_macro2::{Span, TokenStream};
pub fn make_de_token_steam(ctxt: &Ctxt, ast: &ASTContainer) -> Option<TokenStream> {
let pb_ty = ast.attrs.pb_struct_type()?;
let struct_ident = &ast.ident;
let build_take_fields = ast
.data
.all_fields()
.filter(|f| !f.attrs.skip_deserializing())
.flat_map(|field| {
if let Some(func) = field.attrs.deserialize_with() {
let member = &field.member;
Some(quote! { o.#member=#struct_ident::#func(pb); })
} else if field.attrs.is_one_of() {
token_stream_for_one_of(ctxt, field)
} else {
token_stream_for_field(ctxt, &field.member, &field.ty, false)
}
});
let de_token_stream: TokenStream = quote! {
impl std::convert::TryFrom<bytes::Bytes> for #struct_ident {
type Error = ::protobuf::ProtobufError;
fn try_from(bytes: bytes::Bytes) -> Result<Self, Self::Error> {
let mut pb: crate::protobuf::#pb_ty = ::protobuf::Message::parse_from_bytes(&bytes)?;
#struct_ident::try_from(&mut pb)
}
}
impl std::convert::TryFrom<&mut crate::protobuf::#pb_ty> for #struct_ident {
type Error = ::protobuf::ProtobufError;
fn try_from(pb: &mut crate::protobuf::#pb_ty) -> Result<Self, Self::Error> {
let mut o = Self::default();
#(#build_take_fields)*
Ok(o)
}
}
}
.into();
Some(de_token_stream)
// None
}
fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream> {
let member = &field.member;
let ident = get_member_ident(ctxt, member)?;
let ty_info = parse_ty(ctxt, &field.ty)?;
let bracketed_ty_info = ty_info.bracket_ty_info.as_ref().as_ref();
let has_func = format_ident!("has_{}", ident.to_string());
// eprintln!("😁{:#?}", ty_info.primitive_ty);
// eprintln!("{:#?}", ty_info.bracket_ty_info);
match ident_category(bracketed_ty_info.unwrap().ident) {
TypeCategory::Enum => {
let get_func = format_ident!("get_{}", ident.to_string());
let ty = ty_info.ty;
Some(quote! {
if pb.#has_func() {
let enum_de_from_pb = #ty::try_from(&pb.#get_func()).unwrap();
o.#member = Some(enum_de_from_pb);
}
})
},
TypeCategory::Primitive => {
let get_func = format_ident!("get_{}", ident.to_string());
Some(quote! {
if pb.#has_func() {
o.#member=Some(pb.#get_func());
}
})
},
TypeCategory::Str => {
let take_func = format_ident!("take_{}", ident.to_string());
Some(quote! {
if pb.#has_func() {
o.#member=Some(pb.#take_func());
}
})
},
TypeCategory::Array => {
let take_func = format_ident!("take_{}", ident.to_string());
Some(quote! {
if pb.#has_func() {
o.#member=Some(pb.#take_func());
}
})
},
_ => {
let take_func = format_ident!("take_{}", ident.to_string());
let ty = bracketed_ty_info.unwrap().ty;
Some(quote! {
if pb.#has_func() {
let val = #ty::try_from(&mut pb.#take_func()).unwrap();
o.#member=Some(val);
}
})
},
}
}
fn token_stream_for_field(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type, is_option: bool) -> Option<TokenStream> {
let ident = get_member_ident(ctxt, member)?;
let ty_info = parse_ty(ctxt, ty)?;
match ident_category(ty_info.ident) {
TypeCategory::Array => {
assert_bracket_ty_is_some(ctxt, &ty_info);
token_stream_for_vec(ctxt, &member, &ty_info.bracket_ty_info.unwrap())
},
TypeCategory::Map => {
assert_bracket_ty_is_some(ctxt, &ty_info);
token_stream_for_map(ctxt, &member, &ty_info.bracket_ty_info.unwrap())
},
TypeCategory::Protobuf => {
// if the type wrapped by SingularPtrField, should call take first
let take = syn::Ident::new("take", Span::call_site());
// inner_type_ty would be the type of the field. (e.g value of AnyData)
let ty = ty_info.ty;
Some(quote! {
let some_value = pb.#member.#take();
if some_value.is_some() {
let struct_de_from_pb = #ty::try_from(&mut some_value.unwrap()).unwrap();
o.#member = struct_de_from_pb;
}
})
},
TypeCategory::Enum => {
let ty = ty_info.ty;
Some(quote! {
let enum_de_from_pb = #ty::try_from(&pb.#member).unwrap();
o.#member = enum_de_from_pb;
})
},
TypeCategory::Str => {
let take_ident = syn::Ident::new(&format!("take_{}", ident.to_string()), Span::call_site());
if is_option {
Some(quote! {
if pb.#member.is_empty() {
o.#member = None;
} else {
o.#member = Some(pb.#take_ident());
}
})
} else {
Some(quote! {
o.#member = pb.#take_ident();
})
}
},
TypeCategory::Opt => token_stream_for_field(ctxt, member, ty_info.bracket_ty_info.unwrap().ty, true),
TypeCategory::Primitive | TypeCategory::Bytes => {
// eprintln!("😄 #{:?}", &field.name().unwrap());
if is_option {
Some(quote! { o.#member = Some(pb.#member.clone()); })
} else {
Some(quote! { o.#member = pb.#member.clone(); })
}
},
}
}
fn token_stream_for_vec(ctxt: &Ctxt, member: &syn::Member, bracketed_type: &TyInfo) -> Option<TokenStream> {
let ident = get_member_ident(ctxt, member)?;
match ident_category(bracketed_type.ident) {
TypeCategory::Protobuf => {
let ty = bracketed_type.ty;
// Deserialize from pb struct of type vec, should call take_xx(), get the
// repeated_field and then calling the into_iter。
let take_ident = format_ident!("take_{}", ident.to_string());
Some(quote! {
o.#member = pb.#take_ident()
.into_iter()
.map(|mut m| #ty::try_from(&mut m).unwrap())
.collect();
})
},
TypeCategory::Bytes => {
// Vec<u8>
Some(quote! {
o.#member = pb.#member.clone();
})
},
_ => {
// String
let take_ident = format_ident!("take_{}", ident.to_string());
Some(quote! {
o.#member = pb.#take_ident().into_vec();
})
},
}
}
fn token_stream_for_map(ctxt: &Ctxt, member: &syn::Member, bracketed_type: &TyInfo) -> Option<TokenStream> {
let ident = get_member_ident(ctxt, member)?;
let take_ident = format_ident!("take_{}", ident.to_string());
let ty = bracketed_type.ty;
match ident_category(bracketed_type.ident) {
TypeCategory::Protobuf => Some(quote! {
let mut m: std::collections::HashMap<String, #ty> = std::collections::HashMap::new();
pb.#take_ident().into_iter().for_each(|(k,mut v)| {
m.insert(k.clone(), #ty::try_from(&mut v).unwrap());
});
o.#member = m;
}),
_ => Some(quote! {
let mut m: std::collections::HashMap<String, #ty> = std::collections::HashMap::new();
pb.#take_ident().into_iter().for_each(|(k,mut v)| {
m.insert(k.clone(), v);
});
o.#member = m;
}),
}
}

View File

@ -1,41 +0,0 @@
use flowy_ast::*;
use proc_macro2::TokenStream;
#[allow(dead_code)]
pub fn make_enum_token_stream(_ctxt: &Ctxt, cont: &ASTContainer) -> Option<TokenStream> {
let enum_ident = &cont.ident;
let pb_enum = cont.attrs.pb_enum_type()?;
let build_to_pb_enum = cont.data.all_idents().map(|i| {
let token_stream: TokenStream = quote! {
#enum_ident::#i => crate::protobuf::#pb_enum::#i,
};
token_stream
});
let build_from_pb_enum = cont.data.all_idents().map(|i| {
let token_stream: TokenStream = quote! {
crate::protobuf::#pb_enum::#i => #enum_ident::#i,
};
token_stream
});
Some(quote! {
impl std::convert::TryFrom<&crate::protobuf::#pb_enum> for #enum_ident {
type Error = String;
fn try_from(pb:&crate::protobuf::#pb_enum) -> Result<Self, Self::Error> {
Ok(match pb {
#(#build_from_pb_enum)*
})
}
}
impl std::convert::TryInto<crate::protobuf::#pb_enum> for #enum_ident {
type Error = String;
fn try_into(self) -> Result<crate::protobuf::#pb_enum, Self::Error> {
Ok(match self {
#(#build_to_pb_enum)*
})
}
}
})
}

View File

@ -1,79 +0,0 @@
mod deserialize;
mod enum_serde;
mod serialize;
mod util;
use crate::proto_buf::{
deserialize::make_de_token_steam,
enum_serde::make_enum_token_stream,
serialize::make_se_token_stream,
};
use flowy_ast::*;
use proc_macro2::TokenStream;
pub fn expand_derive(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
let ctxt = Ctxt::new();
let cont = match ASTContainer::from_ast(&ctxt, input) {
Some(cont) => cont,
None => return Err(ctxt.check().unwrap_err()),
};
let mut token_stream: TokenStream = TokenStream::default();
let de_token_stream = make_de_token_steam(&ctxt, &cont);
if de_token_stream.is_some() {
token_stream.extend(de_token_stream.unwrap());
}
let se_token_stream = make_se_token_stream(&ctxt, &cont);
if se_token_stream.is_some() {
token_stream.extend(se_token_stream.unwrap());
}
ctxt.check()?;
Ok(token_stream)
}
pub fn expand_enum_derive(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
let ctxt = Ctxt::new();
let cont = match ASTContainer::from_ast(&ctxt, input) {
Some(cont) => cont,
None => return Err(ctxt.check().unwrap_err()),
};
let mut token_stream: TokenStream = TokenStream::default();
let enum_token_stream = make_enum_token_stream(&ctxt, &cont);
if enum_token_stream.is_some() {
token_stream.extend(enum_token_stream.unwrap());
}
ctxt.check()?;
Ok(token_stream)
}
// #[macro_use]
// macro_rules! impl_try_for_primitive_type {
// ($target:ident) => {
// impl std::convert::TryFrom<&$target> for $target {
// type Error = String;
// fn try_from(val: &$target) -> Result<Self, Self::Error> {
// Ok(val.clone()) } }
//
// impl std::convert::TryInto<$target> for $target {
// type Error = String;
//
// fn try_into(self) -> Result<Self, Self::Error> { Ok(self) }
// }
// };
// }
//
// impl_try_for_primitive_type!(String);
// impl_try_for_primitive_type!(i64);
// impl_try_for_primitive_type!(i32);
// impl_try_for_primitive_type!(i16);
// impl_try_for_primitive_type!(u64);
// impl_try_for_primitive_type!(u32);
// impl_try_for_primitive_type!(u16);
// impl_try_for_primitive_type!(bool);
// impl_try_for_primitive_type!(f64);
// impl_try_for_primitive_type!(f32);

View File

@ -1,160 +0,0 @@
use crate::{
derive_cache::TypeCategory,
proto_buf::util::{get_member_ident, ident_category},
};
use flowy_ast::*;
use proc_macro2::TokenStream;
pub fn make_se_token_stream(ctxt: &Ctxt, ast: &ASTContainer) -> Option<TokenStream> {
let pb_ty = ast.attrs.pb_struct_type()?;
let struct_ident = &ast.ident;
let build_set_pb_fields = ast
.data
.all_fields()
.filter(|f| !f.attrs.skip_serializing())
.flat_map(|field| se_token_stream_for_field(&ctxt, &field, false));
let se_token_stream: TokenStream = quote! {
impl std::convert::TryInto<bytes::Bytes> for #struct_ident {
type Error = ::protobuf::ProtobufError;
fn try_into(self) -> Result<bytes::Bytes, Self::Error> {
use protobuf::Message;
let pb: crate::protobuf::#pb_ty = self.try_into()?;
let bytes = pb.write_to_bytes()?;
Ok(bytes::Bytes::from(bytes))
}
}
impl std::convert::TryInto<crate::protobuf::#pb_ty> for #struct_ident {
type Error = ::protobuf::ProtobufError;
fn try_into(self) -> Result<crate::protobuf::#pb_ty, Self::Error> {
let mut pb = crate::protobuf::#pb_ty::new();
#(#build_set_pb_fields)*
Ok(pb)
}
}
}
.into();
Some(se_token_stream)
}
fn se_token_stream_for_field(ctxt: &Ctxt, field: &ASTField, _take: bool) -> Option<TokenStream> {
if let Some(func) = &field.attrs.serialize_with() {
let member = &field.member;
Some(quote! { pb.#member=self.#func(); })
} else if field.attrs.is_one_of() {
token_stream_for_one_of(ctxt, field)
} else {
gen_token_stream(ctxt, &field.member, &field.ty, false)
}
}
fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream> {
let member = &field.member;
let ident = get_member_ident(ctxt, member)?;
let ty_info = parse_ty(ctxt, &field.ty)?;
let bracketed_ty_info = ty_info.bracket_ty_info.as_ref().as_ref();
let set_func = format_ident!("set_{}", ident.to_string());
match ident_category(bracketed_ty_info.unwrap().ident) {
TypeCategory::Protobuf => Some(quote! {
match self.#member {
Some(s) => { pb.#set_func(s.try_into().unwrap()) }
None => {}
}
}),
_ => Some(quote! {
match self.#member {
Some(ref s) => { pb.#set_func(s.clone()) }
None => {}
}
}),
}
}
fn gen_token_stream(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type, is_option: bool) -> Option<TokenStream> {
let ty_info = parse_ty(ctxt, ty)?;
match ident_category(ty_info.ident) {
TypeCategory::Array => token_stream_for_vec(ctxt, &member, &ty_info.ty),
TypeCategory::Map => token_stream_for_map(ctxt, &member, &ty_info.bracket_ty_info.unwrap().ty),
TypeCategory::Str => {
if is_option {
Some(quote! {
match self.#member {
Some(ref s) => { pb.#member = s.to_string().clone(); }
None => { pb.#member = String::new(); }
}
})
} else {
Some(quote! { pb.#member = self.#member.clone(); })
}
},
TypeCategory::Protobuf => {
Some(quote! { pb.#member = ::protobuf::SingularPtrField::some(self.#member.try_into().unwrap()); })
},
TypeCategory::Opt => gen_token_stream(ctxt, member, ty_info.bracket_ty_info.unwrap().ty, true),
TypeCategory::Enum => {
// let pb_enum_ident = format_ident!("{}", ty_info.ident.to_string());
// Some(quote! {
// flowy_protobuf::#pb_enum_ident::from_i32(self.#member.value()).unwrap();
// })
Some(quote! {
pb.#member = self.#member.try_into().unwrap();
})
},
_ => Some(quote! { pb.#member = self.#member; }),
}
}
// e.g. pub cells: Vec<CellData>, the memeber will be cells, ty would be Vec
fn token_stream_for_vec(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Option<TokenStream> {
let ty_info = parse_ty(ctxt, ty)?;
match ident_category(ty_info.ident) {
TypeCategory::Protobuf => Some(quote! {
pb.#member = ::protobuf::RepeatedField::from_vec(
self.#member
.into_iter()
.map(|m| m.try_into().unwrap())
.collect());
}),
TypeCategory::Bytes => Some(quote! { pb.#member = self.#member.clone(); }),
_ => Some(quote! {
pb.#member = ::protobuf::RepeatedField::from_vec(self.#member.clone());
}),
}
}
// e.g. pub cells: HashMap<xx, xx>
fn token_stream_for_map(ctxt: &Ctxt, member: &syn::Member, ty: &syn::Type) -> Option<TokenStream> {
// The key of the hashmap must be string
let flowy_protobuf = format_ident!("flowy_protobuf");
let ty_info = parse_ty(ctxt, ty)?;
match ident_category(ty_info.ident) {
TypeCategory::Protobuf => {
let value_type = ty_info.ident;
Some(quote! {
let mut m: std::collections::HashMap<String, #flowy_protobuf::#value_type> = std::collections::HashMap::new();
self.#member.iter().for_each(|(k,v)| {
m.insert(k.clone(), v.try_into().unwrap());
});
pb.#member = m;
})
},
_ => {
let value_type = ty_info.ident;
Some(quote! {
let mut m: std::collections::HashMap<String, #flowy_protobuf::#value_type> = std::collections::HashMap::new();
self.#member.iter().for_each(|(k,v)| {
m.insert(k.clone(), v.clone());
});
pb.#member = m;
})
},
}
}

View File

@ -1,22 +0,0 @@
use crate::derive_cache::*;
use flowy_ast::{Ctxt, TyInfo};
pub fn ident_category(ident: &syn::Ident) -> TypeCategory {
let ident_str: &str = &ident.to_string();
category_from_str(ident_str)
}
pub(crate) fn get_member_ident<'a>(ctxt: &Ctxt, member: &'a syn::Member) -> Option<&'a syn::Ident> {
if let syn::Member::Named(ref ident) = member {
Some(ident)
} else {
ctxt.error_spanned_by(member, format!("Unsupported member, shouldn't be self.0"));
None
}
}
pub fn assert_bracket_ty_is_some(ctxt: &Ctxt, ty_info: &TyInfo) {
if ty_info.bracket_ty_info.is_none() {
ctxt.error_spanned_by(ty_info.ty, format!("Invalid bracketed type when gen de token steam"));
}
}

View File

@ -1,5 +0,0 @@
#[tokio::test]
async fn tests() {
let _t = trybuild::TestCases::new();
// t.pass("tests/protobuf_enum.rs");
}

View File

@ -1,21 +0,0 @@
[package]
name = "flowy-document-infra"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lib-ot = { path = "../lib-ot" }
flowy-derive = { path = "../flowy-derive" }
protobuf = {version = "2.18.0"}
bytes = "1.0"
log = "0.4.14"
md5 = "0.7.0"
tokio = {version = "1", features = ["sync"]}
serde = { version = "1.0", features = ["derive"] }
tracing = { version = "0.1", features = ["log"] }
url = "2.2"
strum = "0.21"
strum_macros = "0.21"
chrono = "0.4.19"

View File

@ -1,3 +0,0 @@
proto_crates = ["src/entities"]
event_files = []

View File

@ -1 +0,0 @@
[{"insert":"\n👋 Welcome to AppFlowy!"},{"insert":"\n","attributes":{"header":1}},{"insert":"\nHere are the basics"},{"insert":"\n","attributes":{"header":2}},{"insert":"Click anywhere and just start typing"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"Highlight","attributes":{"background":"#fff2cd"}},{"insert":" any text, and use the menu at the bottom to "},{"insert":"style","attributes":{"italic":true}},{"insert":" "},{"insert":"your","attributes":{"bold":true}},{"insert":" "},{"insert":"writing","attributes":{"underline":true}},{"insert":" "},{"insert":"however","attributes":{"code":true}},{"insert":" "},{"insert":"you","attributes":{"strike":true}},{"insert":" "},{"insert":"like","attributes":{"background":"#e8e0ff"}},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"Click "},{"insert":"+ New Page","attributes":{"background":"#defff1","bold":true}},{"insert":" button at the bottom of your sidebar to add a new page"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"Click the "},{"insert":"'","attributes":{"background":"#defff1"}},{"insert":"+'","attributes":{"background":"#defff1","bold":true}},{"insert":" next to any page title in the sidebar to quickly add a new subpage"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"\nHave a question? "},{"insert":"\n","attributes":{"header":2}},{"insert":"Click the "},{"insert":"'?'","attributes":{"background":"#defff1","bold":true}},{"insert":" at the bottom right for help and support.\n\nLike AppFlowy? Follow us:"},{"insert":"\n","attributes":{"header":2}},{"insert":"Github: https://github.com/AppFlowy-IO/appflowy"},{"insert":"\n","attributes":{"blockquote":true}},{"insert":"Twitter: https://twitter.com/appflowy"},{"insert":"\n","attributes":{"blockquote":true}},{"insert":"Newsletter: https://www.appflowy.io/blog"},{"insert":"\n","attributes":{"blockquote":true}}]

View File

@ -1,10 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct ImageData {
image: String,
}
impl ToString for ImageData {
fn to_string(&self) -> String { self.image.clone() }
}

View File

@ -1,213 +0,0 @@
use crate::{
core::{
history::{History, UndoResult},
view::{View, RECORD_THRESHOLD},
},
errors::DocumentError,
user_default::doc_initial_delta,
};
use lib_ot::core::*;
use tokio::sync::mpsc;
pub trait CustomDocument {
fn init_delta() -> Delta;
}
pub struct PlainDoc();
impl CustomDocument for PlainDoc {
fn init_delta() -> Delta { Delta::new() }
}
pub struct FlowyDoc();
impl CustomDocument for FlowyDoc {
fn init_delta() -> Delta { doc_initial_delta() }
}
pub struct Document {
delta: Delta,
history: History,
view: View,
last_edit_time: usize,
notify: Option<mpsc::UnboundedSender<()>>,
}
impl Document {
pub fn new<C: CustomDocument>() -> Self { Self::from_delta(C::init_delta()) }
pub fn from_delta(delta: Delta) -> Self {
Document {
delta,
history: History::new(),
view: View::new(),
last_edit_time: 0,
notify: None,
}
}
pub fn from_json(json: &str) -> Result<Self, DocumentError> {
let delta = Delta::from_json(json)?;
Ok(Self::from_delta(delta))
}
pub fn to_json(&self) -> String { self.delta.to_json() }
pub fn to_bytes(&self) -> Vec<u8> { self.delta.clone().to_bytes().to_vec() }
pub fn to_plain_string(&self) -> String { self.delta.apply("").unwrap() }
pub fn delta(&self) -> &Delta { &self.delta }
pub fn set_notify(&mut self, notify: mpsc::UnboundedSender<()>) { self.notify = Some(notify); }
pub fn set_delta(&mut self, data: Delta) {
self.delta = data;
match &self.notify {
None => {},
Some(notify) => {
let _ = notify.send(());
},
}
}
pub fn compose_delta(&mut self, mut delta: Delta) -> Result<(), DocumentError> {
trim(&mut delta);
tracing::trace!("{} compose {}", &self.delta.to_json(), delta.to_json());
let mut composed_delta = self.delta.compose(&delta)?;
let mut undo_delta = delta.invert(&self.delta);
let now = chrono::Utc::now().timestamp_millis() as usize;
if now - self.last_edit_time < RECORD_THRESHOLD {
if let Some(last_delta) = self.history.undo() {
tracing::trace!("compose previous change");
tracing::trace!("current = {}", undo_delta);
tracing::trace!("previous = {}", last_delta);
undo_delta = undo_delta.compose(&last_delta)?;
}
} else {
self.last_edit_time = now;
}
tracing::trace!("👉 receive change undo: {}", undo_delta);
if !undo_delta.is_empty() {
self.history.record(undo_delta);
}
tracing::trace!("compose result: {}", composed_delta.to_json());
trim(&mut composed_delta);
self.set_delta(composed_delta);
Ok(())
}
pub fn insert<T: ToString>(&mut self, index: usize, data: T) -> Result<Delta, DocumentError> {
let interval = Interval::new(index, index);
let _ = validate_interval(&self.delta, &interval)?;
let text = data.to_string();
let delta = self.view.insert(&self.delta, &text, interval)?;
tracing::trace!("👉 receive change: {}", delta);
self.compose_delta(delta.clone())?;
Ok(delta)
}
pub fn delete(&mut self, interval: Interval) -> Result<Delta, DocumentError> {
let _ = validate_interval(&self.delta, &interval)?;
debug_assert_eq!(interval.is_empty(), false);
let delete = self.view.delete(&self.delta, interval)?;
if !delete.is_empty() {
tracing::trace!("👉 receive change: {}", delete);
let _ = self.compose_delta(delete.clone())?;
}
Ok(delete)
}
pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<Delta, DocumentError> {
let _ = validate_interval(&self.delta, &interval)?;
tracing::trace!("format with {} at {}", attribute, interval);
let format_delta = self.view.format(&self.delta, attribute.clone(), interval).unwrap();
tracing::trace!("👉 receive change: {}", format_delta);
self.compose_delta(format_delta.clone())?;
Ok(format_delta)
}
pub fn replace<T: ToString>(&mut self, interval: Interval, data: T) -> Result<Delta, DocumentError> {
let _ = validate_interval(&self.delta, &interval)?;
let mut delta = Delta::default();
let text = data.to_string();
if !text.is_empty() {
delta = self.view.insert(&self.delta, &text, interval)?;
tracing::trace!("👉 receive change: {}", delta);
self.compose_delta(delta.clone())?;
}
if !interval.is_empty() {
let delete = self.delete(interval)?;
delta = delta.compose(&delete)?;
}
Ok(delta)
}
pub fn can_undo(&self) -> bool { self.history.can_undo() }
pub fn can_redo(&self) -> bool { self.history.can_redo() }
pub fn undo(&mut self) -> Result<UndoResult, DocumentError> {
match self.history.undo() {
None => Err(DocumentError::undo().context("Undo stack is empty")),
Some(undo_delta) => {
let (new_delta, inverted_delta) = self.invert(&undo_delta)?;
let result = UndoResult::success(new_delta.target_len as usize);
self.set_delta(new_delta);
self.history.add_redo(inverted_delta);
Ok(result)
},
}
}
pub fn redo(&mut self) -> Result<UndoResult, DocumentError> {
match self.history.redo() {
None => Err(DocumentError::redo()),
Some(redo_delta) => {
let (new_delta, inverted_delta) = self.invert(&redo_delta)?;
let result = UndoResult::success(new_delta.target_len as usize);
self.set_delta(new_delta);
self.history.add_undo(inverted_delta);
Ok(result)
},
}
}
}
impl Document {
fn invert(&self, delta: &Delta) -> Result<(Delta, Delta), DocumentError> {
// c = a.compose(b)
// d = b.invert(a)
// a = c.compose(d)
tracing::trace!("Invert {}", delta);
let new_delta = self.delta.compose(delta)?;
let inverted_delta = delta.invert(&self.delta);
Ok((new_delta, inverted_delta))
}
}
fn validate_interval(delta: &Delta, interval: &Interval) -> Result<(), DocumentError> {
if delta.target_len < interval.end {
log::error!("{:?} out of bounds. should 0..{}", interval, delta.target_len);
return Err(DocumentError::out_of_bound());
}
Ok(())
}
/// Removes trailing retain operation with empty attributes, if present.
pub fn trim(delta: &mut Delta) {
if let Some(last) = delta.ops.last() {
if last.is_retain() && last.is_plain() {
delta.ops.pop();
}
}
}

View File

@ -1,16 +0,0 @@
use crate::core::extensions::DeleteExt;
use lib_ot::core::{Delta, DeltaBuilder, Interval};
pub struct DefaultDelete {}
impl DeleteExt for DefaultDelete {
fn ext_name(&self) -> &str { "DefaultDelete" }
fn apply(&self, _delta: &Delta, interval: Interval) -> Option<Delta> {
Some(
DeltaBuilder::new()
.retain(interval.start)
.delete(interval.size())
.build(),
)
}
}

View File

@ -1,5 +0,0 @@
mod default_delete;
mod preserve_line_format_merge;
pub use default_delete::*;
pub use preserve_line_format_merge::*;

View File

@ -1,57 +0,0 @@
use crate::{core::extensions::DeleteExt, util::is_newline};
use lib_ot::core::{plain_attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, NEW_LINE};
pub struct PreserveLineFormatOnMerge {}
impl DeleteExt for PreserveLineFormatOnMerge {
fn ext_name(&self) -> &str { "PreserveLineFormatOnMerge" }
fn apply(&self, delta: &Delta, interval: Interval) -> Option<Delta> {
if interval.is_empty() {
return None;
}
// seek to the interval start pos. e.g. You backspace enter pos
let mut iter = DeltaIter::from_offset(delta, interval.start);
// op will be the "\n"
let newline_op = iter.next_op_with_len(1)?;
if !is_newline(newline_op.get_data()) {
return None;
}
iter.seek::<CharMetric>(interval.size() - 1);
let mut new_delta = DeltaBuilder::new()
.retain(interval.start)
.delete(interval.size())
.build();
while iter.has_next() {
match iter.next() {
None => log::error!("op must be not None when has_next() return true"),
Some(op) => {
//
match op.get_data().find(NEW_LINE) {
None => {
new_delta.retain(op.len(), plain_attributes());
continue;
},
Some(line_break) => {
let mut attributes = op.get_attributes();
attributes.mark_all_as_removed_except(None);
if newline_op.has_attribute() {
attributes.extend(newline_op.get_attributes());
}
new_delta.retain(line_break, plain_attributes());
new_delta.retain(1, attributes);
break;
},
}
},
}
}
Some(new_delta)
}
}

View File

@ -1,48 +0,0 @@
// use crate::{
// client::extensions::FormatExt,
// core::{Attribute, AttributeKey, Delta, DeltaBuilder, DeltaIter,
// Interval}, };
//
// pub struct FormatLinkAtCaretPositionExt {}
//
// impl FormatExt for FormatLinkAtCaretPositionExt {
// fn ext_name(&self) -> &str {
// std::any::type_name::<FormatLinkAtCaretPositionExt>() }
//
// fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute)
// -> Option<Delta> { if attribute.key != AttributeKey::Link ||
// interval.size() != 0 { return None;
// }
//
// let mut iter = DeltaIter::from_offset(delta, interval.start);
// let (before, after) = (iter.next_op_with_len(interval.size()),
// iter.next_op()); let mut start = interval.end;
// let mut retain = 0;
//
// if let Some(before) = before {
// if before.contain_attribute(attribute) {
// start -= before.len();
// retain += before.len();
// }
// }
//
// if let Some(after) = after {
// if after.contain_attribute(attribute) {
// if retain != 0 {
// retain += after.len();
// }
// }
// }
//
// if retain == 0 {
// return None;
// }
//
// Some(
// DeltaBuilder::new()
// .retain(start)
// .retain_with_attributes(retain, (attribute.clone()).into())
// .build(),
// )
// }
// }

View File

@ -1,37 +0,0 @@
use crate::util::find_newline;
use lib_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, Operation};
pub(crate) fn line_break(op: &Operation, attribute: &Attribute, scope: AttributeScope) -> Delta {
let mut new_delta = Delta::new();
let mut start = 0;
let end = op.len();
let mut s = op.get_data();
while let Some(line_break) = find_newline(s) {
match scope {
AttributeScope::Inline => {
new_delta.retain(line_break - start, attribute.clone().into());
new_delta.retain(1, plain_attributes());
},
AttributeScope::Block => {
new_delta.retain(line_break - start, plain_attributes());
new_delta.retain(1, attribute.clone().into());
},
_ => {
log::error!("Unsupported parser line break for {:?}", scope);
},
}
start = line_break + 1;
s = &s[start..s.len()];
}
if start < end {
match scope {
AttributeScope::Inline => new_delta.retain(end - start, attribute.clone().into()),
AttributeScope::Block => new_delta.retain(end - start, plain_attributes()),
_ => log::error!("Unsupported parser line break for {:?}", scope),
}
}
new_delta
}

View File

@ -1,8 +0,0 @@
mod format_at_position;
mod helper;
mod resolve_block_format;
mod resolve_inline_format;
pub use format_at_position::*;
pub use resolve_block_format::*;
pub use resolve_inline_format::*;

View File

@ -1,48 +0,0 @@
use crate::{
core::extensions::{format::helper::line_break, FormatExt},
util::find_newline,
};
use lib_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval};
pub struct ResolveBlockFormat {}
impl FormatExt for ResolveBlockFormat {
fn ext_name(&self) -> &str { std::any::type_name::<ResolveBlockFormat>() }
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
if attribute.scope != AttributeScope::Block {
return None;
}
let mut new_delta = DeltaBuilder::new().retain(interval.start).build();
let mut iter = DeltaIter::from_offset(delta, interval.start);
let mut start = 0;
let end = interval.size();
while start < end && iter.has_next() {
let next_op = iter.next_op_with_len(end - start).unwrap();
match find_newline(next_op.get_data()) {
None => new_delta.retain(next_op.len(), plain_attributes()),
Some(_) => {
let tmp_delta = line_break(&next_op, attribute, AttributeScope::Block);
new_delta.extend(tmp_delta);
},
}
start += next_op.len();
}
while iter.has_next() {
let op = iter.next_op().expect("Unexpected None, iter.has_next() must return op");
match find_newline(op.get_data()) {
None => new_delta.retain(op.len(), plain_attributes()),
Some(line_break) => {
new_delta.retain(line_break, plain_attributes());
new_delta.retain(1, attribute.clone().into());
break;
},
}
}
Some(new_delta)
}
}

View File

@ -1,35 +0,0 @@
use crate::{
core::extensions::{format::helper::line_break, FormatExt},
util::find_newline,
};
use lib_ot::core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval};
pub struct ResolveInlineFormat {}
impl FormatExt for ResolveInlineFormat {
fn ext_name(&self) -> &str { std::any::type_name::<ResolveInlineFormat>() }
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
if attribute.scope != AttributeScope::Inline {
return None;
}
let mut new_delta = DeltaBuilder::new().retain(interval.start).build();
let mut iter = DeltaIter::from_offset(delta, interval.start);
let mut start = 0;
let end = interval.size();
while start < end && iter.has_next() {
let next_op = iter.next_op_with_len(end - start).unwrap();
match find_newline(next_op.get_data()) {
None => new_delta.retain(next_op.len(), attribute.clone().into()),
Some(_) => {
let tmp_delta = line_break(&next_op, attribute, AttributeScope::Inline);
new_delta.extend(tmp_delta);
},
}
start += next_op.len();
}
Some(new_delta)
}
}

View File

@ -1,51 +0,0 @@
use crate::{core::extensions::InsertExt, util::is_newline};
use lib_ot::core::{attributes_except_header, is_empty_line_at_index, AttributeKey, Delta, DeltaBuilder, DeltaIter};
pub struct AutoExitBlock {}
impl InsertExt for AutoExitBlock {
fn ext_name(&self) -> &str { std::any::type_name::<AutoExitBlock>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
// Auto exit block will be triggered by enter two new lines
if !is_newline(text) {
return None;
}
if !is_empty_line_at_index(delta, index) {
return None;
}
let mut iter = DeltaIter::from_offset(delta, index);
let next = iter.next_op()?;
let mut attributes = next.get_attributes();
let block_attributes = attributes_except_header(&next);
if block_attributes.is_empty() {
return None;
}
if next.len() > 1 {
return None;
}
match iter.next_op_with_newline() {
None => {},
Some((newline_op, _)) => {
let newline_attributes = attributes_except_header(&newline_op);
if block_attributes == newline_attributes {
return None;
}
},
}
attributes.mark_all_as_removed_except(Some(AttributeKey::Header));
Some(
DeltaBuilder::new()
.retain(index + replace_len)
.retain_with_attributes(1, attributes)
.build(),
)
}
}

View File

@ -1,82 +0,0 @@
use crate::{core::extensions::InsertExt, util::is_whitespace};
use lib_ot::core::{count_utf16_code_units, plain_attributes, Attribute, Attributes, Delta, DeltaBuilder, DeltaIter};
use std::cmp::min;
use url::Url;
pub struct AutoFormatExt {}
impl InsertExt for AutoFormatExt {
fn ext_name(&self) -> &str { std::any::type_name::<AutoFormatExt>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
// enter whitespace to trigger auto format
if !is_whitespace(text) {
return None;
}
let mut iter = DeltaIter::new(delta);
if let Some(prev) = iter.next_op_with_len(index) {
match AutoFormat::parse(prev.get_data()) {
None => {},
Some(formatter) => {
let mut new_attributes = prev.get_attributes();
// format_len should not greater than index. The url crate will add "/" to the
// end of input string that causes the format_len greater than the input string
let format_len = min(index, formatter.format_len());
let format_attributes = formatter.to_attributes();
format_attributes.iter().for_each(|(k, v)| {
if !new_attributes.contains_key(k) {
new_attributes.insert(k.clone(), v.clone());
}
});
let next_attributes = match iter.next_op() {
None => plain_attributes(),
Some(op) => op.get_attributes(),
};
return Some(
DeltaBuilder::new()
.retain(index + replace_len - min(index, format_len))
.retain_with_attributes(format_len, format_attributes)
.insert_with_attributes(text, next_attributes)
.build(),
);
},
}
}
None
}
}
pub enum AutoFormatter {
Url(Url),
}
impl AutoFormatter {
pub fn to_attributes(&self) -> Attributes {
match self {
AutoFormatter::Url(url) => Attribute::Link(url.as_str()).into(),
}
}
pub fn format_len(&self) -> usize {
let s = match self {
AutoFormatter::Url(url) => url.to_string(),
};
count_utf16_code_units(&s)
}
}
pub struct AutoFormat {}
impl AutoFormat {
fn parse(s: &str) -> Option<AutoFormatter> {
if let Ok(url) = Url::parse(s) {
return Some(AutoFormatter::Url(url));
}
None
}
}

View File

@ -1,35 +0,0 @@
use crate::core::extensions::InsertExt;
use lib_ot::core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE};
pub struct DefaultInsertAttribute {}
impl InsertExt for DefaultInsertAttribute {
fn ext_name(&self) -> &str { std::any::type_name::<DefaultInsertAttribute>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
let iter = DeltaIter::new(delta);
let mut attributes = Attributes::new();
// Enable each line split by "\n" remains the block attributes. for example:
// insert "\n" to "123456" at index 3
//
// [{"insert":"123"},{"insert":"\n","attributes":{"header":1}},
// {"insert":"456"},{"insert":"\n","attributes":{"header":1}}]
if text.ends_with(NEW_LINE) {
match iter.last() {
None => {},
Some(op) => {
if op.get_attributes().contains_key(&AttributeKey::Header) {
attributes.extend(op.get_attributes());
}
},
}
}
Some(
DeltaBuilder::new()
.retain(index + replace_len)
.insert_with_attributes(text, attributes)
.build(),
)
}
}

View File

@ -1,29 +0,0 @@
use crate::core::extensions::InsertExt;
pub use auto_exit_block::*;
pub use auto_format::*;
pub use default_insert::*;
use lib_ot::core::Delta;
pub use preserve_block_format::*;
pub use preserve_inline_format::*;
pub use reset_format_on_new_line::*;
mod auto_exit_block;
mod auto_format;
mod default_insert;
mod preserve_block_format;
mod preserve_inline_format;
mod reset_format_on_new_line;
pub struct InsertEmbedsExt {}
impl InsertExt for InsertEmbedsExt {
fn ext_name(&self) -> &str { "InsertEmbedsExt" }
fn apply(&self, _delta: &Delta, _replace_len: usize, _text: &str, _index: usize) -> Option<Delta> { None }
}
pub struct ForceNewlineForInsertsAroundEmbedExt {}
impl InsertExt for ForceNewlineForInsertsAroundEmbedExt {
fn ext_name(&self) -> &str { "ForceNewlineForInsertsAroundEmbedExt" }
fn apply(&self, _delta: &Delta, _replace_len: usize, _text: &str, _index: usize) -> Option<Delta> { None }
}

View File

@ -1,66 +0,0 @@
use crate::{core::extensions::InsertExt, util::is_newline};
use lib_ot::core::{
attributes_except_header,
plain_attributes,
Attribute,
AttributeKey,
Attributes,
Delta,
DeltaBuilder,
DeltaIter,
NEW_LINE,
};
pub struct PreserveBlockFormatOnInsert {}
impl InsertExt for PreserveBlockFormatOnInsert {
fn ext_name(&self) -> &str { std::any::type_name::<PreserveBlockFormatOnInsert>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
if !is_newline(text) {
return None;
}
let mut iter = DeltaIter::from_offset(delta, index);
match iter.next_op_with_newline() {
None => {},
Some((newline_op, offset)) => {
let newline_attributes = newline_op.get_attributes();
let block_attributes = attributes_except_header(&newline_op);
if block_attributes.is_empty() {
return None;
}
let mut reset_attribute = Attributes::new();
if newline_attributes.contains_key(&AttributeKey::Header) {
reset_attribute.add(Attribute::Header(1));
}
let lines: Vec<_> = text.split(NEW_LINE).collect();
let mut new_delta = DeltaBuilder::new().retain(index + replace_len).build();
lines.iter().enumerate().for_each(|(i, line)| {
if !line.is_empty() {
new_delta.insert(line, plain_attributes());
}
if i == 0 {
new_delta.insert(NEW_LINE, newline_attributes.clone());
} else if i < lines.len() - 1 {
new_delta.insert(NEW_LINE, block_attributes.clone());
} else {
// do nothing
}
});
if !reset_attribute.is_empty() {
new_delta.retain(offset, plain_attributes());
let len = newline_op.get_data().find(NEW_LINE).unwrap();
new_delta.retain(len, plain_attributes());
new_delta.retain(1, reset_attribute.clone());
}
return Some(new_delta);
},
}
None
}
}

View File

@ -1,90 +0,0 @@
use crate::{
core::extensions::InsertExt,
util::{contain_newline, is_newline},
};
use lib_ot::core::{plain_attributes, AttributeKey, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE};
pub struct PreserveInlineFormat {}
impl InsertExt for PreserveInlineFormat {
fn ext_name(&self) -> &str { std::any::type_name::<PreserveInlineFormat>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
if contain_newline(text) {
return None;
}
let mut iter = DeltaIter::new(delta);
let prev = iter.next_op_with_len(index)?;
if OpNewline::parse(&prev).is_contain() {
return None;
}
let mut attributes = prev.get_attributes();
if attributes.is_empty() || !attributes.contains_key(&AttributeKey::Link) {
return Some(
DeltaBuilder::new()
.retain(index + replace_len)
.insert_with_attributes(text, attributes)
.build(),
);
}
let next = iter.next_op();
match &next {
None => attributes = plain_attributes(),
Some(next) => {
if OpNewline::parse(&next).is_equal() {
attributes = plain_attributes();
}
},
}
let new_delta = DeltaBuilder::new()
.retain(index + replace_len)
.insert_with_attributes(text, attributes)
.build();
return Some(new_delta);
}
}
pub struct PreserveLineFormatOnSplit {}
impl InsertExt for PreserveLineFormatOnSplit {
fn ext_name(&self) -> &str { std::any::type_name::<PreserveLineFormatOnSplit>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
if !is_newline(text) {
return None;
}
let mut iter = DeltaIter::new(delta);
let prev = iter.next_op_with_len(index)?;
if OpNewline::parse(&prev).is_end() {
return None;
}
let next = iter.next_op()?;
let newline_status = OpNewline::parse(&next);
if newline_status.is_end() {
return None;
}
let mut new_delta = Delta::new();
new_delta.retain(index + replace_len, plain_attributes());
if newline_status.is_contain() {
debug_assert!(next.has_attribute() == false);
new_delta.insert(NEW_LINE, plain_attributes());
return Some(new_delta);
}
match iter.next_op_with_newline() {
None => {},
Some((newline_op, _)) => {
new_delta.insert(NEW_LINE, newline_op.get_attributes());
},
}
Some(new_delta)
}
}

View File

@ -1,35 +0,0 @@
use crate::{core::extensions::InsertExt, util::is_newline};
use lib_ot::core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE};
pub struct ResetLineFormatOnNewLine {}
impl InsertExt for ResetLineFormatOnNewLine {
fn ext_name(&self) -> &str { std::any::type_name::<ResetLineFormatOnNewLine>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
if !is_newline(text) {
return None;
}
let mut iter = DeltaIter::new(delta);
iter.seek::<CharMetric>(index);
let next_op = iter.next_op()?;
if !next_op.get_data().starts_with(NEW_LINE) {
return None;
}
let mut reset_attribute = Attributes::new();
if next_op.get_attributes().contains_key(&AttributeKey::Header) {
reset_attribute.delete(&AttributeKey::Header);
}
let len = index + replace_len;
Some(
DeltaBuilder::new()
.retain(len)
.insert_with_attributes(NEW_LINE, next_op.get_attributes())
.retain_with_attributes(1, reset_attribute)
.trim()
.build(),
)
}
}

View File

@ -1,28 +0,0 @@
pub use delete::*;
pub use format::*;
pub use insert::*;
use lib_ot::core::{Attribute, Delta, Interval};
mod delete;
mod format;
mod insert;
pub type InsertExtension = Box<dyn InsertExt + Send + Sync>;
pub type FormatExtension = Box<dyn FormatExt + Send + Sync>;
pub type DeleteExtension = Box<dyn DeleteExt + Send + Sync>;
pub trait InsertExt {
fn ext_name(&self) -> &str;
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta>;
}
pub trait FormatExt {
fn ext_name(&self) -> &str;
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta>;
}
pub trait DeleteExt {
fn ext_name(&self) -> &str;
fn apply(&self, delta: &Delta, interval: Interval) -> Option<Delta>;
}

View File

@ -1,72 +0,0 @@
use lib_ot::core::Delta;
const MAX_UNDOS: usize = 20;
#[derive(Debug, Clone)]
pub struct UndoResult {
success: bool,
len: usize,
}
impl UndoResult {
pub fn fail() -> Self { UndoResult { success: false, len: 0 } }
pub fn success(len: usize) -> Self { UndoResult { success: true, len } }
}
#[derive(Debug, Clone)]
pub struct History {
cur_undo: usize,
undos: Vec<Delta>,
redoes: Vec<Delta>,
capacity: usize,
}
impl History {
pub fn new() -> Self {
History {
cur_undo: 1,
undos: Vec::new(),
redoes: Vec::new(),
capacity: MAX_UNDOS,
}
}
pub fn can_undo(&self) -> bool { !self.undos.is_empty() }
pub fn can_redo(&self) -> bool { !self.redoes.is_empty() }
pub fn add_undo(&mut self, delta: Delta) { self.undos.push(delta); }
pub fn add_redo(&mut self, delta: Delta) { self.redoes.push(delta); }
pub fn record(&mut self, delta: Delta) {
if delta.ops.is_empty() {
return;
}
self.redoes.clear();
self.add_undo(delta);
if self.undos.len() > self.capacity {
self.undos.remove(0);
}
}
pub fn undo(&mut self) -> Option<Delta> {
if !self.can_undo() {
return None;
}
let delta = self.undos.pop().unwrap();
Some(delta)
}
pub fn redo(&mut self) -> Option<Delta> {
if !self.can_redo() {
return None;
}
let delta = self.redoes.pop().unwrap();
Some(delta)
}
}

View File

@ -1,8 +0,0 @@
mod data;
mod document;
mod extensions;
pub mod history;
mod view;
pub use document::*;
pub use view::RECORD_THRESHOLD;

View File

@ -1,100 +0,0 @@
use crate::core::extensions::*;
use lib_ot::{
core::{trim, Attribute, Delta, Interval},
errors::{ErrorBuilder, OTError, OTErrorCode},
};
pub const RECORD_THRESHOLD: usize = 400; // in milliseconds
pub struct View {
insert_exts: Vec<InsertExtension>,
format_exts: Vec<FormatExtension>,
delete_exts: Vec<DeleteExtension>,
}
impl View {
pub(crate) fn new() -> Self {
Self {
insert_exts: construct_insert_exts(),
format_exts: construct_format_exts(),
delete_exts: construct_delete_exts(),
}
}
pub(crate) fn insert(&self, delta: &Delta, text: &str, interval: Interval) -> Result<Delta, OTError> {
let mut new_delta = None;
for ext in &self.insert_exts {
if let Some(mut delta) = ext.apply(delta, interval.size(), text, interval.start) {
trim(&mut delta);
tracing::trace!("[{}]: applied, delta: {}", ext.ext_name(), delta);
new_delta = Some(delta);
break;
}
}
match new_delta {
None => Err(ErrorBuilder::new(OTErrorCode::ApplyInsertFail).build()),
Some(new_delta) => Ok(new_delta),
}
}
pub(crate) fn delete(&self, delta: &Delta, interval: Interval) -> Result<Delta, OTError> {
let mut new_delta = None;
for ext in &self.delete_exts {
if let Some(mut delta) = ext.apply(delta, interval) {
trim(&mut delta);
tracing::trace!("[{}]: applied, delta: {}", ext.ext_name(), delta);
new_delta = Some(delta);
break;
}
}
match new_delta {
None => Err(ErrorBuilder::new(OTErrorCode::ApplyDeleteFail).build()),
Some(new_delta) => Ok(new_delta),
}
}
pub(crate) fn format(&self, delta: &Delta, attribute: Attribute, interval: Interval) -> Result<Delta, OTError> {
let mut new_delta = None;
for ext in &self.format_exts {
if let Some(mut delta) = ext.apply(delta, interval, &attribute) {
trim(&mut delta);
tracing::trace!("[{}]: applied, delta: {}", ext.ext_name(), delta);
new_delta = Some(delta);
break;
}
}
match new_delta {
None => Err(ErrorBuilder::new(OTErrorCode::ApplyFormatFail).build()),
Some(new_delta) => Ok(new_delta),
}
}
}
fn construct_insert_exts() -> Vec<InsertExtension> {
vec![
Box::new(InsertEmbedsExt {}),
Box::new(ForceNewlineForInsertsAroundEmbedExt {}),
Box::new(AutoExitBlock {}),
Box::new(PreserveBlockFormatOnInsert {}),
Box::new(PreserveLineFormatOnSplit {}),
Box::new(ResetLineFormatOnNewLine {}),
Box::new(AutoFormatExt {}),
Box::new(PreserveInlineFormat {}),
Box::new(DefaultInsertAttribute {}),
]
}
fn construct_format_exts() -> Vec<FormatExtension> {
vec![
// Box::new(FormatLinkAtCaretPositionExt {}),
Box::new(ResolveBlockFormat {}),
Box::new(ResolveInlineFormat {}),
]
}
fn construct_delete_exts() -> Vec<DeleteExtension> {
vec![Box::new(PreserveLineFormatOnMerge {}), Box::new(DefaultDelete {})]
}

View File

@ -1,93 +0,0 @@
use flowy_derive::ProtoBuf;
use lib_ot::{core::Delta, errors::OTError};
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct CreateDocParams {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub data: String,
}
impl CreateDocParams {
pub fn new(id: &str, data: String) -> Self {
Self {
id: id.to_owned(),
data,
}
}
}
#[derive(ProtoBuf, Default, Debug, Clone, Eq, PartialEq)]
pub struct Doc {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub data: String,
#[pb(index = 3)]
pub rev_id: i64,
#[pb(index = 4)]
pub base_rev_id: i64,
}
impl Doc {
pub fn delta(&self) -> Result<Delta, OTError> {
let delta = Delta::from_bytes(&self.data)?;
Ok(delta)
}
}
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct UpdateDocParams {
#[pb(index = 1)]
pub doc_id: String,
#[pb(index = 2)]
pub data: String,
#[pb(index = 3)]
pub rev_id: i64,
}
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct DocDelta {
#[pb(index = 1)]
pub doc_id: String,
#[pb(index = 2)]
pub data: String, // Delta
}
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct NewDocUser {
#[pb(index = 1)]
pub user_id: String,
#[pb(index = 2)]
pub rev_id: i64,
#[pb(index = 3)]
pub doc_id: String,
}
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct DocIdentifier {
#[pb(index = 1)]
pub doc_id: String,
}
impl std::convert::From<String> for DocIdentifier {
fn from(doc_id: String) -> Self { DocIdentifier { doc_id } }
}
impl std::convert::From<&String> for DocIdentifier {
fn from(doc_id: &String) -> Self {
DocIdentifier {
doc_id: doc_id.to_owned(),
}
}
}

View File

@ -1,6 +0,0 @@
mod doc;
pub mod parser;
mod revision;
pub use doc::*;
pub use revision::*;

View File

@ -1,16 +0,0 @@
#[derive(Debug)]
pub struct DocId(pub String);
impl DocId {
pub fn parse(s: String) -> Result<DocId, String> {
if s.trim().is_empty() {
return Err(format!("Doc id can not be empty or whitespace"));
}
Ok(Self(s))
}
}
impl AsRef<str> for DocId {
fn as_ref(&self) -> &str { &self.0 }
}

View File

@ -1,3 +0,0 @@
mod doc_id;
pub use doc_id::*;

View File

@ -1,168 +0,0 @@
use crate::{entities::doc::Doc, util::md5};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use lib_ot::core::Delta;
use std::{fmt::Formatter, ops::RangeInclusive};
#[derive(Debug, ProtoBuf_Enum, Clone, Eq, PartialEq)]
pub enum RevType {
Local = 0,
Remote = 1,
}
impl RevType {
pub fn is_local(&self) -> bool { self == &RevType::Local }
}
impl std::default::Default for RevType {
fn default() -> Self { RevType::Local }
}
// [[i64 to bytes]]
// use byteorder::{BigEndian, ReadBytesExt};
// use std::{io::Cursor};
// impl std::convert::TryFrom<Bytes> for RevId {
// type Error = DocError;
//
// fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
// // let mut wtr = vec![];
// // let _ = wtr.write_i64::<BigEndian>(revision.rev_id);
//
// let mut rdr = Cursor::new(bytes);
// match rdr.read_i64::<BigEndian>() {
// Ok(rev_id) => Ok(RevId(rev_id)),
// Err(e) => Err(DocError::internal().context(e)),
// }
// }
// }
#[derive(Clone, Debug, ProtoBuf, Default)]
pub struct RevId {
#[pb(index = 1)]
pub value: i64,
}
impl AsRef<i64> for RevId {
fn as_ref(&self) -> &i64 { &self.value }
}
impl std::convert::Into<i64> for RevId {
fn into(self) -> i64 { self.value }
}
impl std::convert::From<i64> for RevId {
fn from(value: i64) -> Self { RevId { value } }
}
impl std::fmt::Display for RevId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("{}", self.value)) }
}
#[derive(PartialEq, Eq, Clone, Default, ProtoBuf)]
pub struct Revision {
#[pb(index = 1)]
pub base_rev_id: i64,
#[pb(index = 2)]
pub rev_id: i64,
#[pb(index = 3)]
pub delta_data: Vec<u8>,
#[pb(index = 4)]
pub md5: String,
#[pb(index = 5)]
pub doc_id: String,
#[pb(index = 6)]
pub ty: RevType,
}
impl Revision {
pub fn is_empty(&self) -> bool { self.base_rev_id == self.rev_id }
}
impl std::fmt::Debug for Revision {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
let _ = f.write_fmt(format_args!("doc_id {}, ", self.doc_id))?;
let _ = f.write_fmt(format_args!("base_rev_id {}, ", self.base_rev_id))?;
let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?;
match Delta::from_bytes(&self.delta_data) {
Ok(delta) => {
let _ = f.write_fmt(format_args!("delta {:?}", delta.to_json()))?;
},
Err(e) => {
let _ = f.write_fmt(format_args!("delta {:?}", e))?;
},
}
Ok(())
}
}
impl Revision {
pub fn new<T1, T2, D>(base_rev_id: T1, rev_id: T2, delta: D, doc_id: &str, ty: RevType) -> Revision
where
T1: Into<i64>,
T2: Into<i64>,
D: AsRef<[u8]>,
{
let md5 = md5(&delta);
let doc_id = doc_id.to_owned();
let delta_data = delta.as_ref().to_vec();
let base_rev_id = base_rev_id.into();
let rev_id = rev_id.into();
if base_rev_id != 0 {
debug_assert!(base_rev_id != rev_id);
}
Self {
base_rev_id,
rev_id,
delta_data,
md5,
doc_id,
ty,
}
}
}
pub fn revision_from_doc(doc: Doc, ty: RevType) -> Revision {
let delta_data = doc.data.as_bytes();
let revision = Revision::new(
doc.base_rev_id.clone(),
doc.rev_id.clone(),
delta_data.to_owned(),
&doc.id,
ty,
);
revision
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct RevisionRange {
#[pb(index = 1)]
pub doc_id: String,
#[pb(index = 2)]
pub start: i64,
#[pb(index = 3)]
pub end: i64,
}
impl RevisionRange {
pub fn len(&self) -> i64 {
debug_assert!(self.end >= self.start);
if self.end >= self.start {
self.end - self.start + 1
} else {
0
}
}
pub fn iter(&self) -> RangeInclusive<i64> {
debug_assert!(self.start != self.end);
RangeInclusive::new(self.start, self.end)
}
}

View File

@ -1,2 +0,0 @@
pub mod doc;
pub mod ws;

View File

@ -1,3 +0,0 @@
mod ws;
pub use ws::*;

View File

@ -1,66 +0,0 @@
use crate::{
entities::doc::{NewDocUser, Revision},
errors::DocumentError,
};
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use std::convert::{TryFrom, TryInto};
#[derive(Debug, Clone, ProtoBuf_Enum, Eq, PartialEq, Hash)]
pub enum WsDataType {
Acked = 0,
PushRev = 1,
PullRev = 2, // data should be Revision
Conflict = 3,
NewDocUser = 4,
}
impl WsDataType {
pub fn data<T>(&self, bytes: Bytes) -> Result<T, DocumentError>
where
T: TryFrom<Bytes, Error = DocumentError>,
{
T::try_from(bytes)
}
}
impl std::default::Default for WsDataType {
fn default() -> Self { WsDataType::Acked }
}
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct WsDocumentData {
#[pb(index = 1)]
pub doc_id: String,
#[pb(index = 2)]
pub ty: WsDataType,
// Opti: parse the data with type constraints
#[pb(index = 3)]
pub data: Vec<u8>,
}
impl std::convert::From<Revision> for WsDocumentData {
fn from(revision: Revision) -> Self {
let doc_id = revision.doc_id.clone();
let bytes: Bytes = revision.try_into().unwrap();
Self {
doc_id,
ty: WsDataType::PushRev,
data: bytes.to_vec(),
}
}
}
impl std::convert::From<NewDocUser> for WsDocumentData {
fn from(user: NewDocUser) -> Self {
let doc_id = user.doc_id.clone();
let bytes: Bytes = user.try_into().unwrap();
Self {
doc_id,
ty: WsDataType::NewDocUser,
data: bytes.to_vec(),
}
}
}

View File

@ -1,63 +0,0 @@
use std::{fmt, fmt::Debug};
use strum_macros::Display;
macro_rules! static_doc_error {
($name:ident, $status:expr) => {
#[allow(non_snake_case, missing_docs)]
pub fn $name() -> DocumentError {
DocumentError {
code: $status,
msg: format!("{}", $status),
}
}
};
}
pub type DocumentResult<T> = std::result::Result<T, DocumentError>;
#[derive(Debug, Clone)]
pub struct DocumentError {
pub code: ErrorCode,
pub msg: String,
}
impl DocumentError {
fn new(code: ErrorCode, msg: &str) -> Self {
Self {
code,
msg: msg.to_owned(),
}
}
pub fn context<T: Debug>(mut self, error: T) -> Self {
self.msg = format!("{:?}", error);
self
}
static_doc_error!(internal, ErrorCode::InternalError);
static_doc_error!(undo, ErrorCode::UndoFail);
static_doc_error!(redo, ErrorCode::RedoFail);
static_doc_error!(out_of_bound, ErrorCode::OutOfBound);
}
impl fmt::Display for DocumentError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}: {}", &self.code, &self.msg) }
}
#[derive(Debug, Clone, Display, PartialEq, Eq)]
pub enum ErrorCode {
DocIdInvalid = 0,
DocNotfound = 1,
UndoFail = 200,
RedoFail = 201,
OutOfBound = 202,
InternalError = 1000,
}
impl std::convert::From<lib_ot::errors::OTError> for DocumentError {
fn from(error: lib_ot::errors::OTError) -> Self { DocumentError::new(ErrorCode::InternalError, "").context(error) }
}
impl std::convert::From<protobuf::ProtobufError> for DocumentError {
fn from(e: protobuf::ProtobufError) -> Self { DocumentError::internal().context(e) }
}

View File

@ -1,6 +0,0 @@
pub mod core;
pub mod entities;
pub mod errors;
pub mod protobuf;
pub mod user_default;
pub mod util;

View File

@ -1,4 +0,0 @@
mod model;
pub use model::*;

View File

@ -1,10 +0,0 @@
// Auto-generated, do not edit
mod ws;
pub use ws::*;
mod revision;
pub use revision::*;
mod doc;
pub use doc::*;

View File

@ -1,861 +0,0 @@
// This file is generated by rust-protobuf 2.22.1. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `revision.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
#[derive(PartialEq,Clone,Default)]
pub struct RevId {
// message fields
pub value: i64,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a RevId {
fn default() -> &'a RevId {
<RevId as ::protobuf::Message>::default_instance()
}
}
impl RevId {
pub fn new() -> RevId {
::std::default::Default::default()
}
// int64 value = 1;
pub fn get_value(&self) -> i64 {
self.value
}
pub fn clear_value(&mut self) {
self.value = 0;
}
// Param is passed by value, moved
pub fn set_value(&mut self, v: i64) {
self.value = v;
}
}
impl ::protobuf::Message for RevId {
fn is_initialized(&self) -> bool {
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 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
let tmp = is.read_int64()?;
self.value = tmp;
},
_ => {
::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;
if self.value != 0 {
my_size += ::protobuf::rt::value_size(1, self.value, ::protobuf::wire_format::WireTypeVarint);
}
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<()> {
if self.value != 0 {
os.write_int64(1, self.value)?;
}
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() -> RevId {
RevId::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_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
"value",
|m: &RevId| { &m.value },
|m: &mut RevId| { &mut m.value },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<RevId>(
"RevId",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static RevId {
static instance: ::protobuf::rt::LazyV2<RevId> = ::protobuf::rt::LazyV2::INIT;
instance.get(RevId::new)
}
}
impl ::protobuf::Clear for RevId {
fn clear(&mut self) {
self.value = 0;
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for RevId {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for RevId {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct Revision {
// message fields
pub base_rev_id: i64,
pub rev_id: i64,
pub delta_data: ::std::vec::Vec<u8>,
pub md5: ::std::string::String,
pub doc_id: ::std::string::String,
pub ty: RevType,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a Revision {
fn default() -> &'a Revision {
<Revision as ::protobuf::Message>::default_instance()
}
}
impl Revision {
pub fn new() -> Revision {
::std::default::Default::default()
}
// int64 base_rev_id = 1;
pub fn get_base_rev_id(&self) -> i64 {
self.base_rev_id
}
pub fn clear_base_rev_id(&mut self) {
self.base_rev_id = 0;
}
// Param is passed by value, moved
pub fn set_base_rev_id(&mut self, v: i64) {
self.base_rev_id = v;
}
// int64 rev_id = 2;
pub fn get_rev_id(&self) -> i64 {
self.rev_id
}
pub fn clear_rev_id(&mut self) {
self.rev_id = 0;
}
// Param is passed by value, moved
pub fn set_rev_id(&mut self, v: i64) {
self.rev_id = v;
}
// bytes delta_data = 3;
pub fn get_delta_data(&self) -> &[u8] {
&self.delta_data
}
pub fn clear_delta_data(&mut self) {
self.delta_data.clear();
}
// Param is passed by value, moved
pub fn set_delta_data(&mut self, v: ::std::vec::Vec<u8>) {
self.delta_data = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_delta_data(&mut self) -> &mut ::std::vec::Vec<u8> {
&mut self.delta_data
}
// Take field
pub fn take_delta_data(&mut self) -> ::std::vec::Vec<u8> {
::std::mem::replace(&mut self.delta_data, ::std::vec::Vec::new())
}
// string md5 = 4;
pub fn get_md5(&self) -> &str {
&self.md5
}
pub fn clear_md5(&mut self) {
self.md5.clear();
}
// Param is passed by value, moved
pub fn set_md5(&mut self, v: ::std::string::String) {
self.md5 = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_md5(&mut self) -> &mut ::std::string::String {
&mut self.md5
}
// Take field
pub fn take_md5(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.md5, ::std::string::String::new())
}
// string doc_id = 5;
pub fn get_doc_id(&self) -> &str {
&self.doc_id
}
pub fn clear_doc_id(&mut self) {
self.doc_id.clear();
}
// Param is passed by value, moved
pub fn set_doc_id(&mut self, v: ::std::string::String) {
self.doc_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_doc_id(&mut self) -> &mut ::std::string::String {
&mut self.doc_id
}
// Take field
pub fn take_doc_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.doc_id, ::std::string::String::new())
}
// .RevType ty = 6;
pub fn get_ty(&self) -> RevType {
self.ty
}
pub fn clear_ty(&mut self) {
self.ty = RevType::Local;
}
// Param is passed by value, moved
pub fn set_ty(&mut self, v: RevType) {
self.ty = v;
}
}
impl ::protobuf::Message for Revision {
fn is_initialized(&self) -> bool {
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 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
let tmp = is.read_int64()?;
self.base_rev_id = tmp;
},
2 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
let tmp = is.read_int64()?;
self.rev_id = tmp;
},
3 => {
::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.delta_data)?;
},
4 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.md5)?;
},
5 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.doc_id)?;
},
6 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.ty, 6, &mut self.unknown_fields)?
},
_ => {
::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;
if self.base_rev_id != 0 {
my_size += ::protobuf::rt::value_size(1, self.base_rev_id, ::protobuf::wire_format::WireTypeVarint);
}
if self.rev_id != 0 {
my_size += ::protobuf::rt::value_size(2, self.rev_id, ::protobuf::wire_format::WireTypeVarint);
}
if !self.delta_data.is_empty() {
my_size += ::protobuf::rt::bytes_size(3, &self.delta_data);
}
if !self.md5.is_empty() {
my_size += ::protobuf::rt::string_size(4, &self.md5);
}
if !self.doc_id.is_empty() {
my_size += ::protobuf::rt::string_size(5, &self.doc_id);
}
if self.ty != RevType::Local {
my_size += ::protobuf::rt::enum_size(6, self.ty);
}
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<()> {
if self.base_rev_id != 0 {
os.write_int64(1, self.base_rev_id)?;
}
if self.rev_id != 0 {
os.write_int64(2, self.rev_id)?;
}
if !self.delta_data.is_empty() {
os.write_bytes(3, &self.delta_data)?;
}
if !self.md5.is_empty() {
os.write_string(4, &self.md5)?;
}
if !self.doc_id.is_empty() {
os.write_string(5, &self.doc_id)?;
}
if self.ty != RevType::Local {
os.write_enum(6, ::protobuf::ProtobufEnum::value(&self.ty))?;
}
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() -> Revision {
Revision::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_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
"base_rev_id",
|m: &Revision| { &m.base_rev_id },
|m: &mut Revision| { &mut m.base_rev_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
"rev_id",
|m: &Revision| { &m.rev_id },
|m: &mut Revision| { &mut m.rev_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"delta_data",
|m: &Revision| { &m.delta_data },
|m: &mut Revision| { &mut m.delta_data },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"md5",
|m: &Revision| { &m.md5 },
|m: &mut Revision| { &mut m.md5 },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"doc_id",
|m: &Revision| { &m.doc_id },
|m: &mut Revision| { &mut m.doc_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<RevType>>(
"ty",
|m: &Revision| { &m.ty },
|m: &mut Revision| { &mut m.ty },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<Revision>(
"Revision",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static Revision {
static instance: ::protobuf::rt::LazyV2<Revision> = ::protobuf::rt::LazyV2::INIT;
instance.get(Revision::new)
}
}
impl ::protobuf::Clear for Revision {
fn clear(&mut self) {
self.base_rev_id = 0;
self.rev_id = 0;
self.delta_data.clear();
self.md5.clear();
self.doc_id.clear();
self.ty = RevType::Local;
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for Revision {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for Revision {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct RevisionRange {
// message fields
pub doc_id: ::std::string::String,
pub start: i64,
pub end: i64,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a RevisionRange {
fn default() -> &'a RevisionRange {
<RevisionRange as ::protobuf::Message>::default_instance()
}
}
impl RevisionRange {
pub fn new() -> RevisionRange {
::std::default::Default::default()
}
// string doc_id = 1;
pub fn get_doc_id(&self) -> &str {
&self.doc_id
}
pub fn clear_doc_id(&mut self) {
self.doc_id.clear();
}
// Param is passed by value, moved
pub fn set_doc_id(&mut self, v: ::std::string::String) {
self.doc_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_doc_id(&mut self) -> &mut ::std::string::String {
&mut self.doc_id
}
// Take field
pub fn take_doc_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.doc_id, ::std::string::String::new())
}
// int64 start = 2;
pub fn get_start(&self) -> i64 {
self.start
}
pub fn clear_start(&mut self) {
self.start = 0;
}
// Param is passed by value, moved
pub fn set_start(&mut self, v: i64) {
self.start = v;
}
// int64 end = 3;
pub fn get_end(&self) -> i64 {
self.end
}
pub fn clear_end(&mut self) {
self.end = 0;
}
// Param is passed by value, moved
pub fn set_end(&mut self, v: i64) {
self.end = v;
}
}
impl ::protobuf::Message for RevisionRange {
fn is_initialized(&self) -> bool {
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_singular_proto3_string_into(wire_type, is, &mut self.doc_id)?;
},
2 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
let tmp = is.read_int64()?;
self.start = tmp;
},
3 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
let tmp = is.read_int64()?;
self.end = tmp;
},
_ => {
::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;
if !self.doc_id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.doc_id);
}
if self.start != 0 {
my_size += ::protobuf::rt::value_size(2, self.start, ::protobuf::wire_format::WireTypeVarint);
}
if self.end != 0 {
my_size += ::protobuf::rt::value_size(3, self.end, ::protobuf::wire_format::WireTypeVarint);
}
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<()> {
if !self.doc_id.is_empty() {
os.write_string(1, &self.doc_id)?;
}
if self.start != 0 {
os.write_int64(2, self.start)?;
}
if self.end != 0 {
os.write_int64(3, self.end)?;
}
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() -> RevisionRange {
RevisionRange::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_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"doc_id",
|m: &RevisionRange| { &m.doc_id },
|m: &mut RevisionRange| { &mut m.doc_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
"start",
|m: &RevisionRange| { &m.start },
|m: &mut RevisionRange| { &mut m.start },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
"end",
|m: &RevisionRange| { &m.end },
|m: &mut RevisionRange| { &mut m.end },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<RevisionRange>(
"RevisionRange",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static RevisionRange {
static instance: ::protobuf::rt::LazyV2<RevisionRange> = ::protobuf::rt::LazyV2::INIT;
instance.get(RevisionRange::new)
}
}
impl ::protobuf::Clear for RevisionRange {
fn clear(&mut self) {
self.doc_id.clear();
self.start = 0;
self.end = 0;
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for RevisionRange {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for RevisionRange {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum RevType {
Local = 0,
Remote = 1,
}
impl ::protobuf::ProtobufEnum for RevType {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<RevType> {
match value {
0 => ::std::option::Option::Some(RevType::Local),
1 => ::std::option::Option::Some(RevType::Remote),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [RevType] = &[
RevType::Local,
RevType::Remote,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<RevType>("RevType", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for RevType {
}
impl ::std::default::Default for RevType {
fn default() -> Self {
RevType::Local
}
}
impl ::protobuf::reflect::ProtobufValue for RevType {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0erevision.proto\"\x1d\n\x05RevId\x12\x14\n\x05value\x18\x01\x20\x01\
(\x03R\x05value\"\xa3\x01\n\x08Revision\x12\x1e\n\x0bbase_rev_id\x18\x01\
\x20\x01(\x03R\tbaseRevId\x12\x15\n\x06rev_id\x18\x02\x20\x01(\x03R\x05r\
evId\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\"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\x01J\xea\x05\n\x06\x12\x04\0\0\x15\x01\n\x08\n\x01\x0c\
\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x04\x01\n\n\n\x03\x04\0\
\x01\x12\x03\x02\x08\r\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x14\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\x0f\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x12\x13\n\n\n\x02\
\x04\x01\x12\x04\x05\0\x0c\x01\n\n\n\x03\x04\x01\x01\x12\x03\x05\x08\x10\
\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x06\x04\x1a\n\x0c\n\x05\x04\x01\x02\0\
\x05\x12\x03\x06\x04\t\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x06\n\x15\n\
\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x06\x18\x19\n\x0b\n\x04\x04\x01\x02\
\x01\x12\x03\x07\x04\x15\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x07\x04\
\t\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x07\n\x10\n\x0c\n\x05\x04\x01\
\x02\x01\x03\x12\x03\x07\x13\x14\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x08\
\x04\x19\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\x08\x04\t\n\x0c\n\x05\
\x04\x01\x02\x02\x01\x12\x03\x08\n\x14\n\x0c\n\x05\x04\x01\x02\x02\x03\
\x12\x03\x08\x17\x18\n\x0b\n\x04\x04\x01\x02\x03\x12\x03\t\x04\x13\n\x0c\
\n\x05\x04\x01\x02\x03\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\x01\x02\x03\
\x01\x12\x03\t\x0b\x0e\n\x0c\n\x05\x04\x01\x02\x03\x03\x12\x03\t\x11\x12\
\n\x0b\n\x04\x04\x01\x02\x04\x12\x03\n\x04\x16\n\x0c\n\x05\x04\x01\x02\
\x04\x05\x12\x03\n\x04\n\n\x0c\n\x05\x04\x01\x02\x04\x01\x12\x03\n\x0b\
\x11\n\x0c\n\x05\x04\x01\x02\x04\x03\x12\x03\n\x14\x15\n\x0b\n\x04\x04\
\x01\x02\x05\x12\x03\x0b\x04\x13\n\x0c\n\x05\x04\x01\x02\x05\x06\x12\x03\
\x0b\x04\x0b\n\x0c\n\x05\x04\x01\x02\x05\x01\x12\x03\x0b\x0c\x0e\n\x0c\n\
\x05\x04\x01\x02\x05\x03\x12\x03\x0b\x11\x12\n\n\n\x02\x04\x02\x12\x04\r\
\0\x11\x01\n\n\n\x03\x04\x02\x01\x12\x03\r\x08\x15\n\x0b\n\x04\x04\x02\
\x02\0\x12\x03\x0e\x04\x16\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0e\x04\
\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x0e\x0b\x11\n\x0c\n\x05\x04\x02\
\x02\0\x03\x12\x03\x0e\x14\x15\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\x0f\
\x04\x14\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\x03\x0f\x04\t\n\x0c\n\x05\
\x04\x02\x02\x01\x01\x12\x03\x0f\n\x0f\n\x0c\n\x05\x04\x02\x02\x01\x03\
\x12\x03\x0f\x12\x13\n\x0b\n\x04\x04\x02\x02\x02\x12\x03\x10\x04\x12\n\
\x0c\n\x05\x04\x02\x02\x02\x05\x12\x03\x10\x04\t\n\x0c\n\x05\x04\x02\x02\
\x02\x01\x12\x03\x10\n\r\n\x0c\n\x05\x04\x02\x02\x02\x03\x12\x03\x10\x10\
\x11\n\n\n\x02\x05\0\x12\x04\x12\0\x15\x01\n\n\n\x03\x05\0\x01\x12\x03\
\x12\x05\x0c\n\x0b\n\x04\x05\0\x02\0\x12\x03\x13\x04\x0e\n\x0c\n\x05\x05\
\0\x02\0\x01\x12\x03\x13\x04\t\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x13\
\x0c\r\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x14\x04\x0f\n\x0c\n\x05\x05\0\
\x02\x01\x01\x12\x03\x14\x04\n\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x14\
\r\x0eb\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -1,357 +0,0 @@
// This file is generated by rust-protobuf 2.22.1. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `ws.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
#[derive(PartialEq,Clone,Default)]
pub struct WsDocumentData {
// message fields
pub doc_id: ::std::string::String,
pub ty: WsDataType,
pub data: ::std::vec::Vec<u8>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a WsDocumentData {
fn default() -> &'a WsDocumentData {
<WsDocumentData as ::protobuf::Message>::default_instance()
}
}
impl WsDocumentData {
pub fn new() -> WsDocumentData {
::std::default::Default::default()
}
// string doc_id = 1;
pub fn get_doc_id(&self) -> &str {
&self.doc_id
}
pub fn clear_doc_id(&mut self) {
self.doc_id.clear();
}
// Param is passed by value, moved
pub fn set_doc_id(&mut self, v: ::std::string::String) {
self.doc_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_doc_id(&mut self) -> &mut ::std::string::String {
&mut self.doc_id
}
// Take field
pub fn take_doc_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.doc_id, ::std::string::String::new())
}
// .WsDataType ty = 2;
pub fn get_ty(&self) -> WsDataType {
self.ty
}
pub fn clear_ty(&mut self) {
self.ty = WsDataType::Acked;
}
// Param is passed by value, moved
pub fn set_ty(&mut self, v: WsDataType) {
self.ty = v;
}
// bytes data = 3;
pub fn get_data(&self) -> &[u8] {
&self.data
}
pub fn clear_data(&mut self) {
self.data.clear();
}
// Param is passed by value, moved
pub fn set_data(&mut self, v: ::std::vec::Vec<u8>) {
self.data = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_data(&mut self) -> &mut ::std::vec::Vec<u8> {
&mut self.data
}
// Take field
pub fn take_data(&mut self) -> ::std::vec::Vec<u8> {
::std::mem::replace(&mut self.data, ::std::vec::Vec::new())
}
}
impl ::protobuf::Message for WsDocumentData {
fn is_initialized(&self) -> bool {
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_singular_proto3_string_into(wire_type, is, &mut self.doc_id)?;
},
2 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.ty, 2, &mut self.unknown_fields)?
},
3 => {
::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?;
},
_ => {
::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;
if !self.doc_id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.doc_id);
}
if self.ty != WsDataType::Acked {
my_size += ::protobuf::rt::enum_size(2, self.ty);
}
if !self.data.is_empty() {
my_size += ::protobuf::rt::bytes_size(3, &self.data);
}
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<()> {
if !self.doc_id.is_empty() {
os.write_string(1, &self.doc_id)?;
}
if self.ty != WsDataType::Acked {
os.write_enum(2, ::protobuf::ProtobufEnum::value(&self.ty))?;
}
if !self.data.is_empty() {
os.write_bytes(3, &self.data)?;
}
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() -> WsDocumentData {
WsDocumentData::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_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"doc_id",
|m: &WsDocumentData| { &m.doc_id },
|m: &mut WsDocumentData| { &mut m.doc_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<WsDataType>>(
"ty",
|m: &WsDocumentData| { &m.ty },
|m: &mut WsDocumentData| { &mut m.ty },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"data",
|m: &WsDocumentData| { &m.data },
|m: &mut WsDocumentData| { &mut m.data },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<WsDocumentData>(
"WsDocumentData",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static WsDocumentData {
static instance: ::protobuf::rt::LazyV2<WsDocumentData> = ::protobuf::rt::LazyV2::INIT;
instance.get(WsDocumentData::new)
}
}
impl ::protobuf::Clear for WsDocumentData {
fn clear(&mut self) {
self.doc_id.clear();
self.ty = WsDataType::Acked;
self.data.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for WsDocumentData {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for WsDocumentData {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum WsDataType {
Acked = 0,
PushRev = 1,
PullRev = 2,
Conflict = 3,
NewDocUser = 4,
}
impl ::protobuf::ProtobufEnum for WsDataType {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<WsDataType> {
match value {
0 => ::std::option::Option::Some(WsDataType::Acked),
1 => ::std::option::Option::Some(WsDataType::PushRev),
2 => ::std::option::Option::Some(WsDataType::PullRev),
3 => ::std::option::Option::Some(WsDataType::Conflict),
4 => ::std::option::Option::Some(WsDataType::NewDocUser),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [WsDataType] = &[
WsDataType::Acked,
WsDataType::PushRev,
WsDataType::PullRev,
WsDataType::Conflict,
WsDataType::NewDocUser,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<WsDataType>("WsDataType", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for WsDataType {
}
impl ::std::default::Default for WsDataType {
fn default() -> Self {
WsDataType::Acked
}
}
impl ::protobuf::reflect::ProtobufValue for WsDataType {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x08ws.proto\"X\n\x0eWsDocumentData\x12\x15\n\x06doc_id\x18\x01\x20\
\x01(\tR\x05docId\x12\x1b\n\x02ty\x18\x02\x20\x01(\x0e2\x0b.WsDataTypeR\
\x02ty\x12\x12\n\x04data\x18\x03\x20\x01(\x0cR\x04data*O\n\nWsDataType\
\x12\t\n\x05Acked\x10\0\x12\x0b\n\x07PushRev\x10\x01\x12\x0b\n\x07PullRe\
v\x10\x02\x12\x0c\n\x08Conflict\x10\x03\x12\x0e\n\nNewDocUser\x10\x04J\
\xb4\x03\n\x06\x12\x04\0\0\r\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\
\x02\x04\0\x12\x04\x02\0\x06\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x16\
\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x16\n\x0c\n\x05\x04\0\x02\0\x05\
\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x11\n\x0c\
\n\x05\x04\0\x02\0\x03\x12\x03\x03\x14\x15\n\x0b\n\x04\x04\0\x02\x01\x12\
\x03\x04\x04\x16\n\x0c\n\x05\x04\0\x02\x01\x06\x12\x03\x04\x04\x0e\n\x0c\
\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0f\x11\n\x0c\n\x05\x04\0\x02\x01\
\x03\x12\x03\x04\x14\x15\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x13\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\x0e\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x11\x12\
\n\n\n\x02\x05\0\x12\x04\x07\0\r\x01\n\n\n\x03\x05\0\x01\x12\x03\x07\x05\
\x0f\n\x0b\n\x04\x05\0\x02\0\x12\x03\x08\x04\x0e\n\x0c\n\x05\x05\0\x02\0\
\x01\x12\x03\x08\x04\t\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x08\x0c\r\n\
\x0b\n\x04\x05\0\x02\x01\x12\x03\t\x04\x10\n\x0c\n\x05\x05\0\x02\x01\x01\
\x12\x03\t\x04\x0b\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\t\x0e\x0f\n\x0b\
\n\x04\x05\0\x02\x02\x12\x03\n\x04\x10\n\x0c\n\x05\x05\0\x02\x02\x01\x12\
\x03\n\x04\x0b\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\n\x0e\x0f\n\x0b\n\
\x04\x05\0\x02\x03\x12\x03\x0b\x04\x11\n\x0c\n\x05\x05\0\x02\x03\x01\x12\
\x03\x0b\x04\x0c\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\x0b\x0f\x10\n\x0b\
\n\x04\x05\0\x02\x04\x12\x03\x0c\x04\x13\n\x0c\n\x05\x05\0\x02\x04\x01\
\x12\x03\x0c\x04\x0e\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x0c\x11\x12b\
\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -1,29 +0,0 @@
syntax = "proto3";
message CreateDocParams {
string id = 1;
string data = 2;
}
message Doc {
string id = 1;
string data = 2;
int64 rev_id = 3;
int64 base_rev_id = 4;
}
message UpdateDocParams {
string doc_id = 1;
string data = 2;
int64 rev_id = 3;
}
message DocDelta {
string doc_id = 1;
string data = 2;
}
message NewDocUser {
string user_id = 1;
int64 rev_id = 2;
string doc_id = 3;
}
message DocIdentifier {
string doc_id = 1;
}

View File

@ -1,22 +0,0 @@
syntax = "proto3";
message RevId {
int64 value = 1;
}
message Revision {
int64 base_rev_id = 1;
int64 rev_id = 2;
bytes delta_data = 3;
string md5 = 4;
string doc_id = 5;
RevType ty = 6;
}
message RevisionRange {
string doc_id = 1;
int64 start = 2;
int64 end = 3;
}
enum RevType {
Local = 0;
Remote = 1;
}

View File

@ -1,14 +0,0 @@
syntax = "proto3";
message WsDocumentData {
string doc_id = 1;
WsDataType ty = 2;
bytes data = 3;
}
enum WsDataType {
Acked = 0;
PushRev = 1;
PullRev = 2;
Conflict = 3;
NewDocUser = 4;
}

View File

@ -1,24 +0,0 @@
use lib_ot::core::{Delta, DeltaBuilder};
#[inline]
pub fn doc_initial_delta() -> Delta { DeltaBuilder::new().insert("\n").build() }
#[inline]
pub fn doc_initial_string() -> String { doc_initial_delta().to_json() }
#[inline]
pub fn initial_read_me() -> Delta {
let json = include_str!("READ_ME.json");
let delta = Delta::from_json(json).unwrap();
delta
}
#[cfg(test)]
mod tests {
use crate::user_default::initial_read_me;
#[test]
fn load_read_me() {
println!("{}", initial_read_me().to_json());
}
}

View File

@ -1,39 +0,0 @@
use lib_ot::core::{NEW_LINE, WHITESPACE};
use std::sync::atomic::{AtomicI64, Ordering::SeqCst};
#[inline]
pub fn find_newline(s: &str) -> Option<usize> {
match s.find(NEW_LINE) {
None => None,
Some(line_break) => Some(line_break),
}
}
#[inline]
pub fn is_newline(s: &str) -> bool { s == NEW_LINE }
#[inline]
pub fn is_whitespace(s: &str) -> bool { s == WHITESPACE }
#[inline]
pub fn contain_newline(s: &str) -> bool { s.contains(NEW_LINE) }
#[inline]
pub fn md5<T: AsRef<[u8]>>(data: T) -> String {
let md5 = format!("{:x}", md5::compute(data));
md5
}
#[derive(Debug)]
pub struct RevIdCounter(pub AtomicI64);
impl RevIdCounter {
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) }
pub fn set(&self, n: i64) { let _ = self.0.fetch_update(SeqCst, SeqCst, |_| Some(n)); }
}

View File

@ -7,16 +7,17 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
flowy-document-infra = { path = "../flowy-document-infra" }
flowy-document-infra = { path = "../../../shared-lib/flowy-document-infra" }
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
lib-ot = { path = "../../../shared-lib/lib-ot" }
lib-ws = { path = "../../../shared-lib/lib-ws" }
backend-service = { path = "../../../shared-lib/backend-service" }
derive_more = {version = "0.99", features = ["display"]}
lib-dispatch = { path = "../lib-dispatch" }
flowy-derive = { path = "../flowy-derive" }
flowy-database = { path = "../flowy-database" }
lib-infra = { path = "../lib-infra" }
flowy-database = { path = "../flowy-database" }
dart-notify = { path = "../dart-notify" }
lib-ot = { path = "../lib-ot" }
lib-ws = { path = "../lib-ws" }
backend-service = { path = "../backend-service" }
diesel = {version = "1.4.8", features = ["sqlite"]}

View File

@ -9,13 +9,10 @@ edition = "2018"
lib-dispatch = { path = "../lib-dispatch" }
lib-log = { path = "../lib-log" }
flowy-user = { path = "../flowy-user" }
lib-infra = { path = "../lib-infra" }
flowy-workspace = { path = "../flowy-workspace", default-features = false }
flowy-database = { path = "../flowy-database" }
flowy-document = { path = "../flowy-document" }
flowy-document-infra = { path = "../flowy-document-infra" }
lib-ws = { path = "../lib-ws" }
backend-service = { path = "../backend-service" }
lib-infra = { path = "../lib-infra" }
tracing = { version = "0.1" }
log = "0.4.14"
futures-core = { version = "0.3", default-features = false }
@ -24,6 +21,12 @@ bytes = "1.0"
tokio = { version = "1", features = ["rt"] }
parking_lot = "0.11"
flowy-document-infra = { path = "../../../shared-lib/flowy-document-infra" }
lib-ws = { path = "../../../shared-lib/lib-ws" }
backend-service = { path = "../../../shared-lib/backend-service" }
[dev-dependencies]
serde = { version = "1.0", features = ["derive"] }
bincode = { version = "1.3"}

View File

@ -7,13 +7,15 @@ edition = "2018"
[dependencies]
flowy-sdk = { path = "../flowy-sdk"}
lib-dispatch = { path = "../lib-dispatch" }
flowy-user = { path = "../flowy-user"}
flowy-workspace = { path = "../flowy-workspace", default-features = false}
lib-infra = { path = "../lib-infra" }
flowy-document = { path = "../flowy-document"}
flowy-document-infra = { path = "../flowy-document-infra"}
backend-service = { path = "../backend-service" }
lib-dispatch = { path = "../lib-dispatch" }
lib-infra = { path = "../lib-infra" }
flowy-document-infra = { path = "../../../shared-lib/flowy-document-infra" }
backend-service = { path = "../../../shared-lib/backend-service" }
serde = { version = "1.0", features = ["derive"] }
bincode = { version = "1.3"}

View File

@ -1,26 +0,0 @@
[package]
name = "flowy-user-infra"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
flowy-derive = { path = "../flowy-derive" }
protobuf = {version = "2.18.0"}
bytes = "1.0"
unicode-segmentation = "1.8"
derive_more = {version = "0.99", features = ["display"]}
validator = "0.12.0"
log = "0.4.14"
fancy-regex = "0.5.0"
lazy_static = "1.4"
[dev-dependencies]
quickcheck = "0.9.2"
quickcheck_macros = "0.9.1"
fake = "~2.3.0"
claim = "0.4.0"
futures = "0.3.15"
serial_test = "0.5.1"

View File

@ -1,3 +0,0 @@
proto_crates = ["src/entities", "src/errors.rs"]
event_files = []

View File

@ -1,113 +0,0 @@
use std::convert::TryInto;
use flowy_derive::ProtoBuf;
use crate::{errors::*, parser::*};
#[derive(ProtoBuf, Default)]
pub struct SignInRequest {
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub password: String,
#[pb(index = 3)]
pub name: String,
}
#[derive(Default, ProtoBuf, Debug)]
pub struct SignInParams {
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub password: String,
#[pb(index = 3)]
pub name: String,
}
#[derive(Debug, Default, ProtoBuf)]
pub struct SignInResponse {
#[pb(index = 1)]
pub user_id: String,
#[pb(index = 2)]
pub name: String,
#[pb(index = 3)]
pub email: String,
#[pb(index = 4)]
pub token: String,
}
impl TryInto<SignInParams> for SignInRequest {
type Error = ErrorCode;
fn try_into(self) -> Result<SignInParams, Self::Error> {
let email = UserEmail::parse(self.email)?;
let password = UserPassword::parse(self.password)?;
Ok(SignInParams {
email: email.0,
password: password.0,
name: self.name,
})
}
}
#[derive(ProtoBuf, Default)]
pub struct SignUpRequest {
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub name: String,
#[pb(index = 3)]
pub password: String,
}
impl TryInto<SignUpParams> for SignUpRequest {
type Error = ErrorCode;
fn try_into(self) -> Result<SignUpParams, Self::Error> {
let email = UserEmail::parse(self.email)?;
let password = UserPassword::parse(self.password)?;
let name = UserName::parse(self.name)?;
Ok(SignUpParams {
email: email.0,
name: name.0,
password: password.0,
})
}
}
#[derive(ProtoBuf, Default, Debug)]
pub struct SignUpParams {
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub name: String,
#[pb(index = 3)]
pub password: String,
}
#[derive(ProtoBuf, Debug, Default)]
pub struct SignUpResponse {
#[pb(index = 1)]
pub user_id: String,
#[pb(index = 2)]
pub name: String,
#[pb(index = 3)]
pub email: String,
#[pb(index = 4)]
pub token: String,
}

View File

@ -1,9 +0,0 @@
pub use auth::*;
pub use user_profile::*;
pub mod auth;
mod user_profile;
pub mod prelude {
pub use crate::entities::{auth::*, user_profile::*};
}

View File

@ -1,137 +0,0 @@
use flowy_derive::ProtoBuf;
use std::convert::TryInto;
use crate::{
errors::ErrorCode,
parser::{UserEmail, UserId, UserName, UserPassword},
};
#[derive(Default, ProtoBuf)]
pub struct UserToken {
#[pb(index = 1)]
pub token: String,
}
#[derive(ProtoBuf, Default, Debug, PartialEq, Eq, Clone)]
pub struct UserProfile {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub email: String,
#[pb(index = 3)]
pub name: String,
#[pb(index = 4)]
pub token: String,
}
#[derive(ProtoBuf, Default)]
pub struct UpdateUserRequest {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2, one_of)]
pub name: Option<String>,
#[pb(index = 3, one_of)]
pub email: Option<String>,
#[pb(index = 4, one_of)]
pub password: Option<String>,
}
impl UpdateUserRequest {
pub fn new(id: &str) -> Self {
Self {
id: id.to_owned(),
..Default::default()
}
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_owned());
self
}
pub fn email(mut self, email: &str) -> Self {
self.email = Some(email.to_owned());
self
}
pub fn password(mut self, password: &str) -> Self {
self.password = Some(password.to_owned());
self
}
}
#[derive(ProtoBuf, Default, Clone, Debug)]
pub struct UpdateUserParams {
// TODO: remove user id
#[pb(index = 1)]
pub id: String,
#[pb(index = 2, one_of)]
pub name: Option<String>,
#[pb(index = 3, one_of)]
pub email: Option<String>,
#[pb(index = 4, one_of)]
pub password: Option<String>,
}
impl UpdateUserParams {
pub fn new(user_id: &str) -> Self {
Self {
id: user_id.to_owned(),
..Default::default()
}
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_owned());
self
}
pub fn email(mut self, email: &str) -> Self {
self.email = Some(email.to_owned());
self
}
pub fn password(mut self, password: &str) -> Self {
self.password = Some(password.to_owned());
self
}
}
impl TryInto<UpdateUserParams> for UpdateUserRequest {
type Error = ErrorCode;
fn try_into(self) -> Result<UpdateUserParams, Self::Error> {
let id = UserId::parse(self.id)?.0;
let name = match self.name {
None => None,
Some(name) => Some(UserName::parse(name)?.0),
};
let email = match self.email {
None => None,
Some(email) => Some(UserEmail::parse(email)?.0),
};
let password = match self.password {
None => None,
Some(password) => Some(UserPassword::parse(password)?.0),
};
Ok(UpdateUserParams {
id,
name,
email,
password,
})
}
}

View File

@ -1,61 +0,0 @@
use crate::protobuf::ErrorCode as ProtoBufErrorCode;
use derive_more::Display;
use flowy_derive::ProtoBuf_Enum;
use protobuf::ProtobufEnum;
use std::convert::{TryFrom, TryInto};
#[derive(Debug, Clone, ProtoBuf_Enum, Display, PartialEq, Eq)]
pub enum ErrorCode {
#[display(fmt = "Email can not be empty or whitespace")]
EmailIsEmpty = 0,
#[display(fmt = "Email format is not valid")]
EmailFormatInvalid = 1,
#[display(fmt = "Email already exists")]
EmailAlreadyExists = 2,
#[display(fmt = "Password can not be empty or whitespace")]
PasswordIsEmpty = 10,
#[display(fmt = "Password format too long")]
PasswordTooLong = 11,
#[display(fmt = "Password contains forbidden characters.")]
PasswordContainsForbidCharacters = 12,
#[display(fmt = "Password should contain a minimum of 6 characters with 1 special 1 letter and 1 numeric")]
PasswordFormatInvalid = 13,
#[display(fmt = "Password not match")]
PasswordNotMatch = 14,
#[display(fmt = "User name is too long")]
UserNameTooLong = 20,
#[display(fmt = "User name contain forbidden characters")]
UserNameContainForbiddenCharacters = 21,
#[display(fmt = "User name can not be empty or whitespace")]
UserNameIsEmpty = 22,
#[display(fmt = "user id is empty or whitespace")]
UserIdInvalid = 23,
#[display(fmt = "User token is invalid")]
UserUnauthorized = 24,
#[display(fmt = "User not exist")]
UserNotExist = 25,
#[display(fmt = "Server error")]
ServerError = 99,
#[display(fmt = "Internal error")]
InternalError = 100,
}
impl ErrorCode {
pub fn value(&self) -> i32 {
let code: ProtoBufErrorCode = self.clone().try_into().unwrap();
code.value()
}
pub fn from_i32(value: i32) -> Self {
match ProtoBufErrorCode::from_i32(value) {
None => ErrorCode::InternalError,
Some(code) => ErrorCode::try_from(&code).unwrap(),
}
}
}
impl std::default::Default for ErrorCode {
fn default() -> Self { ErrorCode::InternalError }
}

View File

@ -1,5 +0,0 @@
pub mod entities;
pub mod errors;
pub mod parser;
pub mod protobuf;
pub mod user_default;

View File

@ -1,12 +0,0 @@
// https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
mod user_email;
mod user_id;
mod user_name;
mod user_password;
mod user_workspace;
pub use user_email::*;
pub use user_id::*;
pub use user_name::*;
pub use user_password::*;
pub use user_workspace::*;

View File

@ -1,63 +0,0 @@
use crate::errors::ErrorCode;
use validator::validate_email;
#[derive(Debug)]
pub struct UserEmail(pub String);
impl UserEmail {
pub fn parse(s: String) -> Result<UserEmail, ErrorCode> {
if s.trim().is_empty() {
return Err(ErrorCode::EmailIsEmpty);
}
if validate_email(&s) {
Ok(Self(s))
} else {
Err(ErrorCode::EmailFormatInvalid)
}
}
}
impl AsRef<str> for UserEmail {
fn as_ref(&self) -> &str { &self.0 }
}
#[cfg(test)]
mod tests {
use super::*;
use claim::assert_err;
use fake::{faker::internet::en::SafeEmail, Fake};
#[test]
fn empty_string_is_rejected() {
let email = "".to_string();
assert_err!(UserEmail::parse(email));
}
#[test]
fn email_missing_at_symbol_is_rejected() {
let email = "helloworld.com".to_string();
assert_err!(UserEmail::parse(email));
}
#[test]
fn email_missing_subject_is_rejected() {
let email = "@domain.com".to_string();
assert_err!(UserEmail::parse(email));
}
#[derive(Debug, Clone)]
struct ValidEmailFixture(pub String);
impl quickcheck::Arbitrary for ValidEmailFixture {
fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self {
let email = SafeEmail().fake_with_rng(g);
Self(email)
}
}
#[quickcheck_macros::quickcheck]
fn valid_emails_are_parsed_successfully(valid_email: ValidEmailFixture) -> bool {
UserEmail::parse(valid_email.0).is_ok()
}
}

View File

@ -1,18 +0,0 @@
use crate::errors::ErrorCode;
#[derive(Debug)]
pub struct UserId(pub String);
impl UserId {
pub fn parse(s: String) -> Result<UserId, ErrorCode> {
let is_empty_or_whitespace = s.trim().is_empty();
if is_empty_or_whitespace {
return Err(ErrorCode::UserIdInvalid);
}
Ok(Self(s))
}
}
impl AsRef<str> for UserId {
fn as_ref(&self) -> &str { &self.0 }
}

View File

@ -1,82 +0,0 @@
use crate::errors::ErrorCode;
use unicode_segmentation::UnicodeSegmentation;
#[derive(Debug)]
pub struct UserName(pub String);
impl UserName {
pub fn parse(s: String) -> Result<UserName, ErrorCode> {
let is_empty_or_whitespace = s.trim().is_empty();
if is_empty_or_whitespace {
return Err(ErrorCode::UserNameIsEmpty);
}
// A grapheme is defined by the Unicode standard as a "user-perceived"
// character: `å` is a single grapheme, but it is composed of two characters
// (`a` and `̊`).
//
// `graphemes` returns an iterator over the graphemes in the input `s`.
// `true` specifies that we want to use the extended grapheme definition set,
// the recommended one.
let is_too_long = s.graphemes(true).count() > 256;
if is_too_long {
return Err(ErrorCode::UserNameTooLong);
}
let forbidden_characters = ['/', '(', ')', '"', '<', '>', '\\', '{', '}'];
let contains_forbidden_characters = s.chars().any(|g| forbidden_characters.contains(&g));
if contains_forbidden_characters {
return Err(ErrorCode::UserNameContainForbiddenCharacters);
}
Ok(Self(s))
}
}
impl AsRef<str> for UserName {
fn as_ref(&self) -> &str { &self.0 }
}
#[cfg(test)]
mod tests {
use super::UserName;
use claim::{assert_err, assert_ok};
#[test]
fn a_256_grapheme_long_name_is_valid() {
let name = "".repeat(256);
assert_ok!(UserName::parse(name));
}
#[test]
fn a_name_longer_than_256_graphemes_is_rejected() {
let name = "a".repeat(257);
assert_err!(UserName::parse(name));
}
#[test]
fn whitespace_only_names_are_rejected() {
let name = " ".to_string();
assert_err!(UserName::parse(name));
}
#[test]
fn empty_string_is_rejected() {
let name = "".to_string();
assert_err!(UserName::parse(name));
}
#[test]
fn names_containing_an_invalid_character_are_rejected() {
for name in vec!['/', '(', ')', '"', '<', '>', '\\', '{', '}'] {
let name = name.to_string();
assert_err!(UserName::parse(name));
}
}
#[test]
fn a_valid_name_is_parsed_successfully() {
let name = "nathan".to_string();
assert_ok!(UserName::parse(name));
}
}

View File

@ -1,62 +0,0 @@
use crate::errors::ErrorCode;
use fancy_regex::Regex;
use lazy_static::lazy_static;
use unicode_segmentation::UnicodeSegmentation;
#[derive(Debug)]
pub struct UserPassword(pub String);
impl UserPassword {
pub fn parse(s: String) -> Result<UserPassword, ErrorCode> {
if s.trim().is_empty() {
return Err(ErrorCode::PasswordIsEmpty);
}
if s.graphemes(true).count() > 100 {
return Err(ErrorCode::PasswordTooLong);
}
let forbidden_characters = ['/', '(', ')', '"', '<', '>', '\\', '{', '}'];
let contains_forbidden_characters = s.chars().any(|g| forbidden_characters.contains(&g));
if contains_forbidden_characters {
return Err(ErrorCode::PasswordContainsForbidCharacters);
}
if !validate_password(&s) {
return Err(ErrorCode::PasswordFormatInvalid);
}
Ok(Self(s))
}
}
impl AsRef<str> for UserPassword {
fn as_ref(&self) -> &str { &self.0 }
}
lazy_static! {
// Test it in https://regex101.com/
// https://stackoverflow.com/questions/2370015/regular-expression-for-password-validation/2370045
// Hell1!
// [invalid, greater or equal to 6]
// Hel1!
//
// Hello1!
// [invalid, must include number]
// Hello!
//
// Hello12!
// [invalid must include upper case]
// hello12!
static ref PASSWORD: Regex = Regex::new("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\\W]).{6,20})").unwrap();
}
pub fn validate_password(password: &str) -> bool {
match PASSWORD.is_match(password) {
Ok(is_match) => is_match,
Err(e) => {
log::error!("validate_password fail: {:?}", e);
false
},
}
}

View File

@ -1,16 +0,0 @@
#[derive(Debug)]
pub struct UserWorkspace(pub String);
impl UserWorkspace {
pub fn parse(s: String) -> Result<UserWorkspace, String> {
let is_empty_or_whitespace = s.trim().is_empty();
if is_empty_or_whitespace {
return Err(format!("workspace id is empty or whitespace"));
}
Ok(Self(s))
}
}
impl AsRef<str> for UserWorkspace {
fn as_ref(&self) -> &str { &self.0 }
}

View File

@ -1,4 +0,0 @@
mod model;
pub use model::*;

File diff suppressed because it is too large Load Diff

View File

@ -1,175 +0,0 @@
// This file is generated by rust-protobuf 2.22.1. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `errors.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum ErrorCode {
EmailIsEmpty = 0,
EmailFormatInvalid = 1,
EmailAlreadyExists = 2,
PasswordIsEmpty = 10,
PasswordTooLong = 11,
PasswordContainsForbidCharacters = 12,
PasswordFormatInvalid = 13,
PasswordNotMatch = 14,
UserNameTooLong = 20,
UserNameContainForbiddenCharacters = 21,
UserNameIsEmpty = 22,
UserIdInvalid = 23,
UserUnauthorized = 24,
UserNotExist = 25,
ServerError = 99,
InternalError = 100,
}
impl ::protobuf::ProtobufEnum for ErrorCode {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<ErrorCode> {
match value {
0 => ::std::option::Option::Some(ErrorCode::EmailIsEmpty),
1 => ::std::option::Option::Some(ErrorCode::EmailFormatInvalid),
2 => ::std::option::Option::Some(ErrorCode::EmailAlreadyExists),
10 => ::std::option::Option::Some(ErrorCode::PasswordIsEmpty),
11 => ::std::option::Option::Some(ErrorCode::PasswordTooLong),
12 => ::std::option::Option::Some(ErrorCode::PasswordContainsForbidCharacters),
13 => ::std::option::Option::Some(ErrorCode::PasswordFormatInvalid),
14 => ::std::option::Option::Some(ErrorCode::PasswordNotMatch),
20 => ::std::option::Option::Some(ErrorCode::UserNameTooLong),
21 => ::std::option::Option::Some(ErrorCode::UserNameContainForbiddenCharacters),
22 => ::std::option::Option::Some(ErrorCode::UserNameIsEmpty),
23 => ::std::option::Option::Some(ErrorCode::UserIdInvalid),
24 => ::std::option::Option::Some(ErrorCode::UserUnauthorized),
25 => ::std::option::Option::Some(ErrorCode::UserNotExist),
99 => ::std::option::Option::Some(ErrorCode::ServerError),
100 => ::std::option::Option::Some(ErrorCode::InternalError),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [ErrorCode] = &[
ErrorCode::EmailIsEmpty,
ErrorCode::EmailFormatInvalid,
ErrorCode::EmailAlreadyExists,
ErrorCode::PasswordIsEmpty,
ErrorCode::PasswordTooLong,
ErrorCode::PasswordContainsForbidCharacters,
ErrorCode::PasswordFormatInvalid,
ErrorCode::PasswordNotMatch,
ErrorCode::UserNameTooLong,
ErrorCode::UserNameContainForbiddenCharacters,
ErrorCode::UserNameIsEmpty,
ErrorCode::UserIdInvalid,
ErrorCode::UserUnauthorized,
ErrorCode::UserNotExist,
ErrorCode::ServerError,
ErrorCode::InternalError,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<ErrorCode>("ErrorCode", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for ErrorCode {
}
impl ::std::default::Default for ErrorCode {
fn default() -> Self {
ErrorCode::EmailIsEmpty
}
}
impl ::protobuf::reflect::ProtobufValue for ErrorCode {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0cerrors.proto*\xff\x02\n\tErrorCode\x12\x10\n\x0cEmailIsEmpty\x10\0\
\x12\x16\n\x12EmailFormatInvalid\x10\x01\x12\x16\n\x12EmailAlreadyExists\
\x10\x02\x12\x13\n\x0fPasswordIsEmpty\x10\n\x12\x13\n\x0fPasswordTooLong\
\x10\x0b\x12$\n\x20PasswordContainsForbidCharacters\x10\x0c\x12\x19\n\
\x15PasswordFormatInvalid\x10\r\x12\x14\n\x10PasswordNotMatch\x10\x0e\
\x12\x13\n\x0fUserNameTooLong\x10\x14\x12&\n\"UserNameContainForbiddenCh\
aracters\x10\x15\x12\x13\n\x0fUserNameIsEmpty\x10\x16\x12\x11\n\rUserIdI\
nvalid\x10\x17\x12\x14\n\x10UserUnauthorized\x10\x18\x12\x10\n\x0cUserNo\
tExist\x10\x19\x12\x0f\n\x0bServerError\x10c\x12\x11\n\rInternalError\
\x10dJ\xba\x05\n\x06\x12\x04\0\0\x13\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\
\n\n\n\x02\x05\0\x12\x04\x02\0\x13\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\
\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x15\n\x0c\n\x05\x05\0\
\x02\0\x01\x12\x03\x03\x04\x10\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\
\x13\x14\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x1b\n\x0c\n\x05\x05\0\
\x02\x01\x01\x12\x03\x04\x04\x16\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\
\x04\x19\x1a\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x1b\n\x0c\n\x05\
\x05\0\x02\x02\x01\x12\x03\x05\x04\x16\n\x0c\n\x05\x05\0\x02\x02\x02\x12\
\x03\x05\x19\x1a\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\x04\x19\n\x0c\n\
\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\n\x05\x05\0\x02\x03\x02\
\x12\x03\x06\x16\x18\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x07\x04\x19\n\x0c\
\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x13\n\x0c\n\x05\x05\0\x02\x04\
\x02\x12\x03\x07\x16\x18\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x08\x04*\n\
\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04$\n\x0c\n\x05\x05\0\x02\x05\
\x02\x12\x03\x08')\n\x0b\n\x04\x05\0\x02\x06\x12\x03\t\x04\x1f\n\x0c\n\
\x05\x05\0\x02\x06\x01\x12\x03\t\x04\x19\n\x0c\n\x05\x05\0\x02\x06\x02\
\x12\x03\t\x1c\x1e\n\x0b\n\x04\x05\0\x02\x07\x12\x03\n\x04\x1a\n\x0c\n\
\x05\x05\0\x02\x07\x01\x12\x03\n\x04\x14\n\x0c\n\x05\x05\0\x02\x07\x02\
\x12\x03\n\x17\x19\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0b\x04\x19\n\x0c\n\
\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x13\n\x0c\n\x05\x05\0\x02\x08\x02\
\x12\x03\x0b\x16\x18\n\x0b\n\x04\x05\0\x02\t\x12\x03\x0c\x04,\n\x0c\n\
\x05\x05\0\x02\t\x01\x12\x03\x0c\x04&\n\x0c\n\x05\x05\0\x02\t\x02\x12\
\x03\x0c)+\n\x0b\n\x04\x05\0\x02\n\x12\x03\r\x04\x19\n\x0c\n\x05\x05\0\
\x02\n\x01\x12\x03\r\x04\x13\n\x0c\n\x05\x05\0\x02\n\x02\x12\x03\r\x16\
\x18\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\x04\x17\n\x0c\n\x05\x05\0\x02\
\x0b\x01\x12\x03\x0e\x04\x11\n\x0c\n\x05\x05\0\x02\x0b\x02\x12\x03\x0e\
\x14\x16\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\x0f\x04\x1a\n\x0c\n\x05\x05\0\
\x02\x0c\x01\x12\x03\x0f\x04\x14\n\x0c\n\x05\x05\0\x02\x0c\x02\x12\x03\
\x0f\x17\x19\n\x0b\n\x04\x05\0\x02\r\x12\x03\x10\x04\x16\n\x0c\n\x05\x05\
\0\x02\r\x01\x12\x03\x10\x04\x10\n\x0c\n\x05\x05\0\x02\r\x02\x12\x03\x10\
\x13\x15\n\x0b\n\x04\x05\0\x02\x0e\x12\x03\x11\x04\x15\n\x0c\n\x05\x05\0\
\x02\x0e\x01\x12\x03\x11\x04\x0f\n\x0c\n\x05\x05\0\x02\x0e\x02\x12\x03\
\x11\x12\x14\n\x0b\n\x04\x05\0\x02\x0f\x12\x03\x12\x04\x18\n\x0c\n\x05\
\x05\0\x02\x0f\x01\x12\x03\x12\x04\x11\n\x0c\n\x05\x05\0\x02\x0f\x02\x12\
\x03\x12\x14\x17b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -1,10 +0,0 @@
// Auto-generated, do not edit
mod errors;
pub use errors::*;
mod user_profile;
pub use user_profile::*;
mod auth;
pub use auth::*;

Some files were not shown because too many files have changed in this diff Show More