mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
add shared-lib folder
This commit is contained in:
@ -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"]
|
||||
|
@ -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"]
|
@ -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) }
|
||||
}
|
@ -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,
|
||||
}
|
@ -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;
|
@ -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),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
mod request;
|
||||
|
||||
pub use request::*;
|
@ -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()
|
||||
},
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
mod response;
|
||||
|
||||
#[cfg(feature = "http_server")]
|
||||
mod response_http;
|
||||
|
||||
pub use response::*;
|
@ -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) }
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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(())
|
||||
}
|
@ -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)
|
||||
}
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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"
|
@ -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()
|
||||
}
|
@ -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() }
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
@ -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) }
|
||||
}
|
@ -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;
|
||||
}
|
2
frontend/rust-lib/flowy-derive/.gitignore
vendored
2
frontend/rust-lib/flowy-derive/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
/target
|
||||
Cargo.lock
|
@ -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"
|
@ -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)*
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
@ -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,
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
mod derive_cache;
|
||||
|
||||
pub use derive_cache::*;
|
@ -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)*)
|
||||
}
|
@ -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;
|
||||
}),
|
||||
}
|
||||
}
|
@ -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)*
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
@ -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);
|
@ -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;
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
#[tokio::test]
|
||||
async fn tests() {
|
||||
let _t = trybuild::TestCases::new();
|
||||
// t.pass("tests/protobuf_enum.rs");
|
||||
}
|
@ -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"
|
@ -1,3 +0,0 @@
|
||||
|
||||
proto_crates = ["src/entities"]
|
||||
event_files = []
|
@ -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}}]
|
@ -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() }
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
mod default_delete;
|
||||
mod preserve_line_format_merge;
|
||||
|
||||
pub use default_delete::*;
|
||||
pub use preserve_line_format_merge::*;
|
@ -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)
|
||||
}
|
||||
}
|
@ -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(),
|
||||
// )
|
||||
// }
|
||||
// }
|
@ -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
|
||||
}
|
@ -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::*;
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
@ -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 }
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
@ -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>;
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
mod data;
|
||||
mod document;
|
||||
mod extensions;
|
||||
pub mod history;
|
||||
mod view;
|
||||
|
||||
pub use document::*;
|
||||
pub use view::RECORD_THRESHOLD;
|
@ -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 {})]
|
||||
}
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
mod doc;
|
||||
pub mod parser;
|
||||
mod revision;
|
||||
|
||||
pub use doc::*;
|
||||
pub use revision::*;
|
@ -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 }
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
mod doc_id;
|
||||
|
||||
pub use doc_id::*;
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
pub mod doc;
|
||||
pub mod ws;
|
@ -1,3 +0,0 @@
|
||||
mod ws;
|
||||
|
||||
pub use ws::*;
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
@ -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) }
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
pub mod core;
|
||||
pub mod entities;
|
||||
pub mod errors;
|
||||
pub mod protobuf;
|
||||
pub mod user_default;
|
||||
pub mod util;
|
@ -1,4 +0,0 @@
|
||||
|
||||
mod model;
|
||||
pub use model::*;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +0,0 @@
|
||||
// Auto-generated, do not edit
|
||||
|
||||
mod ws;
|
||||
pub use ws::*;
|
||||
|
||||
mod revision;
|
||||
pub use revision::*;
|
||||
|
||||
mod doc;
|
||||
pub use doc::*;
|
@ -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()
|
||||
})
|
||||
}
|
@ -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()
|
||||
})
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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)); }
|
||||
}
|
@ -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"]}
|
||||
|
@ -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"}
|
||||
|
@ -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"}
|
||||
|
@ -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"
|
@ -1,3 +0,0 @@
|
||||
|
||||
proto_crates = ["src/entities", "src/errors.rs"]
|
||||
event_files = []
|
@ -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,
|
||||
}
|
@ -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::*};
|
||||
}
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
@ -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 }
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
pub mod entities;
|
||||
pub mod errors;
|
||||
pub mod parser;
|
||||
pub mod protobuf;
|
||||
pub mod user_default;
|
@ -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::*;
|
@ -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()
|
||||
}
|
||||
}
|
@ -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 }
|
||||
}
|
@ -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 = "a̐".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));
|
||||
}
|
||||
}
|
@ -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
|
||||
},
|
||||
}
|
||||
}
|
@ -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 }
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
|
||||
mod model;
|
||||
pub use model::*;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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()
|
||||
})
|
||||
}
|
@ -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::*;
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user