mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
operation & delta serde
This commit is contained in:
parent
eae0c17dda
commit
592244f6b9
@ -7,6 +7,8 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytecount = "0.6.0"
|
bytecount = "0.6.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = {version = "1.0"}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use std::collections::{hash_map::RandomState, HashMap};
|
use std::collections::{hash_map::RandomState, HashMap};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Attributes {
|
pub struct Attributes {
|
||||||
|
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
||||||
|
#[serde(flatten)]
|
||||||
inner: HashMap<String, String>,
|
inner: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,11 +53,11 @@ impl Delta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(&mut self, op: Operation) {
|
pub fn add(&mut self, op: Operation) {
|
||||||
match op {
|
match op {
|
||||||
Operation::Delete(i) => self.delete(i),
|
Operation::Delete(i) => self.delete(i),
|
||||||
Operation::Insert(i) => self.insert(&i.s, i.attrs),
|
Operation::Insert(i) => self.insert(&i.s, i.attributes),
|
||||||
Operation::Retain(r) => self.retain(r.n, r.attrs),
|
Operation::Retain(r) => self.retain(r.n, r.attributes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ impl Delta {
|
|||||||
_ => Operation::Insert(s.into()),
|
_ => Operation::Insert(s.into()),
|
||||||
};
|
};
|
||||||
self.ops
|
self.ops
|
||||||
.push(OpBuilder::new(new_last).with_attrs(attrs).build());
|
.push(OpBuilder::new(new_last).attributes(attrs).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn retain(&mut self, n: u64, attrs: Option<Attributes>) {
|
pub fn retain(&mut self, n: u64, attrs: Option<Attributes>) {
|
||||||
@ -108,10 +108,10 @@ impl Delta {
|
|||||||
|
|
||||||
if let Some(Operation::Retain(i_last)) = self.ops.last_mut() {
|
if let Some(Operation::Retain(i_last)) = self.ops.last_mut() {
|
||||||
i_last.n += n;
|
i_last.n += n;
|
||||||
i_last.attrs = attrs;
|
i_last.attributes = attrs;
|
||||||
} else {
|
} else {
|
||||||
self.ops
|
self.ops
|
||||||
.push(OpBuilder::retain(n).with_attrs(attrs).build());
|
.push(OpBuilder::retain(n).attributes(attrs).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,7 +415,7 @@ impl Delta {
|
|||||||
Operation::Delete(delete) => {
|
Operation::Delete(delete) => {
|
||||||
inverted.insert(
|
inverted.insert(
|
||||||
&chars.take(*delete as usize).collect::<String>(),
|
&chars.take(*delete as usize).collect::<String>(),
|
||||||
op.attrs(),
|
op.attributes(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -452,6 +452,6 @@ impl Delta {
|
|||||||
pub fn get_attrs(operation: &Option<Operation>) -> Option<Attributes> {
|
pub fn get_attrs(operation: &Option<Operation>) -> Option<Attributes> {
|
||||||
match operation {
|
match operation {
|
||||||
None => None,
|
None => None,
|
||||||
Some(operation) => operation.attrs(),
|
Some(operation) => operation.attributes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,3 +2,4 @@ pub mod attributes;
|
|||||||
pub mod delta;
|
pub mod delta;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod operation;
|
pub mod operation;
|
||||||
|
mod operation_serde;
|
||||||
|
@ -29,15 +29,35 @@ impl Operation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attrs(&self) -> Option<Attributes> {
|
pub fn attributes(&self) -> Option<Attributes> {
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(_) => None,
|
Operation::Delete(_) => None,
|
||||||
Operation::Retain(retain) => retain.attrs.clone(),
|
Operation::Retain(retain) => retain.attributes.clone(),
|
||||||
Operation::Insert(insert) => insert.attrs.clone(),
|
Operation::Insert(insert) => insert.attributes.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_plain(&self) -> bool { self.attrs().is_none() }
|
pub fn set_attributes(&mut self, attributes: Option<Attributes>) {
|
||||||
|
match self {
|
||||||
|
Operation::Delete(_) => {},
|
||||||
|
Operation::Retain(retain) => {
|
||||||
|
retain.attributes = attributes;
|
||||||
|
},
|
||||||
|
Operation::Insert(insert) => {
|
||||||
|
insert.attributes = attributes;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_plain(&self) -> bool { self.attributes().is_none() }
|
||||||
|
|
||||||
|
pub fn length(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Operation::Delete(n) => *n,
|
||||||
|
Operation::Retain(r) => r.n,
|
||||||
|
Operation::Insert(i) => i.num_chars(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OpBuilder {
|
pub struct OpBuilder {
|
||||||
@ -54,7 +74,7 @@ impl OpBuilder {
|
|||||||
|
|
||||||
pub fn insert(s: String) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) }
|
pub fn insert(s: String) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) }
|
||||||
|
|
||||||
pub fn with_attrs(mut self, attrs: Option<Attributes>) -> OpBuilder {
|
pub fn attributes(mut self, attrs: Option<Attributes>) -> OpBuilder {
|
||||||
self.attrs = attrs;
|
self.attrs = attrs;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -63,21 +83,28 @@ impl OpBuilder {
|
|||||||
let mut operation = self.ty;
|
let mut operation = self.ty;
|
||||||
match &mut operation {
|
match &mut operation {
|
||||||
Operation::Delete(_) => {},
|
Operation::Delete(_) => {},
|
||||||
Operation::Retain(retain) => retain.attrs = self.attrs,
|
Operation::Retain(retain) => retain.attributes = self.attrs,
|
||||||
Operation::Insert(insert) => insert.attrs = self.attrs,
|
Operation::Insert(insert) => insert.attributes = self.attrs,
|
||||||
}
|
}
|
||||||
operation
|
operation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Retain {
|
pub struct Retain {
|
||||||
|
#[serde(rename(serialize = "retain", deserialize = "retain"))]
|
||||||
pub n: u64,
|
pub n: u64,
|
||||||
pub(crate) attrs: Option<Attributes>,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) attributes: Option<Attributes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<u64> for Retain {
|
impl std::convert::From<u64> for Retain {
|
||||||
fn from(n: u64) -> Self { Retain { n, attrs: None } }
|
fn from(n: u64) -> Self {
|
||||||
|
Retain {
|
||||||
|
n,
|
||||||
|
attributes: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Retain {
|
impl Deref for Retain {
|
||||||
@ -90,10 +117,13 @@ impl DerefMut for Retain {
|
|||||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.n }
|
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.n }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Insert {
|
pub struct Insert {
|
||||||
|
#[serde(rename(serialize = "insert", deserialize = "insert"))]
|
||||||
pub s: String,
|
pub s: String,
|
||||||
pub attrs: Option<Attributes>,
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub attributes: Option<Attributes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Insert {
|
impl Insert {
|
||||||
@ -105,14 +135,19 @@ impl Insert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<String> for Insert {
|
impl std::convert::From<String> for Insert {
|
||||||
fn from(s: String) -> Self { Insert { s, attrs: None } }
|
fn from(s: String) -> Self {
|
||||||
|
Insert {
|
||||||
|
s,
|
||||||
|
attributes: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<&str> for Insert {
|
impl std::convert::From<&str> for Insert {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
Insert {
|
Insert {
|
||||||
s: s.to_owned(),
|
s: s.to_owned(),
|
||||||
attrs: None,
|
attributes: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
137
rust-lib/flowy-ot/src/operation_serde.rs
Normal file
137
rust-lib/flowy-ot/src/operation_serde.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use crate::{attributes::Attributes, delta::Delta, operation::Operation};
|
||||||
|
use serde::{
|
||||||
|
de,
|
||||||
|
de::{MapAccess, SeqAccess, Visitor},
|
||||||
|
ser::{SerializeMap, SerializeSeq},
|
||||||
|
Deserialize,
|
||||||
|
Deserializer,
|
||||||
|
Serialize,
|
||||||
|
Serializer,
|
||||||
|
};
|
||||||
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
|
impl Serialize for Operation {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Operation::Retain(retain) => retain.serialize(serializer),
|
||||||
|
Operation::Delete(i) => {
|
||||||
|
let mut map = serializer.serialize_map(Some(1))?;
|
||||||
|
map.serialize_entry("delete", i)?;
|
||||||
|
map.end()
|
||||||
|
},
|
||||||
|
Operation::Insert(insert) => insert.serialize(serializer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Operation {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Operation, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct OperationVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for OperationVisitor {
|
||||||
|
type Value = Operation;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("an integer between -2^64 and 2^63 or a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
|
||||||
|
where
|
||||||
|
V: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut operation = None;
|
||||||
|
let mut attributes = None;
|
||||||
|
while let Some(key) = map.next_key()? {
|
||||||
|
match key {
|
||||||
|
"delete" => {
|
||||||
|
if operation.is_some() {
|
||||||
|
return Err(de::Error::duplicate_field("operation"));
|
||||||
|
}
|
||||||
|
operation = Some(Operation::Delete(map.next_value()?));
|
||||||
|
},
|
||||||
|
"retain" => {
|
||||||
|
if operation.is_some() {
|
||||||
|
return Err(de::Error::duplicate_field("operation"));
|
||||||
|
}
|
||||||
|
let i: u64 = map.next_value()?;
|
||||||
|
operation = Some(Operation::Retain(i.into()));
|
||||||
|
},
|
||||||
|
"insert" => {
|
||||||
|
if operation.is_some() {
|
||||||
|
return Err(de::Error::duplicate_field("operation"));
|
||||||
|
}
|
||||||
|
let i: String = map.next_value()?;
|
||||||
|
operation = Some(Operation::Insert(i.into()));
|
||||||
|
},
|
||||||
|
"attributes" => {
|
||||||
|
if attributes.is_some() {
|
||||||
|
return Err(de::Error::duplicate_field("attributes"));
|
||||||
|
}
|
||||||
|
let map: Attributes = map.next_value()?;
|
||||||
|
attributes = Some(map);
|
||||||
|
},
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match operation {
|
||||||
|
None => Err(de::Error::missing_field("operation")),
|
||||||
|
Some(mut operation) => {
|
||||||
|
operation.set_attributes(attributes);
|
||||||
|
Ok(operation)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(OperationVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Delta {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let mut seq = serializer.serialize_seq(Some(self.ops.len()))?;
|
||||||
|
for op in self.ops.iter() {
|
||||||
|
seq.serialize_element(op)?;
|
||||||
|
}
|
||||||
|
seq.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Delta {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Delta, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct OperationSeqVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for OperationSeqVisitor {
|
||||||
|
type Value = Delta;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a sequence")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: SeqAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut o = Delta::default();
|
||||||
|
while let Some(op) = seq.next_element()? {
|
||||||
|
o.add(op);
|
||||||
|
}
|
||||||
|
Ok(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_seq(OperationSeqVisitor)
|
||||||
|
}
|
||||||
|
}
|
2
rust-lib/flowy-ot/tests/mod.rs
Normal file
2
rust-lib/flowy-ot/tests/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mod helper;
|
||||||
|
mod serde_test;
|
@ -1,5 +1,3 @@
|
|||||||
mod helper;
|
|
||||||
|
|
||||||
use crate::helper::Rng;
|
use crate::helper::Rng;
|
||||||
use bytecount::num_chars;
|
use bytecount::num_chars;
|
||||||
use flowy_ot::{
|
use flowy_ot::{
|
||||||
|
55
rust-lib/flowy-ot/tests/serde_test.rs
Normal file
55
rust-lib/flowy-ot/tests/serde_test.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use flowy_ot::{
|
||||||
|
attributes::{Attributes, AttributesBuilder},
|
||||||
|
delta::Delta,
|
||||||
|
operation::{OpBuilder, Operation, Retain},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn operation_insert_serialize_test() {
|
||||||
|
let attributes = AttributesBuilder::new().bold().italic().build();
|
||||||
|
let operation = OpBuilder::insert("123".to_owned())
|
||||||
|
.attributes(Some(attributes))
|
||||||
|
.build();
|
||||||
|
let json = serde_json::to_string(&operation).unwrap();
|
||||||
|
eprintln!("{}", json);
|
||||||
|
|
||||||
|
let insert_op: Operation = serde_json::from_str(&json).unwrap();
|
||||||
|
assert_eq!(insert_op, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn operation_retain_serialize_test() {
|
||||||
|
let operation = Operation::Retain(12.into());
|
||||||
|
let json = serde_json::to_string(&operation).unwrap();
|
||||||
|
eprintln!("{}", json);
|
||||||
|
let insert_op: Operation = serde_json::from_str(&json).unwrap();
|
||||||
|
assert_eq!(insert_op, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn operation_delete_serialize_test() {
|
||||||
|
let operation = Operation::Delete(2);
|
||||||
|
let json = serde_json::to_string(&operation).unwrap();
|
||||||
|
let insert_op: Operation = serde_json::from_str(&json).unwrap();
|
||||||
|
assert_eq!(insert_op, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_serialize_test() {
|
||||||
|
let mut delta = Delta::default();
|
||||||
|
|
||||||
|
let attributes = AttributesBuilder::new().bold().italic().build();
|
||||||
|
let retain = OpBuilder::insert("123".to_owned())
|
||||||
|
.attributes(Some(attributes))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
delta.add(retain);
|
||||||
|
delta.add(Operation::Retain(5.into()));
|
||||||
|
delta.add(Operation::Delete(3));
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&delta).unwrap();
|
||||||
|
eprintln!("{}", json);
|
||||||
|
|
||||||
|
let delta_from_json: Delta = serde_json::from_str(&json).unwrap();
|
||||||
|
assert_eq!(delta_from_json, delta);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user