diff --git a/rust-lib/flowy-ot/Cargo.toml b/rust-lib/flowy-ot/Cargo.toml index 280a9a3953..a4ba029d72 100644 --- a/rust-lib/flowy-ot/Cargo.toml +++ b/rust-lib/flowy-ot/Cargo.toml @@ -7,6 +7,8 @@ edition = "2018" [dependencies] bytecount = "0.6.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = {version = "1.0"} [dev-dependencies] criterion = "0.3" diff --git a/rust-lib/flowy-ot/src/attributes.rs b/rust-lib/flowy-ot/src/attributes.rs index 67042af043..0c64732960 100644 --- a/rust-lib/flowy-ot/src/attributes.rs +++ b/rust-lib/flowy-ot/src/attributes.rs @@ -1,7 +1,9 @@ use std::collections::{hash_map::RandomState, HashMap}; -#[derive(Debug, Clone, Default, PartialEq)] +#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Attributes { + #[serde(skip_serializing_if = "HashMap::is_empty")] + #[serde(flatten)] inner: HashMap, } diff --git a/rust-lib/flowy-ot/src/delta.rs b/rust-lib/flowy-ot/src/delta.rs index 850abc0a88..f0f93bc0c7 100644 --- a/rust-lib/flowy-ot/src/delta.rs +++ b/rust-lib/flowy-ot/src/delta.rs @@ -53,11 +53,11 @@ impl Delta { } } - fn add(&mut self, op: Operation) { + pub fn add(&mut self, op: Operation) { match op { Operation::Delete(i) => self.delete(i), - Operation::Insert(i) => self.insert(&i.s, i.attrs), - Operation::Retain(r) => self.retain(r.n, r.attrs), + Operation::Insert(i) => self.insert(&i.s, i.attributes), + Operation::Retain(r) => self.retain(r.n, r.attributes), } } @@ -96,7 +96,7 @@ impl Delta { _ => Operation::Insert(s.into()), }; 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) { @@ -108,10 +108,10 @@ impl Delta { if let Some(Operation::Retain(i_last)) = self.ops.last_mut() { i_last.n += n; - i_last.attrs = attrs; + i_last.attributes = attrs; } else { 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) => { inverted.insert( &chars.take(*delete as usize).collect::(), - op.attrs(), + op.attributes(), ); }, } @@ -452,6 +452,6 @@ impl Delta { pub fn get_attrs(operation: &Option) -> Option { match operation { None => None, - Some(operation) => operation.attrs(), + Some(operation) => operation.attributes(), } } diff --git a/rust-lib/flowy-ot/src/lib.rs b/rust-lib/flowy-ot/src/lib.rs index 5ab1009b4e..375cc3c628 100644 --- a/rust-lib/flowy-ot/src/lib.rs +++ b/rust-lib/flowy-ot/src/lib.rs @@ -2,3 +2,4 @@ pub mod attributes; pub mod delta; pub mod errors; pub mod operation; +mod operation_serde; diff --git a/rust-lib/flowy-ot/src/operation.rs b/rust-lib/flowy-ot/src/operation.rs index d9427b504a..83ce744a82 100644 --- a/rust-lib/flowy-ot/src/operation.rs +++ b/rust-lib/flowy-ot/src/operation.rs @@ -29,15 +29,35 @@ impl Operation { } } - pub fn attrs(&self) -> Option { + pub fn attributes(&self) -> Option { match self { Operation::Delete(_) => None, - Operation::Retain(retain) => retain.attrs.clone(), - Operation::Insert(insert) => insert.attrs.clone(), + Operation::Retain(retain) => retain.attributes.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) { + 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 { @@ -54,7 +74,7 @@ impl OpBuilder { pub fn insert(s: String) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) } - pub fn with_attrs(mut self, attrs: Option) -> OpBuilder { + pub fn attributes(mut self, attrs: Option) -> OpBuilder { self.attrs = attrs; self } @@ -63,21 +83,28 @@ impl OpBuilder { let mut operation = self.ty; match &mut operation { Operation::Delete(_) => {}, - Operation::Retain(retain) => retain.attrs = self.attrs, - Operation::Insert(insert) => insert.attrs = self.attrs, + Operation::Retain(retain) => retain.attributes = self.attrs, + Operation::Insert(insert) => insert.attributes = self.attrs, } operation } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Retain { + #[serde(rename(serialize = "retain", deserialize = "retain"))] pub n: u64, - pub(crate) attrs: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) attributes: Option, } impl std::convert::From for Retain { - fn from(n: u64) -> Self { Retain { n, attrs: None } } + fn from(n: u64) -> Self { + Retain { + n, + attributes: None, + } + } } impl Deref for Retain { @@ -90,10 +117,13 @@ impl DerefMut for Retain { 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 { + #[serde(rename(serialize = "insert", deserialize = "insert"))] pub s: String, - pub attrs: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub attributes: Option, } impl Insert { @@ -105,14 +135,19 @@ impl Insert { } impl std::convert::From 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 { fn from(s: &str) -> Self { Insert { s: s.to_owned(), - attrs: None, + attributes: None, } } } diff --git a/rust-lib/flowy-ot/src/operation_serde.rs b/rust-lib/flowy-ot/src/operation_serde.rs new file mode 100644 index 0000000000..6570d74c79 --- /dev/null +++ b/rust-lib/flowy-ot/src/operation_serde.rs @@ -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(&self, serializer: S) -> Result + 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(deserializer: D) -> Result + 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(self, mut map: V) -> Result + 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(&self, serializer: S) -> Result + 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(deserializer: D) -> Result + 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(self, mut seq: A) -> Result + 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) + } +} diff --git a/rust-lib/flowy-ot/tests/mod.rs b/rust-lib/flowy-ot/tests/mod.rs new file mode 100644 index 0000000000..6aa2b0ea90 --- /dev/null +++ b/rust-lib/flowy-ot/tests/mod.rs @@ -0,0 +1,2 @@ +mod helper; +mod serde_test; diff --git a/rust-lib/flowy-ot/tests/op_test.rs b/rust-lib/flowy-ot/tests/op_test.rs index eadfdce0f5..423bddafbf 100644 --- a/rust-lib/flowy-ot/tests/op_test.rs +++ b/rust-lib/flowy-ot/tests/op_test.rs @@ -1,5 +1,3 @@ -mod helper; - use crate::helper::Rng; use bytecount::num_chars; use flowy_ot::{ diff --git a/rust-lib/flowy-ot/tests/serde_test.rs b/rust-lib/flowy-ot/tests/serde_test.rs new file mode 100644 index 0000000000..a5825740dd --- /dev/null +++ b/rust-lib/flowy-ot/tests/serde_test.rs @@ -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); +}