mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
parser delta to rust
This commit is contained in:
parent
335b5327de
commit
0edd442562
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'package:flowy_editor/flowy_editor.dart';
|
import 'package:flowy_editor/flowy_editor.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:flowy_editor/src/model/quill_delta.dart';
|
import 'package:flowy_editor/src/model/quill_delta.dart';
|
||||||
|
import 'package:flowy_log/flowy_log.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ class FlowyDoc implements EditorChangesetSender {
|
|||||||
@override
|
@override
|
||||||
void sendDelta(Delta delta) {
|
void sendDelta(Delta delta) {
|
||||||
final json = jsonEncode(delta.toJson());
|
final json = jsonEncode(delta.toJson());
|
||||||
|
Log.debug("Send json: $json");
|
||||||
iDocImpl.applyChangeset(json: json);
|
iDocImpl.applyChangeset(json: json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::errors::{DocError, ErrorBuilder, ErrorCode};
|
use crate::errors::{DocError, ErrorBuilder, ErrorCode};
|
||||||
use dashmap::{mapref::one::Ref, DashMap};
|
use dashmap::DashMap;
|
||||||
use flowy_ot::{
|
use flowy_ot::{
|
||||||
client::{Document, FlowyDoc},
|
client::{Document, FlowyDoc},
|
||||||
core::Delta,
|
core::Delta,
|
||||||
|
@ -21,8 +21,7 @@
|
|||||||
// type Value = FlowyResponse<T>;
|
// type Value = FlowyResponse<T>;
|
||||||
//
|
//
|
||||||
// fn expecting(&self, formatter: &mut fmt::Formatter) ->
|
// fn expecting(&self, formatter: &mut fmt::Formatter) ->
|
||||||
// fmt::Result { formatter.write_str("struct Duration")
|
// fmt::Result { formatter.write_str("struct Duration") }
|
||||||
// }
|
|
||||||
//
|
//
|
||||||
// fn visit_map<V>(self, mut map: V) -> Result<Self::Value,
|
// fn visit_map<V>(self, mut map: V) -> Result<Self::Value,
|
||||||
// V::Error> where
|
// V::Error> where
|
||||||
@ -63,12 +62,8 @@
|
|||||||
// Ok(Self::Value::new(data, msg, code)) }
|
// Ok(Self::Value::new(data, msg, code)) }
|
||||||
// }
|
// }
|
||||||
// const FIELDS: &'static [&'static str] = &["msg", "code", "data"];
|
// const FIELDS: &'static [&'static str] = &["msg", "code", "data"];
|
||||||
// deserializer.deserialize_struct(
|
// deserializer.deserialize_struct("ServerResponse", FIELDS,
|
||||||
// "ServerResponse",
|
// ServerResponseVisitor(PhantomData)) }
|
||||||
// FIELDS,
|
|
||||||
// ServerResponseVisitor(PhantomData),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// struct DeserializeWith<'de, T: ServerData<'de>> {
|
// struct DeserializeWith<'de, T: ServerData<'de>> {
|
||||||
@ -101,8 +96,7 @@
|
|||||||
// type Value = Option<T>;
|
// type Value = Option<T>;
|
||||||
//
|
//
|
||||||
// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
// formatter.write_str("string or struct impl deserialize")
|
// formatter.write_str("string or struct impl deserialize") }
|
||||||
// }
|
|
||||||
//
|
//
|
||||||
// fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
// fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
// where
|
// where
|
||||||
|
@ -15,6 +15,8 @@ color-eyre = { version = "0.5", default-features = false }
|
|||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
|
strum = "0.21"
|
||||||
|
strum_macros = "0.21"
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -21,7 +21,7 @@ impl InsertExt for ResetLineFormatOnNewLine {
|
|||||||
|
|
||||||
let mut reset_attribute = Attributes::new();
|
let mut reset_attribute = Attributes::new();
|
||||||
if next_op.get_attributes().contains_key(&AttributeKey::Header) {
|
if next_op.get_attributes().contains_key(&AttributeKey::Header) {
|
||||||
reset_attribute.mark_as_removed(&AttributeKey::Header);
|
reset_attribute.delete(&AttributeKey::Header);
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = index + replace_len;
|
let len = index + replace_len;
|
||||||
|
@ -1,16 +1,159 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
use crate::core::Attributes;
|
|
||||||
use derive_more::Display;
|
use crate::{block_attribute, core::Attributes, ignore_attribute, inline_attribute};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashSet, fmt, fmt::Formatter, iter::FromIterator};
|
use std::{collections::HashSet, fmt, fmt::Formatter, iter::FromIterator};
|
||||||
|
use strum_macros::Display;
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Attribute {
|
||||||
|
pub key: AttributeKey,
|
||||||
|
pub value: AttributeValue,
|
||||||
|
pub scope: AttributeScope,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attribute {
|
||||||
|
// inline
|
||||||
|
inline_attribute!(Bold, bool);
|
||||||
|
inline_attribute!(Italic, bool);
|
||||||
|
inline_attribute!(Underline, bool);
|
||||||
|
inline_attribute!(StrikeThrough, bool);
|
||||||
|
inline_attribute!(Link, &str);
|
||||||
|
inline_attribute!(Color, String);
|
||||||
|
inline_attribute!(Font, usize);
|
||||||
|
inline_attribute!(Size, usize);
|
||||||
|
inline_attribute!(Background, String);
|
||||||
|
|
||||||
|
// block
|
||||||
|
block_attribute!(Header, usize);
|
||||||
|
block_attribute!(Indent, usize);
|
||||||
|
block_attribute!(Align, String);
|
||||||
|
block_attribute!(List, String);
|
||||||
|
block_attribute!(Bullet, bool);
|
||||||
|
block_attribute!(Ordered, bool);
|
||||||
|
block_attribute!(Checked, bool);
|
||||||
|
block_attribute!(UnChecked, bool);
|
||||||
|
block_attribute!(CodeBlock, bool);
|
||||||
|
block_attribute!(QuoteBlock, bool);
|
||||||
|
|
||||||
|
// ignore
|
||||||
|
ignore_attribute!(Width, usize);
|
||||||
|
ignore_attribute!(Height, usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Attribute {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
let s = format!("{:?}:{:?} {:?}", self.key, self.value.0, self.scope);
|
||||||
|
f.write_str(&s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::Into<Attributes> for Attribute {
|
||||||
|
fn into(self) -> Attributes {
|
||||||
|
let mut attributes = Attributes::new();
|
||||||
|
attributes.add(self);
|
||||||
|
attributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Display, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
// serde.rs/variant-attrs.html
|
||||||
|
// #[serde(rename_all = "snake_case")]
|
||||||
|
pub enum AttributeKey {
|
||||||
|
#[serde(rename = "bold")]
|
||||||
|
Bold,
|
||||||
|
#[serde(rename = "italic")]
|
||||||
|
Italic,
|
||||||
|
#[serde(rename = "underline")]
|
||||||
|
Underline,
|
||||||
|
#[serde(rename = "strikethrough")]
|
||||||
|
StrikeThrough,
|
||||||
|
#[serde(rename = "font")]
|
||||||
|
Font,
|
||||||
|
#[serde(rename = "size")]
|
||||||
|
Size,
|
||||||
|
#[serde(rename = "link")]
|
||||||
|
Link,
|
||||||
|
#[serde(rename = "color")]
|
||||||
|
Color,
|
||||||
|
#[serde(rename = "background")]
|
||||||
|
Background,
|
||||||
|
#[serde(rename = "indent")]
|
||||||
|
Indent,
|
||||||
|
#[serde(rename = "align")]
|
||||||
|
Align,
|
||||||
|
#[serde(rename = "code_block")]
|
||||||
|
CodeBlock,
|
||||||
|
#[serde(rename = "list")]
|
||||||
|
List,
|
||||||
|
#[serde(rename = "quote_block")]
|
||||||
|
QuoteBlock,
|
||||||
|
#[serde(rename = "width")]
|
||||||
|
Width,
|
||||||
|
#[serde(rename = "height")]
|
||||||
|
Height,
|
||||||
|
#[serde(rename = "header")]
|
||||||
|
Header,
|
||||||
|
#[serde(rename = "bullet")]
|
||||||
|
Bullet,
|
||||||
|
#[serde(rename = "ordered")]
|
||||||
|
Ordered,
|
||||||
|
#[serde(rename = "checked")]
|
||||||
|
Checked,
|
||||||
|
#[serde(rename = "unchecked")]
|
||||||
|
UnChecked,
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub trait AttributeValueData<'a>: Serialize + Deserialize<'a> {}
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct AttributeValue(pub(crate) Option<String>);
|
||||||
|
|
||||||
|
impl std::convert::From<&usize> for AttributeValue {
|
||||||
|
fn from(val: &usize) -> Self { AttributeValue::from(*val) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<usize> for AttributeValue {
|
||||||
|
fn from(val: usize) -> Self {
|
||||||
|
if val > (0 as usize) {
|
||||||
|
AttributeValue(Some(format!("{}", val)))
|
||||||
|
} else {
|
||||||
|
AttributeValue(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<&str> for AttributeValue {
|
||||||
|
fn from(val: &str) -> Self { AttributeValue(Some(val.to_owned())) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<String> for AttributeValue {
|
||||||
|
fn from(val: String) -> Self { AttributeValue(Some(val)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<&bool> for AttributeValue {
|
||||||
|
fn from(val: &bool) -> Self { AttributeValue::from(*val) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<bool> for AttributeValue {
|
||||||
|
fn from(val: bool) -> Self {
|
||||||
|
let val = match val {
|
||||||
|
true => Some("true".to_owned()),
|
||||||
|
false => None,
|
||||||
|
};
|
||||||
|
AttributeValue(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_block_except_header(k: &AttributeKey) -> bool {
|
||||||
|
if k == &AttributeKey::Header {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BLOCK_KEYS.contains(k)
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref BLOCK_KEYS: HashSet<AttributeKey> = HashSet::from_iter(vec![
|
static ref BLOCK_KEYS: HashSet<AttributeKey> = HashSet::from_iter(vec![
|
||||||
AttributeKey::Header,
|
AttributeKey::Header,
|
||||||
AttributeKey::LeftAlignment,
|
|
||||||
AttributeKey::CenterAlignment,
|
|
||||||
AttributeKey::RightAlignment,
|
|
||||||
AttributeKey::JustifyAlignment,
|
|
||||||
AttributeKey::Indent,
|
AttributeKey::Indent,
|
||||||
AttributeKey::Align,
|
AttributeKey::Align,
|
||||||
AttributeKey::CodeBlock,
|
AttributeKey::CodeBlock,
|
||||||
@ -42,208 +185,3 @@ pub enum AttributeScope {
|
|||||||
Embeds,
|
Embeds,
|
||||||
Ignore,
|
Ignore,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! inline_attribute {
|
|
||||||
(
|
|
||||||
$key: ident,
|
|
||||||
$value: ty
|
|
||||||
) => {
|
|
||||||
pub fn $key(value: $value) -> Self {
|
|
||||||
Self {
|
|
||||||
key: AttributeKey::$key,
|
|
||||||
value: value.into(),
|
|
||||||
scope: AttributeScope::Inline,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! block_attribute {
|
|
||||||
(
|
|
||||||
$key: ident,
|
|
||||||
$value: ident
|
|
||||||
) => {
|
|
||||||
pub fn $key(value: $value) -> Self {
|
|
||||||
Self {
|
|
||||||
key: AttributeKey::$key,
|
|
||||||
value: value.into(),
|
|
||||||
scope: AttributeScope::Block,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! ignore_attribute {
|
|
||||||
(
|
|
||||||
$key: ident,
|
|
||||||
$value: ident
|
|
||||||
) => {
|
|
||||||
pub fn $key(value: $value) -> Self {
|
|
||||||
Self {
|
|
||||||
key: AttributeKey::$key,
|
|
||||||
value: value.into(),
|
|
||||||
scope: AttributeScope::Ignore,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Attribute {
|
|
||||||
pub key: AttributeKey,
|
|
||||||
pub value: AttributeValue,
|
|
||||||
pub scope: AttributeScope,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Attribute {
|
|
||||||
// inline
|
|
||||||
inline_attribute!(Bold, bool);
|
|
||||||
inline_attribute!(Italic, bool);
|
|
||||||
inline_attribute!(Underline, bool);
|
|
||||||
inline_attribute!(StrikeThrough, bool);
|
|
||||||
inline_attribute!(Link, &str);
|
|
||||||
inline_attribute!(Color, String);
|
|
||||||
inline_attribute!(Font, usize);
|
|
||||||
inline_attribute!(Size, usize);
|
|
||||||
inline_attribute!(Background, String);
|
|
||||||
|
|
||||||
// block
|
|
||||||
block_attribute!(Header, usize);
|
|
||||||
block_attribute!(LeftAlignment, usize);
|
|
||||||
block_attribute!(CenterAlignment, usize);
|
|
||||||
block_attribute!(RightAlignment, usize);
|
|
||||||
block_attribute!(JustifyAlignment, bool);
|
|
||||||
block_attribute!(Indent, String);
|
|
||||||
block_attribute!(Align, String);
|
|
||||||
block_attribute!(CodeBlock, String);
|
|
||||||
block_attribute!(List, String);
|
|
||||||
block_attribute!(Bullet, bool);
|
|
||||||
block_attribute!(Ordered, bool);
|
|
||||||
block_attribute!(Checked, bool);
|
|
||||||
block_attribute!(UnChecked, bool);
|
|
||||||
block_attribute!(QuoteBlock, bool);
|
|
||||||
|
|
||||||
// ignore
|
|
||||||
ignore_attribute!(Width, usize);
|
|
||||||
ignore_attribute!(Height, usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Attribute {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
let s = format!("{:?}:{} {:?}", self.key, self.value.as_ref(), self.scope);
|
|
||||||
f.write_str(&s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::Into<Attributes> for Attribute {
|
|
||||||
fn into(self) -> Attributes {
|
|
||||||
let mut attributes = Attributes::new();
|
|
||||||
attributes.add(self);
|
|
||||||
attributes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Display, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub enum AttributeKey {
|
|
||||||
#[display(fmt = "bold")]
|
|
||||||
Bold,
|
|
||||||
#[display(fmt = "italic")]
|
|
||||||
Italic,
|
|
||||||
#[display(fmt = "underline")]
|
|
||||||
Underline,
|
|
||||||
#[display(fmt = "strike_through")]
|
|
||||||
StrikeThrough,
|
|
||||||
#[display(fmt = "font")]
|
|
||||||
Font,
|
|
||||||
#[display(fmt = "size")]
|
|
||||||
Size,
|
|
||||||
#[display(fmt = "link")]
|
|
||||||
Link,
|
|
||||||
#[display(fmt = "color")]
|
|
||||||
Color,
|
|
||||||
#[display(fmt = "background")]
|
|
||||||
Background,
|
|
||||||
#[display(fmt = "indent")]
|
|
||||||
Indent,
|
|
||||||
#[display(fmt = "align")]
|
|
||||||
Align,
|
|
||||||
#[display(fmt = "code_block")]
|
|
||||||
CodeBlock,
|
|
||||||
#[display(fmt = "list")]
|
|
||||||
List,
|
|
||||||
#[display(fmt = "quote_block")]
|
|
||||||
QuoteBlock,
|
|
||||||
#[display(fmt = "width")]
|
|
||||||
Width,
|
|
||||||
#[display(fmt = "height")]
|
|
||||||
Height,
|
|
||||||
#[display(fmt = "header")]
|
|
||||||
Header,
|
|
||||||
#[display(fmt = "left")]
|
|
||||||
LeftAlignment,
|
|
||||||
#[display(fmt = "center")]
|
|
||||||
CenterAlignment,
|
|
||||||
#[display(fmt = "right")]
|
|
||||||
RightAlignment,
|
|
||||||
#[display(fmt = "justify")]
|
|
||||||
JustifyAlignment,
|
|
||||||
#[display(fmt = "bullet")]
|
|
||||||
Bullet,
|
|
||||||
#[display(fmt = "ordered")]
|
|
||||||
Ordered,
|
|
||||||
#[display(fmt = "checked")]
|
|
||||||
Checked,
|
|
||||||
#[display(fmt = "unchecked")]
|
|
||||||
UnChecked,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct AttributeValue(pub(crate) String);
|
|
||||||
|
|
||||||
impl AsRef<str> for AttributeValue {
|
|
||||||
fn as_ref(&self) -> &str { &self.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<&usize> for AttributeValue {
|
|
||||||
fn from(val: &usize) -> Self { AttributeValue::from(*val) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<usize> for AttributeValue {
|
|
||||||
fn from(val: usize) -> Self {
|
|
||||||
if val > (0 as usize) {
|
|
||||||
AttributeValue(format!("{}", val))
|
|
||||||
} else {
|
|
||||||
AttributeValue(format!(""))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<&str> for AttributeValue {
|
|
||||||
fn from(val: &str) -> Self { AttributeValue(val.to_owned()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<String> for AttributeValue {
|
|
||||||
fn from(val: String) -> Self { AttributeValue(val) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<&bool> for AttributeValue {
|
|
||||||
fn from(val: &bool) -> Self { AttributeValue::from(*val) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<bool> for AttributeValue {
|
|
||||||
fn from(val: bool) -> Self {
|
|
||||||
let val = match val {
|
|
||||||
true => "true",
|
|
||||||
false => "",
|
|
||||||
};
|
|
||||||
AttributeValue(val.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_block_except_header(k: &AttributeKey) -> bool {
|
|
||||||
if k == &AttributeKey::Header {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
BLOCK_KEYS.contains(k)
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
use crate::core::{Attribute, AttributeKey, AttributeValue, Operation};
|
use crate::core::{Attribute, AttributeKey, AttributeValue, Operation};
|
||||||
use std::{collections::HashMap, fmt};
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
pub const REMOVE_FLAG: &'static str = "";
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub(crate) fn should_remove(val: &AttributeValue) -> bool { val.0 == REMOVE_FLAG }
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct Attributes {
|
pub struct Attributes {
|
||||||
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
// #[serde(skip_serializing_if = "HashMap::is_empty")]
|
||||||
#[serde(flatten)]
|
// #[serde(flatten)]
|
||||||
pub(crate) inner: HashMap<AttributeKey, AttributeValue>,
|
pub(crate) inner: HashMap<AttributeKey, AttributeValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,20 +32,19 @@ impl Attributes {
|
|||||||
self.inner.insert(key, value);
|
self.inner.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mark_as_removed(&mut self, key: &AttributeKey) {
|
pub fn add_kv(&mut self, key: AttributeKey, value: AttributeValue) { self.inner.insert(key, value); }
|
||||||
let value: AttributeValue = REMOVE_FLAG.into();
|
|
||||||
self.inner.insert(key.clone(), value);
|
pub fn delete(&mut self, key: &AttributeKey) { self.inner.insert(key.clone(), AttributeValue(None)); }
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mark_all_as_removed_except(&mut self, attribute: Option<AttributeKey>) {
|
pub fn mark_all_as_removed_except(&mut self, attribute: Option<AttributeKey>) {
|
||||||
match attribute {
|
match attribute {
|
||||||
None => {
|
None => {
|
||||||
self.inner.iter_mut().for_each(|(_k, v)| v.0 = REMOVE_FLAG.into());
|
self.inner.iter_mut().for_each(|(_k, v)| v.0 = None);
|
||||||
},
|
},
|
||||||
Some(attribute) => {
|
Some(attribute) => {
|
||||||
self.inner.iter_mut().for_each(|(k, v)| {
|
self.inner.iter_mut().for_each(|(k, v)| {
|
||||||
if k != &attribute {
|
if k != &attribute {
|
||||||
v.0 = REMOVE_FLAG.into();
|
v.0 = None;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -68,8 +64,8 @@ impl Attributes {
|
|||||||
// new_attributes
|
// new_attributes
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Remove the empty attribute which value is empty. e.g. {bold: ""}.
|
// Remove the empty attribute which value is None.
|
||||||
pub fn remove_empty(&mut self) { self.inner.retain(|_, v| !should_remove(v)); }
|
pub fn remove_empty(&mut self) { self.inner.retain(|_, v| v.0.is_some()); }
|
||||||
|
|
||||||
pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); }
|
pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); }
|
||||||
|
|
||||||
@ -167,7 +163,7 @@ pub fn invert_attributes(attr: Attributes, base: Attributes) -> Attributes {
|
|||||||
|
|
||||||
let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, _)| {
|
let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, _)| {
|
||||||
if base.get(k) != attr.get(k) && !base.contains_key(k) {
|
if base.get(k) != attr.get(k) && !base.contains_key(k) {
|
||||||
attributes.mark_as_removed(k);
|
attributes.delete(k);
|
||||||
}
|
}
|
||||||
attributes
|
attributes
|
||||||
});
|
});
|
||||||
|
@ -1,13 +1,104 @@
|
|||||||
|
#[rustfmt::skip]
|
||||||
use crate::core::AttributeValue;
|
use crate::core::AttributeValue;
|
||||||
use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
use crate::core::{Attribute, AttributeKey, Attributes};
|
||||||
use std::fmt;
|
use serde::{
|
||||||
|
de,
|
||||||
|
de::{MapAccess, Visitor},
|
||||||
|
ser::SerializeMap,
|
||||||
|
Deserialize,
|
||||||
|
Deserializer,
|
||||||
|
Serialize,
|
||||||
|
Serializer,
|
||||||
|
};
|
||||||
|
use std::{collections::HashMap, fmt, marker::PhantomData, str::ParseBoolError};
|
||||||
|
|
||||||
|
impl Serialize for Attributes {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
if self.is_empty() {
|
||||||
|
return serializer.serialize_none();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut map = serializer.serialize_map(Some(self.inner.len()))?;
|
||||||
|
for (k, v) in &self.inner {
|
||||||
|
if let Some(v) = &v.0 {
|
||||||
|
match k {
|
||||||
|
AttributeKey::Bold
|
||||||
|
| AttributeKey::Italic
|
||||||
|
| AttributeKey::Underline
|
||||||
|
| AttributeKey::StrikeThrough
|
||||||
|
| AttributeKey::CodeBlock
|
||||||
|
| AttributeKey::QuoteBlock => match &v.parse::<bool>() {
|
||||||
|
Ok(value) => map.serialize_entry(k, value)?,
|
||||||
|
Err(e) => log::error!("Serial {:?} failed. {:?}", k, e),
|
||||||
|
},
|
||||||
|
|
||||||
|
AttributeKey::Font
|
||||||
|
| AttributeKey::Size
|
||||||
|
| AttributeKey::Header
|
||||||
|
| AttributeKey::Indent
|
||||||
|
| AttributeKey::Width
|
||||||
|
| AttributeKey::Height => match &v.parse::<i32>() {
|
||||||
|
Ok(value) => map.serialize_entry(k, value)?,
|
||||||
|
Err(e) => log::error!("Serial {:?} failed. {:?}", k, e),
|
||||||
|
},
|
||||||
|
|
||||||
|
AttributeKey::Link
|
||||||
|
| AttributeKey::Color
|
||||||
|
| AttributeKey::Background
|
||||||
|
| AttributeKey::Align
|
||||||
|
| AttributeKey::Bullet
|
||||||
|
| AttributeKey::Ordered
|
||||||
|
| AttributeKey::Checked
|
||||||
|
| AttributeKey::UnChecked
|
||||||
|
| AttributeKey::List => {
|
||||||
|
map.serialize_entry(k, v)?;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Attributes {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Attributes, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct AttributesVisitor;
|
||||||
|
impl<'de> Visitor<'de> for AttributesVisitor {
|
||||||
|
type Value = Attributes;
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("Expect map") }
|
||||||
|
|
||||||
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut attributes = Attributes::new();
|
||||||
|
while let Some(key) = map.next_key::<AttributeKey>()? {
|
||||||
|
let value = map.next_value::<AttributeValue>()?;
|
||||||
|
attributes.add_kv(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(attributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_map(AttributesVisitor {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Serialize for AttributeValue {
|
impl Serialize for AttributeValue {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
serializer.serialize_str(&self.0)
|
match &self.0 {
|
||||||
|
None => serializer.serialize_none(),
|
||||||
|
Some(val) => serializer.serialize_str(val),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,22 +107,95 @@ impl<'de> Deserialize<'de> for AttributeValue {
|
|||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
struct OperationSeqVisitor;
|
struct AttributeValueVisitor;
|
||||||
|
impl<'de> Visitor<'de> for AttributeValueVisitor {
|
||||||
impl<'de> Visitor<'de> for OperationSeqVisitor {
|
|
||||||
type Value = AttributeValue;
|
type Value = AttributeValue;
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("Can't find any visit handler") }
|
||||||
|
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(value.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string") }
|
fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(Some(format!("{}", value))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(Some(format!("{}", value))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(Some(format!("{}", value))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(Some(format!("{}", value))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(Some(format!("{}", value))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u16<E>(self, value: u16) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(Some(format!("{}", value))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(Some(format!("{}", value))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(Some(format!("{}", value))))
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||||
where
|
where
|
||||||
E: de::Error,
|
E: de::Error,
|
||||||
{
|
{
|
||||||
let attribute_value = AttributeValue(s.to_owned());
|
Ok(s.into())
|
||||||
Ok(attribute_value)
|
}
|
||||||
|
|
||||||
|
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(None))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(AttributeValue(None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deserializer.deserialize_str(OperationSeqVisitor)
|
deserializer.deserialize_any(AttributeValueVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
47
rust-lib/flowy-ot/src/core/attributes/macros.rs
Normal file
47
rust-lib/flowy-ot/src/core/attributes/macros.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! inline_attribute {
|
||||||
|
(
|
||||||
|
$key: ident,
|
||||||
|
$value: ty
|
||||||
|
) => {
|
||||||
|
pub fn $key(value: $value) -> Self {
|
||||||
|
Self {
|
||||||
|
key: AttributeKey::$key,
|
||||||
|
value: value.into(),
|
||||||
|
scope: AttributeScope::Inline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! block_attribute {
|
||||||
|
(
|
||||||
|
$key: ident,
|
||||||
|
$value: ident
|
||||||
|
) => {
|
||||||
|
pub fn $key(value: $value) -> Self {
|
||||||
|
Self {
|
||||||
|
key: AttributeKey::$key,
|
||||||
|
value: value.into(),
|
||||||
|
scope: AttributeScope::Block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ignore_attribute {
|
||||||
|
(
|
||||||
|
$key: ident,
|
||||||
|
$value: ident
|
||||||
|
) => {
|
||||||
|
pub fn $key(value: $value) -> Self {
|
||||||
|
Self {
|
||||||
|
key: AttributeKey::$key,
|
||||||
|
value: value.into(),
|
||||||
|
scope: AttributeScope::Ignore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -3,6 +3,9 @@ mod attributes;
|
|||||||
mod attributes_serde;
|
mod attributes_serde;
|
||||||
mod builder;
|
mod builder;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
pub use attribute::*;
|
pub use attribute::*;
|
||||||
pub use attributes::*;
|
pub use attributes::*;
|
||||||
pub use builder::*;
|
pub use builder::*;
|
||||||
|
@ -77,8 +77,8 @@ impl Delta {
|
|||||||
|
|
||||||
pub fn from_json(json: &str) -> Result<Self, OTError> {
|
pub fn from_json(json: &str) -> Result<Self, OTError> {
|
||||||
let delta: Delta = serde_json::from_str(json).map_err(|e| {
|
let delta: Delta = serde_json::from_str(json).map_err(|e| {
|
||||||
log::error!("Deserialize Delta failed: {:?}", e);
|
log::trace!("Deserialize failed: {:?}", e);
|
||||||
log::error!("{:?}", json);
|
log::trace!("{:?}", json);
|
||||||
e
|
e
|
||||||
})?;
|
})?;
|
||||||
Ok(delta)
|
Ok(delta)
|
||||||
|
@ -6,7 +6,7 @@ use flowy_ot::{
|
|||||||
use rand::{prelude::*, Rng as WrappedRng};
|
use rand::{prelude::*, Rng as WrappedRng};
|
||||||
use std::{sync::Once, time::Duration};
|
use std::{sync::Once, time::Duration};
|
||||||
|
|
||||||
const LEVEL: &'static str = "info";
|
const LEVEL: &'static str = "debug";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Display)]
|
#[derive(Clone, Debug, Display)]
|
||||||
pub enum TestOp {
|
pub enum TestOp {
|
||||||
|
@ -35,7 +35,19 @@ fn operation_delete_serialize_test() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_serialize_test() {
|
fn attributes_serialize_test() {
|
||||||
|
let attributes = AttributeBuilder::new()
|
||||||
|
.add(Attribute::Bold(true))
|
||||||
|
.add(Attribute::Italic(true))
|
||||||
|
.build();
|
||||||
|
let retain = OpBuilder::insert("123").attributes(attributes).build();
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&retain).unwrap();
|
||||||
|
eprintln!("{}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_serialize_multi_attribute_test() {
|
||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
|
|
||||||
let attributes = AttributeBuilder::new()
|
let attributes = AttributeBuilder::new()
|
||||||
@ -55,6 +67,27 @@ fn delta_serialize_test() {
|
|||||||
assert_eq!(delta_from_json, delta);
|
assert_eq!(delta_from_json, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_deserialize_test() {
|
||||||
|
let json = r#"[
|
||||||
|
{"retain":2,"attributes":{"italic":true}},
|
||||||
|
{"retain":2,"attributes":{"italic":123}},
|
||||||
|
{"retain":2,"attributes":{"italic":"true","bold":"true"}},
|
||||||
|
{"retain":2,"attributes":{"italic":true,"bold":true}}
|
||||||
|
]"#;
|
||||||
|
let delta = Delta::from_json(json).unwrap();
|
||||||
|
eprintln!("{}", delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_deserialize_null_test() {
|
||||||
|
let json = r#"[
|
||||||
|
{"retain":2,"attributes":{"italic":null}}
|
||||||
|
]"#;
|
||||||
|
let delta = Delta::from_json(json).unwrap();
|
||||||
|
eprintln!("{}", delta);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn document_insert_serde_test() {
|
fn document_insert_serde_test() {
|
||||||
let mut document = Document::new::<PlainDoc>();
|
let mut document = Document::new::<PlainDoc>();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user