Merge pull request #753 from AppFlowy-IO/feat/lib_ot_documentation2

chore: update lib-ot documentation again and rename some structs
This commit is contained in:
Nathan.fooo 2022-08-02 09:52:26 +08:00 committed by GitHub
commit 5d1497f917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 191 additions and 181 deletions

View File

@ -215,7 +215,7 @@ impl DefaultFolderBuilder {
for app in workspace_rev.apps.iter() { for app in workspace_rev.apps.iter() {
for (index, view) in app.belongings.iter().enumerate() { for (index, view) in app.belongings.iter().enumerate() {
let view_data = if index == 0 { let view_data = if index == 0 {
initial_read_me().to_json_str() initial_read_me().json_str()
} else { } else {
initial_quill_delta_string() initial_quill_delta_string()
}; };

View File

@ -80,7 +80,7 @@ impl FolderEditor {
pub(crate) fn apply_change(&self, change: FolderChange) -> FlowyResult<()> { pub(crate) fn apply_change(&self, change: FolderChange) -> FlowyResult<()> {
let FolderChange { delta, md5 } = change; let FolderChange { delta, md5 } = change;
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.to_json_bytes(); let delta_data = delta.json_bytes();
let revision = Revision::new( let revision = Revision::new(
&self.rev_manager.object_id, &self.rev_manager.object_id,
base_rev_id, base_rev_id,
@ -133,6 +133,6 @@ pub struct FolderRevisionCompactor();
impl RevisionCompactor for FolderRevisionCompactor { impl RevisionCompactor for FolderRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> { fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?; let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
Ok(delta.to_json_bytes()) Ok(delta.json_bytes())
} }
} }

View File

@ -13,7 +13,7 @@ use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision
use flowy_revision::disk::{RevisionRecord, RevisionState}; use flowy_revision::disk::{RevisionRecord, RevisionState};
use flowy_revision::mk_text_block_revision_disk_cache; use flowy_revision::mk_text_block_revision_disk_cache;
use flowy_sync::{client_folder::FolderPad, entities::revision::Revision}; use flowy_sync::{client_folder::FolderPad, entities::revision::Revision};
use lib_ot::core::PlainTextDeltaBuilder; use lib_ot::core::TextDeltaBuilder;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
pub use version_1::{app_sql::*, trash_sql::*, v1_impl::V1Transaction, view_sql::*, workspace_sql::*}; pub use version_1::{app_sql::*, trash_sql::*, v1_impl::V1Transaction, view_sql::*, workspace_sql::*};
@ -110,7 +110,7 @@ impl FolderPersistence {
pub async fn save_folder(&self, user_id: &str, folder_id: &FolderId, folder: FolderPad) -> FlowyResult<()> { pub async fn save_folder(&self, user_id: &str, folder_id: &FolderId, folder: FolderPad) -> FlowyResult<()> {
let pool = self.database.db_pool()?; let pool = self.database.db_pool()?;
let json = folder.to_json()?; let json = folder.to_json()?;
let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_json_bytes(); let delta_data = TextDeltaBuilder::new().insert(&json).build().json_bytes();
let revision = Revision::initial_revision(user_id, folder_id.as_ref(), delta_data); let revision = Revision::initial_revision(user_id, folder_id.as_ref(), delta_data);
let record = RevisionRecord { let record = RevisionRecord {
revision, revision,

View File

@ -10,7 +10,7 @@ use flowy_sync::{
}, },
}; };
use lib_infra::future::{BoxResultFuture, FutureResult}; use lib_infra::future::{BoxResultFuture, FutureResult};
use lib_ot::core::{OperationTransform, PhantomAttributes, PlainTextDelta}; use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta};
use parking_lot::RwLock; use parking_lot::RwLock;
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
@ -56,7 +56,7 @@ struct FolderConflictResolver {
} }
impl ConflictResolver<PhantomAttributes> for FolderConflictResolver { impl ConflictResolver<PhantomAttributes> for FolderConflictResolver {
fn compose_delta(&self, delta: PlainTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> { fn compose_delta(&self, delta: TextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
let folder_pad = self.folder_pad.clone(); let folder_pad = self.folder_pad.clone();
Box::pin(async move { Box::pin(async move {
let md5 = folder_pad.write().compose_remote_delta(delta)?; let md5 = folder_pad.write().compose_remote_delta(delta)?;
@ -64,15 +64,12 @@ impl ConflictResolver<PhantomAttributes> for FolderConflictResolver {
}) })
} }
fn transform_delta( fn transform_delta(&self, delta: TextDelta) -> BoxResultFuture<TransformDeltas<PhantomAttributes>, FlowyError> {
&self,
delta: PlainTextDelta,
) -> BoxResultFuture<TransformDeltas<PhantomAttributes>, FlowyError> {
let folder_pad = self.folder_pad.clone(); let folder_pad = self.folder_pad.clone();
Box::pin(async move { Box::pin(async move {
let read_guard = folder_pad.read(); let read_guard = folder_pad.read();
let mut server_prime: Option<PlainTextDelta> = None; let mut server_prime: Option<TextDelta> = None;
let client_prime: PlainTextDelta; let client_prime: TextDelta;
if read_guard.is_empty() { if read_guard.is_empty() {
// Do nothing // Do nothing
client_prime = delta; client_prime = delta;
@ -89,7 +86,7 @@ impl ConflictResolver<PhantomAttributes> for FolderConflictResolver {
}) })
} }
fn reset_delta(&self, delta: PlainTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> { fn reset_delta(&self, delta: TextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
let folder_pad = self.folder_pad.clone(); let folder_pad = self.folder_pad.clone();
Box::pin(async move { Box::pin(async move {
let md5 = folder_pad.write().reset_folder(delta)?; let md5 = folder_pad.write().reset_folder(delta)?;

View File

@ -192,7 +192,7 @@ pub async fn make_grid_view_data(
// Create grid's block // Create grid's block
let grid_block_delta = make_grid_block_delta(block_meta_data); let grid_block_delta = make_grid_block_delta(block_meta_data);
let block_delta_data = grid_block_delta.to_json_bytes(); let block_delta_data = grid_block_delta.json_bytes();
let repeated_revision: RepeatedRevision = let repeated_revision: RepeatedRevision =
Revision::initial_revision(user_id, block_id, block_delta_data).into(); Revision::initial_revision(user_id, block_id, block_delta_data).into();
let _ = grid_manager.create_grid_block(&block_id, repeated_revision).await?; let _ = grid_manager.create_grid_block(&block_id, repeated_revision).await?;
@ -202,7 +202,7 @@ pub async fn make_grid_view_data(
// Create grid // Create grid
let grid_meta_delta = make_grid_delta(&grid_rev); let grid_meta_delta = make_grid_delta(&grid_rev);
let grid_delta_data = grid_meta_delta.to_json_bytes(); let grid_delta_data = grid_meta_delta.json_bytes();
let repeated_revision: RepeatedRevision = let repeated_revision: RepeatedRevision =
Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into(); Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
let _ = grid_manager.create_grid(view_id, repeated_revision).await?; let _ = grid_manager.create_grid(view_id, repeated_revision).await?;

View File

@ -161,7 +161,7 @@ impl GridBlockRevisionEditor {
let GridBlockMetaChange { delta, md5 } = change; let GridBlockMetaChange { delta, md5 } = change;
let user_id = self.user_id.clone(); let user_id = self.user_id.clone();
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.to_json_bytes(); let delta_data = delta.json_bytes();
let revision = Revision::new( let revision = Revision::new(
&self.rev_manager.object_id, &self.rev_manager.object_id,
base_rev_id, base_rev_id,
@ -201,6 +201,6 @@ pub struct GridBlockRevisionCompactor();
impl RevisionCompactor for GridBlockRevisionCompactor { impl RevisionCompactor for GridBlockRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> { fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?; let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
Ok(delta.to_json_bytes()) Ok(delta.json_bytes())
} }
} }

View File

@ -572,7 +572,7 @@ impl GridRevisionEditor {
let GridChangeset { delta, md5 } = change; let GridChangeset { delta, md5 } = change;
let user_id = self.user.user_id()?; let user_id = self.user.user_id()?;
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.to_json_bytes(); let delta_data = delta.json_bytes();
let revision = Revision::new( let revision = Revision::new(
&self.rev_manager.object_id, &self.rev_manager.object_id,
base_rev_id, base_rev_id,
@ -664,7 +664,7 @@ pub struct GridRevisionCompactor();
impl RevisionCompactor for GridRevisionCompactor { impl RevisionCompactor for GridRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> { fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?; let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
Ok(delta.to_json_bytes()) Ok(delta.json_bytes())
} }
} }

View File

@ -9,7 +9,7 @@ use flowy_revision::{mk_grid_block_revision_disk_cache, RevisionLoader, Revision
use flowy_sync::client_grid::{make_grid_rev_json_str, GridRevisionPad}; use flowy_sync::client_grid::{make_grid_rev_json_str, GridRevisionPad};
use flowy_sync::entities::revision::Revision; use flowy_sync::entities::revision::Revision;
use lib_ot::core::PlainTextDeltaBuilder; use lib_ot::core::TextDeltaBuilder;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
@ -48,7 +48,7 @@ impl GridMigration {
let pool = self.database.db_pool()?; let pool = self.database.db_pool()?;
let grid_rev_pad = self.get_grid_revision_pad(grid_id).await?; let grid_rev_pad = self.get_grid_revision_pad(grid_id).await?;
let json = grid_rev_pad.json_str()?; let json = grid_rev_pad.json_str()?;
let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_json_bytes(); let delta_data = TextDeltaBuilder::new().insert(&json).build().json_bytes();
let revision = Revision::initial_revision(&user_id, grid_id, delta_data); let revision = Revision::initial_revision(&user_id, grid_id, delta_data);
let record = RevisionRecord::new(revision); let record = RevisionRecord::new(revision);
// //

View File

@ -154,7 +154,7 @@ where
&rev_manager.object_id, &rev_manager.object_id,
base_rev_id, base_rev_id,
rev_id, rev_id,
client_delta.to_json_bytes(), client_delta.json_bytes(),
user_id, user_id,
md5.clone(), md5.clone(),
); );
@ -166,7 +166,7 @@ where
&rev_manager.object_id, &rev_manager.object_id,
base_rev_id, base_rev_id,
rev_id, rev_id,
server_delta.to_json_bytes(), server_delta.json_bytes(),
user_id, user_id,
md5, md5,
); );

View File

@ -238,7 +238,7 @@ impl RevisionObjectBuilder for TextBlockInfoBuilder {
Result::<DocumentPB, FlowyError>::Ok(DocumentPB { Result::<DocumentPB, FlowyError>::Ok(DocumentPB {
block_id: object_id.to_owned(), block_id: object_id.to_owned(),
text: delta.to_json_str(), text: delta.json_str(),
rev_id, rev_id,
base_rev_id, base_rev_id,
}) })

View File

@ -175,7 +175,7 @@ impl EditBlockQueue {
} }
async fn save_local_delta(&self, delta: RichTextDelta, md5: String) -> Result<RevId, FlowyError> { async fn save_local_delta(&self, delta: RichTextDelta, md5: String) -> Result<RevId, FlowyError> {
let delta_data = delta.to_json_bytes(); let delta_data = delta.json_bytes();
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let user_id = self.user.user_id()?; let user_id = self.user.user_id()?;
let revision = Revision::new( let revision = Revision::new(
@ -195,7 +195,7 @@ pub(crate) struct TextBlockRevisionCompactor();
impl RevisionCompactor for TextBlockRevisionCompactor { impl RevisionCompactor for TextBlockRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> { fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<RichTextAttributes>(revisions)?; let delta = make_delta_from_revisions::<RichTextAttributes>(revisions)?;
Ok(delta.to_json_bytes()) Ok(delta.json_bytes())
} }
} }

View File

@ -75,7 +75,7 @@ impl TextBlockEditorTest {
let delta = self.editor.text_block_delta().await.unwrap(); let delta = self.editor.text_block_delta().await.unwrap();
if expected_delta != delta { if expected_delta != delta {
eprintln!("✅ expect: {}", expected,); eprintln!("✅ expect: {}", expected,);
eprintln!("❌ receive: {}", delta.to_json_str()); eprintln!("❌ receive: {}", delta.json_str());
} }
assert_eq!(expected_delta, delta); assert_eq!(expected_delta, delta);
} }

View File

@ -762,19 +762,19 @@ fn attributes_preserve_list_format_on_merge() {
#[test] #[test]
fn delta_compose() { fn delta_compose() {
let mut delta = RichTextDelta::from_json_str(r#"[{"insert":"\n"}]"#).unwrap(); let mut delta = RichTextDelta::from_json(r#"[{"insert":"\n"}]"#).unwrap();
let deltas = vec![ let deltas = vec![
RichTextDelta::from_json_str(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(), RichTextDelta::from_json(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(),
RichTextDelta::from_json_str(r#"[{"insert":"a"}]"#).unwrap(), RichTextDelta::from_json(r#"[{"insert":"a"}]"#).unwrap(),
RichTextDelta::from_json_str(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(), RichTextDelta::from_json(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(),
RichTextDelta::from_json_str(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(), RichTextDelta::from_json(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(),
]; ];
for d in deltas { for d in deltas {
delta = delta.compose(&d).unwrap(); delta = delta.compose(&d).unwrap();
} }
assert_eq!( assert_eq!(
delta.to_json_str(), delta.json_str(),
r#"[{"insert":"a"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"\n"}]"# r#"[{"insert":"a"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"\n"}]"#
); );

View File

@ -108,20 +108,20 @@ impl TestBuilder {
TestOp::Insert(delta_i, s, index) => { TestOp::Insert(delta_i, s, index) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let delta = document.insert(*index, s).unwrap(); let delta = document.insert(*index, s).unwrap();
tracing::debug!("Insert delta: {}", delta.to_json_str()); tracing::debug!("Insert delta: {}", delta.json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Delete(delta_i, iv) => { TestOp::Delete(delta_i, iv) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let delta = document.replace(*iv, "").unwrap(); let delta = document.replace(*iv, "").unwrap();
tracing::trace!("Delete delta: {}", delta.to_json_str()); tracing::trace!("Delete delta: {}", delta.json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Replace(delta_i, iv, s) => { TestOp::Replace(delta_i, iv, s) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let delta = document.replace(*iv, s).unwrap(); let delta = document.replace(*iv, s).unwrap();
tracing::trace!("Replace delta: {}", delta.to_json_str()); tracing::trace!("Replace delta: {}", delta.json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::InsertBold(delta_i, s, iv) => { TestOp::InsertBold(delta_i, s, iv) => {
@ -133,7 +133,7 @@ impl TestBuilder {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Bold(*enable); let attribute = RichTextAttribute::Bold(*enable);
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Bold delta: {}", delta.to_json_str()); tracing::trace!("Bold delta: {}", delta.json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Italic(delta_i, iv, enable) => { TestOp::Italic(delta_i, iv, enable) => {
@ -143,28 +143,28 @@ impl TestBuilder {
false => RichTextAttribute::Italic(false), false => RichTextAttribute::Italic(false),
}; };
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Italic delta: {}", delta.to_json_str()); tracing::trace!("Italic delta: {}", delta.json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Header(delta_i, iv, level) => { TestOp::Header(delta_i, iv, level) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Header(*level); let attribute = RichTextAttribute::Header(*level);
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Header delta: {}", delta.to_json_str()); tracing::trace!("Header delta: {}", delta.json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Link(delta_i, iv, link) => { TestOp::Link(delta_i, iv, link) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Link(link.to_owned()); let attribute = RichTextAttribute::Link(link.to_owned());
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Link delta: {}", delta.to_json_str()); tracing::trace!("Link delta: {}", delta.json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Bullet(delta_i, iv, enable) => { TestOp::Bullet(delta_i, iv, enable) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Bullet(*enable); let attribute = RichTextAttribute::Bullet(*enable);
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::debug!("Bullet delta: {}", delta.to_json_str()); tracing::debug!("Bullet delta: {}", delta.json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
@ -194,15 +194,15 @@ impl TestBuilder {
let delta_a = &self.documents[*delta_a_i].delta(); let delta_a = &self.documents[*delta_a_i].delta();
let delta_b = &self.documents[*delta_b_i].delta(); let delta_b = &self.documents[*delta_b_i].delta();
tracing::debug!("Invert: "); tracing::debug!("Invert: ");
tracing::debug!("a: {}", delta_a.to_json_str()); tracing::debug!("a: {}", delta_a.json_str());
tracing::debug!("b: {}", delta_b.to_json_str()); tracing::debug!("b: {}", delta_b.json_str());
let (_, b_prime) = delta_a.transform(delta_b).unwrap(); let (_, b_prime) = delta_a.transform(delta_b).unwrap();
let undo = b_prime.invert(delta_a); let undo = b_prime.invert(delta_a);
let new_delta = delta_a.compose(&b_prime).unwrap(); let new_delta = delta_a.compose(&b_prime).unwrap();
tracing::debug!("new delta: {}", new_delta.to_json_str()); tracing::debug!("new delta: {}", new_delta.json_str());
tracing::debug!("undo delta: {}", undo.to_json_str()); tracing::debug!("undo delta: {}", undo.json_str());
let new_delta_after_undo = new_delta.compose(&undo).unwrap(); let new_delta_after_undo = new_delta.compose(&undo).unwrap();
@ -238,7 +238,7 @@ impl TestBuilder {
} }
TestOp::AssertPrimeJson(doc_i, expected) => { TestOp::AssertPrimeJson(doc_i, expected) => {
let prime_json = self.primes[*doc_i].as_ref().unwrap().to_json_str(); let prime_json = self.primes[*doc_i].as_ref().unwrap().json_str();
let expected_prime: RichTextDelta = serde_json::from_str(expected).unwrap(); let expected_prime: RichTextDelta = serde_json::from_str(expected).unwrap();
let target_prime: RichTextDelta = serde_json::from_str(&prime_json).unwrap(); let target_prime: RichTextDelta = serde_json::from_str(&prime_json).unwrap();

View File

@ -342,13 +342,10 @@ fn apply_1000() {
#[test] #[test]
fn apply_test() { fn apply_test() {
let s = "hello"; let s = "hello";
let delta_a = PlainTextDeltaBuilder::new().insert(s).build(); let delta_a = TextDeltaBuilder::new().insert(s).build();
let delta_b = PlainTextDeltaBuilder::new() let delta_b = TextDeltaBuilder::new().retain(s.len()).insert(", AppFlowy").build();
.retain(s.len())
.insert(", AppFlowy")
.build();
let after_a = delta_a.content_str().unwrap(); let after_a = delta_a.content().unwrap();
let after_b = delta_b.apply(&after_a).unwrap(); let after_b = delta_b.apply(&after_a).unwrap();
assert_eq!("hello, AppFlowy", &after_b); assert_eq!("hello, AppFlowy", &after_b);
} }
@ -384,7 +381,7 @@ fn invert() {
#[test] #[test]
fn invert_test() { fn invert_test() {
let s = "hello world"; let s = "hello world";
let delta = PlainTextDeltaBuilder::new().insert(s).build(); let delta = TextDeltaBuilder::new().insert(s).build();
let invert_delta = delta.invert_str(""); let invert_delta = delta.invert_str("");
assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len); assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len);
assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len); assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len);

View File

@ -65,7 +65,7 @@ fn delta_serialize_multi_attribute_test() {
let json = serde_json::to_string(&delta).unwrap(); let json = serde_json::to_string(&delta).unwrap();
eprintln!("{}", json); eprintln!("{}", json);
let delta_from_json = Delta::from_json_str(&json).unwrap(); let delta_from_json = Delta::from_json(&json).unwrap();
assert_eq!(delta_from_json, delta); assert_eq!(delta_from_json, delta);
} }
@ -77,7 +77,7 @@ fn delta_deserialize_test() {
{"retain":2,"attributes":{"italic":"true","bold":"true"}}, {"retain":2,"attributes":{"italic":"true","bold":"true"}},
{"retain":2,"attributes":{"italic":true,"bold":true}} {"retain":2,"attributes":{"italic":true,"bold":true}}
]"#; ]"#;
let delta = RichTextDelta::from_json_str(json).unwrap(); let delta = RichTextDelta::from_json(json).unwrap();
eprintln!("{}", delta); eprintln!("{}", delta);
} }
@ -86,13 +86,13 @@ fn delta_deserialize_null_test() {
let json = r#"[ let json = r#"[
{"retain":7,"attributes":{"bold":null}} {"retain":7,"attributes":{"bold":null}}
]"#; ]"#;
let delta1 = RichTextDelta::from_json_str(json).unwrap(); let delta1 = RichTextDelta::from_json(json).unwrap();
let mut attribute = RichTextAttribute::Bold(true); let mut attribute = RichTextAttribute::Bold(true);
attribute.value = RichTextAttributeValue(None); attribute.value = RichTextAttributeValue(None);
let delta2 = DeltaBuilder::new().retain_with_attributes(7, attribute.into()).build(); let delta2 = DeltaBuilder::new().retain_with_attributes(7, attribute.into()).build();
assert_eq!(delta2.to_json_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#); assert_eq!(delta2.json_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#);
assert_eq!(delta1, delta2); assert_eq!(delta1, delta2);
} }

View File

@ -7,13 +7,13 @@ pub fn initial_quill_delta() -> RichTextDelta {
#[inline] #[inline]
pub fn initial_quill_delta_string() -> String { pub fn initial_quill_delta_string() -> String {
initial_quill_delta().to_json_str() initial_quill_delta().json_str()
} }
#[inline] #[inline]
pub fn initial_read_me() -> RichTextDelta { pub fn initial_read_me() -> RichTextDelta {
let json = include_str!("READ_ME.json"); let json = include_str!("READ_ME.json");
RichTextDelta::from_json_str(json).unwrap() RichTextDelta::from_json(json).unwrap()
} }
#[cfg(test)] #[cfg(test)]
@ -22,6 +22,6 @@ mod tests {
#[test] #[test]
fn load_read_me() { fn load_read_me() {
println!("{}", initial_read_me().to_json_str()); println!("{}", initial_read_me().json_str());
} }
} }

View File

@ -55,16 +55,16 @@ impl ClientDocument {
} }
pub fn from_json(json: &str) -> Result<Self, CollaborateError> { pub fn from_json(json: &str) -> Result<Self, CollaborateError> {
let delta = RichTextDelta::from_json_str(json)?; let delta = RichTextDelta::from_json(json)?;
Ok(Self::from_delta(delta)) Ok(Self::from_delta(delta))
} }
pub fn delta_str(&self) -> String { pub fn delta_str(&self) -> String {
self.delta.to_json_str() self.delta.json_str()
} }
pub fn to_bytes(&self) -> Bytes { pub fn to_bytes(&self) -> Bytes {
self.delta.to_json_bytes() self.delta.json_bytes()
} }
pub fn to_plain_string(&self) -> String { pub fn to_plain_string(&self) -> String {
@ -85,7 +85,7 @@ impl ClientDocument {
} }
pub fn set_delta(&mut self, data: RichTextDelta) { pub fn set_delta(&mut self, data: RichTextDelta) {
tracing::trace!("document: {}", data.to_json_str()); tracing::trace!("document: {}", data.json_str());
self.delta = data; self.delta = data;
match &self.notify { match &self.notify {
@ -97,7 +97,7 @@ impl ClientDocument {
} }
pub fn compose_delta(&mut self, delta: RichTextDelta) -> Result<(), CollaborateError> { pub fn compose_delta(&mut self, delta: RichTextDelta) -> Result<(), CollaborateError> {
tracing::trace!("{} compose {}", &self.delta.to_json_str(), delta.to_json_str()); tracing::trace!("{} compose {}", &self.delta.json_str(), delta.json_str());
let composed_delta = self.delta.compose(&delta)?; let composed_delta = self.delta.compose(&delta)?;
let mut undo_delta = delta.invert(&self.delta); let mut undo_delta = delta.invert(&self.delta);

View File

@ -7,7 +7,7 @@ use crate::{
}; };
use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision}; use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision};
use lib_ot::core::{PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder}; use lib_ot::core::{PhantomAttributes, TextDelta, TextDeltaBuilder};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
@ -35,13 +35,13 @@ impl FolderPadBuilder {
self self
} }
pub(crate) fn build_with_delta(self, mut delta: PlainTextDelta) -> CollaborateResult<FolderPad> { pub(crate) fn build_with_delta(self, mut delta: TextDelta) -> CollaborateResult<FolderPad> {
if delta.is_empty() { if delta.is_empty() {
delta = default_folder_delta(); delta = default_folder_delta();
} }
// TODO: Reconvert from history if delta.to_str() failed. // TODO: Reconvert from history if delta.to_str() failed.
let content = delta.content_str()?; let content = delta.content()?;
let mut folder: FolderPad = serde_json::from_str(&content).map_err(|e| { let mut folder: FolderPad = serde_json::from_str(&content).map_err(|e| {
tracing::error!("Deserialize folder from {} failed", content); tracing::error!("Deserialize folder from {} failed", content);
return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e)); return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e));
@ -61,7 +61,7 @@ impl FolderPadBuilder {
Ok(FolderPad { Ok(FolderPad {
workspaces: self.workspaces, workspaces: self.workspaces,
trash: self.trash, trash: self.trash,
delta: PlainTextDeltaBuilder::new().insert(&json).build(), delta: TextDeltaBuilder::new().insert(&json).build(),
}) })
} }
} }

View File

@ -295,7 +295,7 @@ impl FolderPad {
} }
pub fn md5(&self) -> String { pub fn md5(&self) -> String {
md5(&self.delta.to_json_bytes()) md5(&self.delta.json_bytes())
} }
pub fn to_json(&self) -> CollaborateResult<String> { pub fn to_json(&self) -> CollaborateResult<String> {
@ -400,14 +400,14 @@ impl FolderPad {
} }
pub fn default_folder_delta() -> FolderDelta { pub fn default_folder_delta() -> FolderDelta {
PlainTextDeltaBuilder::new() TextDeltaBuilder::new()
.insert(r#"{"workspaces":[],"trash":[]}"#) .insert(r#"{"workspaces":[],"trash":[]}"#)
.build() .build()
} }
pub fn initial_folder_delta(folder_pad: &FolderPad) -> CollaborateResult<FolderDelta> { pub fn initial_folder_delta(folder_pad: &FolderPad) -> CollaborateResult<FolderDelta> {
let json = folder_pad.to_json()?; let json = folder_pad.to_json()?;
let delta = PlainTextDeltaBuilder::new().insert(&json).build(); let delta = TextDeltaBuilder::new().insert(&json).build();
Ok(delta) Ok(delta)
} }
@ -434,7 +434,7 @@ mod tests {
use chrono::Utc; use chrono::Utc;
use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision}; use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision};
use lib_ot::core::{OperationTransform, PlainTextDelta, PlainTextDeltaBuilder}; use lib_ot::core::{OperationTransform, TextDelta, TextDeltaBuilder};
#[test] #[test]
fn folder_add_workspace() { fn folder_add_workspace() {
@ -749,7 +749,7 @@ mod tests {
fn test_folder() -> (FolderPad, FolderDelta, WorkspaceRevision) { fn test_folder() -> (FolderPad, FolderDelta, WorkspaceRevision) {
let mut folder = FolderPad::default(); let mut folder = FolderPad::default();
let folder_json = serde_json::to_string(&folder).unwrap(); let folder_json = serde_json::to_string(&folder).unwrap();
let mut delta = PlainTextDeltaBuilder::new().insert(&folder_json).build(); let mut delta = TextDeltaBuilder::new().insert(&folder_json).build();
let mut workspace_rev = WorkspaceRevision::default(); let mut workspace_rev = WorkspaceRevision::default();
workspace_rev.name = "😁 my first workspace".to_owned(); workspace_rev.name = "😁 my first workspace".to_owned();
@ -791,7 +791,7 @@ mod tests {
fn test_trash() -> (FolderPad, FolderDelta, TrashRevision) { fn test_trash() -> (FolderPad, FolderDelta, TrashRevision) {
let mut folder = FolderPad::default(); let mut folder = FolderPad::default();
let folder_json = serde_json::to_string(&folder).unwrap(); let folder_json = serde_json::to_string(&folder).unwrap();
let mut delta = PlainTextDeltaBuilder::new().insert(&folder_json).build(); let mut delta = TextDeltaBuilder::new().insert(&folder_json).build();
let mut trash_rev = TrashRevision::default(); let mut trash_rev = TrashRevision::default();
trash_rev.name = "🚽 my first trash".to_owned(); trash_rev.name = "🚽 my first trash".to_owned();
@ -810,7 +810,7 @@ mod tests {
(folder, delta, trash_rev) (folder, delta, trash_rev)
} }
fn make_folder_from_delta(mut initial_delta: FolderDelta, deltas: Vec<PlainTextDelta>) -> FolderPad { fn make_folder_from_delta(mut initial_delta: FolderDelta, deltas: Vec<TextDelta>) -> FolderPad {
for delta in deltas { for delta in deltas {
initial_delta = initial_delta.compose(&delta).unwrap(); initial_delta = initial_delta.compose(&delta).unwrap();
} }

View File

@ -4,13 +4,13 @@ use crate::util::{cal_diff, make_delta_from_revisions};
use flowy_grid_data_model::revision::{ use flowy_grid_data_model::revision::{
gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision, gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision,
}; };
use lib_ot::core::{OperationTransform, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder}; use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
pub type GridBlockRevisionDelta = PlainTextDelta; pub type GridBlockRevisionDelta = TextDelta;
pub type GridBlockRevisionDeltaBuilder = PlainTextDeltaBuilder; pub type GridBlockRevisionDeltaBuilder = TextDeltaBuilder;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GridBlockRevisionPad { pub struct GridBlockRevisionPad {
@ -46,7 +46,7 @@ impl GridBlockRevisionPad {
} }
pub fn from_delta(delta: GridBlockRevisionDelta) -> CollaborateResult<Self> { pub fn from_delta(delta: GridBlockRevisionDelta) -> CollaborateResult<Self> {
let s = delta.content_str()?; let s = delta.content()?;
let block_revision: GridBlockRevision = serde_json::from_str(&s).map_err(|e| { let block_revision: GridBlockRevision = serde_json::from_str(&s).map_err(|e| {
let msg = format!("Deserialize delta to block meta failed: {}", e); let msg = format!("Deserialize delta to block meta failed: {}", e);
tracing::error!("{}", s); tracing::error!("{}", s);
@ -198,7 +198,7 @@ impl GridBlockRevisionPad {
match cal_diff::<PhantomAttributes>(old, new) { match cal_diff::<PhantomAttributes>(old, new) {
None => Ok(None), None => Ok(None),
Some(delta) => { Some(delta) => {
tracing::trace!("[GridBlockMeta] Composing delta {}", delta.to_json_str()); tracing::trace!("[GridBlockMeta] Composing delta {}", delta.json_str());
// tracing::debug!( // tracing::debug!(
// "[GridBlockMeta] current delta: {}", // "[GridBlockMeta] current delta: {}",
// self.delta.to_str().unwrap_or_else(|_| "".to_string()) // self.delta.to_str().unwrap_or_else(|_| "".to_string())
@ -231,11 +231,11 @@ impl GridBlockRevisionPad {
} }
pub fn md5(&self) -> String { pub fn md5(&self) -> String {
md5(&self.delta.to_json_bytes()) md5(&self.delta.json_bytes())
} }
pub fn delta_str(&self) -> String { pub fn delta_str(&self) -> String {
self.delta.to_json_str() self.delta.json_str()
} }
} }
@ -247,12 +247,12 @@ pub struct GridBlockMetaChange {
pub fn make_grid_block_delta(block_rev: &GridBlockRevision) -> GridBlockRevisionDelta { pub fn make_grid_block_delta(block_rev: &GridBlockRevision) -> GridBlockRevisionDelta {
let json = serde_json::to_string(&block_rev).unwrap(); let json = serde_json::to_string(&block_rev).unwrap();
PlainTextDeltaBuilder::new().insert(&json).build() TextDeltaBuilder::new().insert(&json).build()
} }
pub fn make_grid_block_revisions(user_id: &str, grid_block_meta_data: &GridBlockRevision) -> RepeatedRevision { pub fn make_grid_block_revisions(user_id: &str, grid_block_meta_data: &GridBlockRevision) -> RepeatedRevision {
let delta = make_grid_block_delta(grid_block_meta_data); let delta = make_grid_block_delta(grid_block_meta_data);
let bytes = delta.to_json_bytes(); let bytes = delta.json_bytes();
let revision = Revision::initial_revision(user_id, &grid_block_meta_data.block_id, bytes); let revision = Revision::initial_revision(user_id, &grid_block_meta_data.block_id, bytes);
revision.into() revision.into()
} }
@ -289,7 +289,7 @@ mod tests {
let change = pad.add_row_rev(row.clone(), None).unwrap().unwrap(); let change = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
assert_eq!(pad.rows.first().unwrap().as_ref(), &row); assert_eq!(pad.rows.first().unwrap().as_ref(), &row);
assert_eq!( assert_eq!(
change.delta.to_json_str(), change.delta.json_str(),
r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"# r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
); );
} }
@ -303,19 +303,19 @@ mod tests {
let change = pad.add_row_rev(row_1.clone(), None).unwrap().unwrap(); let change = pad.add_row_rev(row_1.clone(), None).unwrap().unwrap();
assert_eq!( assert_eq!(
change.delta.to_json_str(), change.delta.json_str(),
r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"# r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
); );
let change = pad.add_row_rev(row_2.clone(), None).unwrap().unwrap(); let change = pad.add_row_rev(row_2.clone(), None).unwrap().unwrap();
assert_eq!( assert_eq!(
change.delta.to_json_str(), change.delta.json_str(),
r#"[{"retain":90},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"# r#"[{"retain":90},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
); );
let change = pad.add_row_rev(row_3.clone(), Some("2".to_string())).unwrap().unwrap(); let change = pad.add_row_rev(row_3.clone(), Some("2".to_string())).unwrap().unwrap();
assert_eq!( assert_eq!(
change.delta.to_json_str(), change.delta.json_str(),
r#"[{"retain":157},{"insert":",{\"id\":\"3\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"# r#"[{"retain":157},{"insert":",{\"id\":\"3\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
); );
@ -380,10 +380,7 @@ mod tests {
let _ = pad.add_row_rev(row.clone(), None).unwrap().unwrap(); let _ = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
let change = pad.delete_rows(vec![Cow::Borrowed(&row.id)]).unwrap().unwrap(); let change = pad.delete_rows(vec![Cow::Borrowed(&row.id)]).unwrap().unwrap();
assert_eq!( assert_eq!(change.delta.json_str(), r#"[{"retain":24},{"delete":66},{"retain":2}]"#);
change.delta.to_json_str(),
r#"[{"retain":24},{"delete":66},{"retain":2}]"#
);
assert_eq!(pad.delta_str(), pre_delta_str); assert_eq!(pad.delta_str(), pre_delta_str);
} }
@ -410,7 +407,7 @@ mod tests {
let change = pad.update_row(changeset).unwrap().unwrap(); let change = pad.update_row(changeset).unwrap().unwrap();
assert_eq!( assert_eq!(
change.delta.to_json_str(), change.delta.json_str(),
r#"[{"retain":69},{"insert":"10"},{"retain":15},{"insert":"tru"},{"delete":4},{"retain":4}]"# r#"[{"retain":69},{"insert":"10"},{"retain":15},{"insert":"tru"},{"delete":4},{"retain":4}]"#
); );
@ -421,8 +418,7 @@ mod tests {
} }
fn test_pad() -> GridBlockRevisionPad { fn test_pad() -> GridBlockRevisionPad {
let delta = let delta = GridBlockRevisionDelta::from_json(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap();
GridBlockRevisionDelta::from_json_str(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap();
GridBlockRevisionPad::from_delta(delta).unwrap() GridBlockRevisionPad::from_delta(delta).unwrap()
} }
} }

View File

@ -9,12 +9,12 @@ use flowy_grid_data_model::revision::{
GridLayoutRevision, GridRevision, GridSettingRevision, GridSortRevision, GridLayoutRevision, GridRevision, GridSettingRevision, GridSortRevision,
}; };
use lib_infra::util::move_vec_element; use lib_infra::util::move_vec_element;
use lib_ot::core::{OperationTransform, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder}; use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
pub type GridRevisionDelta = PlainTextDelta; pub type GridRevisionDelta = TextDelta;
pub type GridRevisionDeltaBuilder = PlainTextDeltaBuilder; pub type GridRevisionDeltaBuilder = TextDeltaBuilder;
pub struct GridRevisionPad { pub struct GridRevisionPad {
grid_rev: Arc<GridRevision>, grid_rev: Arc<GridRevision>,
@ -52,7 +52,7 @@ impl GridRevisionPad {
} }
pub fn from_delta(delta: GridRevisionDelta) -> CollaborateResult<Self> { pub fn from_delta(delta: GridRevisionDelta) -> CollaborateResult<Self> {
let content = delta.content_str()?; let content = delta.content()?;
let grid: GridRevision = serde_json::from_str(&content) let grid: GridRevision = serde_json::from_str(&content)
.map_err(|e| CollaborateError::internal().context(format!("Deserialize delta to grid failed: {}", e)))?; .map_err(|e| CollaborateError::internal().context(format!("Deserialize delta to grid failed: {}", e)))?;
@ -457,15 +457,15 @@ impl GridRevisionPad {
} }
pub fn md5(&self) -> String { pub fn md5(&self) -> String {
md5(&self.delta.to_json_bytes()) md5(&self.delta.json_bytes())
} }
pub fn delta_str(&self) -> String { pub fn delta_str(&self) -> String {
self.delta.to_json_str() self.delta.json_str()
} }
pub fn delta_bytes(&self) -> Bytes { pub fn delta_bytes(&self) -> Bytes {
self.delta.to_json_bytes() self.delta.json_bytes()
} }
pub fn fields(&self) -> &[Arc<FieldRevision>] { pub fn fields(&self) -> &[Arc<FieldRevision>] {
@ -548,12 +548,12 @@ pub struct GridChangeset {
pub fn make_grid_delta(grid_rev: &GridRevision) -> GridRevisionDelta { pub fn make_grid_delta(grid_rev: &GridRevision) -> GridRevisionDelta {
let json = serde_json::to_string(&grid_rev).unwrap(); let json = serde_json::to_string(&grid_rev).unwrap();
PlainTextDeltaBuilder::new().insert(&json).build() TextDeltaBuilder::new().insert(&json).build()
} }
pub fn make_grid_revisions(user_id: &str, grid_rev: &GridRevision) -> RepeatedRevision { pub fn make_grid_revisions(user_id: &str, grid_rev: &GridRevision) -> RepeatedRevision {
let delta = make_grid_delta(grid_rev); let delta = make_grid_delta(grid_rev);
let bytes = delta.to_json_bytes(); let bytes = delta.json_bytes();
let revision = Revision::initial_revision(user_id, &grid_rev.grid_id, bytes); let revision = Revision::initial_revision(user_id, &grid_rev.grid_id, bytes);
revision.into() revision.into()
} }

View File

@ -1,7 +1,7 @@
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use lib_ot::core::PlainTextDelta; use lib_ot::core::TextDelta;
pub type FolderDelta = PlainTextDelta; pub type FolderDelta = TextDelta;
#[derive(ProtoBuf, Default, Debug, Clone, Eq, PartialEq)] #[derive(ProtoBuf, Default, Debug, Clone, Eq, PartialEq)]
pub struct FolderInfo { pub struct FolderInfo {

View File

@ -89,7 +89,7 @@ impl std::fmt::Debug for Revision {
let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?; let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?;
match RichTextDelta::from_bytes(&self.delta_data) { match RichTextDelta::from_bytes(&self.delta_data) {
Ok(delta) => { Ok(delta) => {
let _ = f.write_fmt(format_args!("delta {:?}", delta.to_json_str()))?; let _ = f.write_fmt(format_args!("delta {:?}", delta.json_str()))?;
} }
Err(e) => { Err(e) => {
let _ = f.write_fmt(format_args!("delta {:?}", e))?; let _ = f.write_fmt(format_args!("delta {:?}", e))?;

View File

@ -46,7 +46,7 @@ impl std::convert::TryFrom<Revision> for DocumentPB {
} }
let delta = RichTextDelta::from_bytes(&revision.delta_data)?; let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
let doc_json = delta.to_json_str(); let doc_json = delta.json_str();
Ok(DocumentPB { Ok(DocumentPB {
block_id: revision.object_id, block_id: revision.object_id,

View File

@ -39,7 +39,7 @@ impl RevisionSyncObject<RichTextAttributes> for ServerDocument {
} }
fn to_json(&self) -> String { fn to_json(&self) -> String {
self.delta.to_json_str() self.delta.json_str()
} }
fn set_delta(&mut self, new_delta: Delta<RichTextAttributes>) { fn set_delta(&mut self, new_delta: Delta<RichTextAttributes>) {

View File

@ -1,5 +1,5 @@
use crate::{entities::folder::FolderDelta, errors::CollaborateError, synchronizer::RevisionSyncObject}; use crate::{entities::folder::FolderDelta, errors::CollaborateError, synchronizer::RevisionSyncObject};
use lib_ot::core::{OperationTransform, PhantomAttributes, PlainTextDelta}; use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta};
pub struct ServerFolder { pub struct ServerFolder {
folder_id: String, folder_id: String,
@ -20,22 +20,22 @@ impl RevisionSyncObject<PhantomAttributes> for ServerFolder {
&self.folder_id &self.folder_id
} }
fn compose(&mut self, other: &PlainTextDelta) -> Result<(), CollaborateError> { fn compose(&mut self, other: &TextDelta) -> Result<(), CollaborateError> {
let new_delta = self.delta.compose(other)?; let new_delta = self.delta.compose(other)?;
self.delta = new_delta; self.delta = new_delta;
Ok(()) Ok(())
} }
fn transform(&self, other: &PlainTextDelta) -> Result<(PlainTextDelta, PlainTextDelta), CollaborateError> { fn transform(&self, other: &TextDelta) -> Result<(TextDelta, TextDelta), CollaborateError> {
let value = self.delta.transform(other)?; let value = self.delta.transform(other)?;
Ok(value) Ok(value)
} }
fn to_json(&self) -> String { fn to_json(&self) -> String {
self.delta.to_json_str() self.delta.json_str()
} }
fn set_delta(&mut self, new_delta: PlainTextDelta) { fn set_delta(&mut self, new_delta: TextDelta) {
self.delta = new_delta; self.delta = new_delta;
} }
} }

View File

@ -149,7 +149,7 @@ pub fn make_folder_from_revisions_pb(
folder_delta = folder_delta.compose(&delta)?; folder_delta = folder_delta.compose(&delta)?;
} }
let text = folder_delta.to_json_str(); let text = folder_delta.json_str();
Ok(Some(FolderInfo { Ok(Some(FolderInfo {
folder_id: folder_id.to_string(), folder_id: folder_id.to_string(),
text, text,
@ -183,7 +183,7 @@ pub fn make_document_from_revision_pbs(
delta = delta.compose(&new_delta)?; delta = delta.compose(&new_delta)?;
} }
let text = delta.to_json_str(); let text = delta.json_str();
Ok(Some(DocumentPB { Ok(Some(DocumentPB {
block_id: doc_id.to_owned(), block_id: doc_id.to_owned(),

View File

@ -1,9 +1,7 @@
use crate::core::delta::{trim, Delta}; use crate::core::delta::{trim, Delta};
use crate::core::operation::{Attributes, PhantomAttributes}; use crate::core::operation::Attributes;
use crate::core::Operation; use crate::core::Operation;
pub type PlainTextDeltaBuilder = DeltaBuilder<PhantomAttributes>;
/// A builder for creating new [Delta] objects. /// A builder for creating new [Delta] objects.
/// ///
/// Note that all edit operations must be sorted; the start point of each /// Note that all edit operations must be sorted; the start point of each
@ -12,11 +10,11 @@ pub type PlainTextDeltaBuilder = DeltaBuilder<PhantomAttributes>;
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use lib_ot::core::PlainTextDeltaBuilder; /// use lib_ot::core::TextDeltaBuilder;
/// let delta = PlainTextDeltaBuilder::new() /// let delta = TextDeltaBuilder::new()
/// .insert("AppFlowy") /// .insert("AppFlowy")
/// .build(); /// .build();
/// assert_eq!(delta.content_str().unwrap(), "AppFlowy"); /// assert_eq!(delta.content().unwrap(), "AppFlowy");
/// ``` /// ```
pub struct DeltaBuilder<T: Attributes> { pub struct DeltaBuilder<T: Attributes> {
delta: Delta<T>, delta: Delta<T>,
@ -57,7 +55,7 @@ where
/// let mut attribute = RichTextAttribute::Bold(true); /// let mut attribute = RichTextAttribute::Bold(true);
/// let delta = RichTextDeltaBuilder::new().retain_with_attributes(7, attribute.into()).build(); /// let delta = RichTextDeltaBuilder::new().retain_with_attributes(7, attribute.into()).build();
/// ///
/// assert_eq!(delta.to_json_str(), r#"[{"retain":7,"attributes":{"bold":true}}]"#); /// assert_eq!(delta.json_str(), r#"[{"retain":7,"attributes":{"bold":true}}]"#);
/// ``` /// ```
pub fn retain_with_attributes(mut self, n: usize, attrs: T) -> Self { pub fn retain_with_attributes(mut self, n: usize, attrs: T) -> Self {
self.delta.retain(n, attrs); self.delta.retain(n, attrs);
@ -74,19 +72,19 @@ where
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use lib_ot::core::{OperationTransform, PlainTextDeltaBuilder}; /// use lib_ot::core::{OperationTransform, TextDeltaBuilder};
/// ///
/// let delta = PlainTextDeltaBuilder::new() /// let delta = TextDeltaBuilder::new()
/// .insert("AppFlowy...") /// .insert("AppFlowy...")
/// .build(); /// .build();
/// ///
/// let changeset = PlainTextDeltaBuilder::new() /// let changeset = TextDeltaBuilder::new()
/// .retain(8) /// .retain(8)
/// .delete(3) /// .delete(3)
/// .build(); /// .build();
/// ///
/// let new_delta = delta.compose(&changeset).unwrap(); /// let new_delta = delta.compose(&changeset).unwrap();
/// assert_eq!(new_delta.content_str().unwrap(), "AppFlowy"); /// assert_eq!(new_delta.content().unwrap(), "AppFlowy");
/// ``` /// ```
pub fn delete(mut self, n: usize) -> Self { pub fn delete(mut self, n: usize) -> Self {
self.delta.delete(n); self.delta.delete(n);
@ -110,9 +108,9 @@ where
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use lib_ot::core::{OperationTransform, PlainTextDeltaBuilder}; /// use lib_ot::core::{OperationTransform, TextDeltaBuilder};
/// use lib_ot::rich_text::{RichTextAttribute, RichTextDeltaBuilder}; /// use lib_ot::rich_text::{RichTextAttribute, RichTextDeltaBuilder};
/// let delta = PlainTextDeltaBuilder::new() /// let delta = TextDeltaBuilder::new()
/// .retain(3) /// .retain(3)
/// .trim() /// .trim()
/// .build(); /// .build();

View File

@ -29,14 +29,17 @@ where
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use lib_ot::core::{DeltaIterator, Interval, Operation}; /// use lib_ot::core::{DeltaCursor, DeltaIterator, Interval, Operation};
/// use lib_ot::rich_text::RichTextDelta; /// use lib_ot::rich_text::RichTextDelta;
/// let mut delta = RichTextDelta::default(); /// let mut delta = RichTextDelta::default();
/// let op_1 = Operation::insert("123"); /// delta.add(Operation::insert("123"));
/// let op_2 = Operation::insert("4"); /// delta.add(Operation::insert("4"));
/// delta.add(op_1.clone()); ///
/// delta.add(op_2.clone()); /// let mut cursor = DeltaCursor::new(&delta, Interval::new(0, 3));
/// assert_eq!(DeltaIterator::from_interval(&delta, Interval::new(0, 3)).ops(), vec![op_1.clone()]); /// assert_eq!(cursor.next_iv(), Interval::new(0,3));
/// assert_eq!(cursor.next_with_len(Some(2)).unwrap(), Operation::insert("12"));
/// assert_eq!(cursor.get_next_op().unwrap(), Operation::insert("3"));
/// assert_eq!(cursor.get_next_op(), None);
/// ``` /// ```
pub fn new(delta: &'a Delta<T>, interval: Interval) -> DeltaCursor<'a, T> { pub fn new(delta: &'a Delta<T>, interval: Interval) -> DeltaCursor<'a, T> {
// debug_assert!(interval.start <= delta.target_len); // debug_assert!(interval.start <= delta.target_len);

View File

@ -1,9 +1,10 @@
use crate::errors::{ErrorBuilder, OTError, OTErrorCode}; use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
use crate::core::delta::{DeltaIterator, MAX_IV_LEN}; use crate::core::delta::{DeltaIterator, MAX_IV_LEN};
use crate::core::flowy_str::OTString;
use crate::core::interval::Interval; use crate::core::interval::Interval;
use crate::core::operation::{Attributes, Operation, OperationTransform, PhantomAttributes}; use crate::core::operation::{Attributes, Operation, OperationTransform, PhantomAttributes};
use crate::core::ot_str::OTString;
use crate::core::DeltaBuilder;
use bytes::Bytes; use bytes::Bytes;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::{ use std::{
@ -14,12 +15,15 @@ use std::{
str::FromStr, str::FromStr,
}; };
pub type PlainTextDelta = Delta<PhantomAttributes>; pub type TextDelta = Delta<PhantomAttributes>;
pub type TextDeltaBuilder = DeltaBuilder<PhantomAttributes>;
/// A [Delta] contains list of operations that consists of 'Retain', 'Delete' and 'Insert' operation. /// A [Delta] contains list of operations that consists of 'Retain', 'Delete' and 'Insert' operation.
/// Check out the [Operation] for more details. It describes the document as a sequence of /// Check out the [Operation] for more details. It describes the document as a sequence of
/// operations. /// operations.
/// ///
/// You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/delta) out for more information.
///
/// If the [T] supports 'serde', that will enable delta to serialize to JSON or deserialize from /// If the [T] supports 'serde', that will enable delta to serialize to JSON or deserialize from
/// a JSON string. /// a JSON string.
/// ///
@ -176,15 +180,15 @@ where
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use lib_ot::core::PlainTextDeltaBuilder; /// use lib_ot::core::TextDeltaBuilder;
/// let s = "hello"; /// let s = "hello";
/// let delta_a = PlainTextDeltaBuilder::new().insert(s).build(); /// let delta_a = TextDeltaBuilder::new().insert(s).build();
/// let delta_b = PlainTextDeltaBuilder::new() /// let delta_b = TextDeltaBuilder::new()
/// .retain(s.len()) /// .retain(s.len())
/// .insert(", AppFlowy") /// .insert(", AppFlowy")
/// .build(); /// .build();
/// ///
/// let after_a = delta_a.content_str().unwrap(); /// let after_a = delta_a.content().unwrap();
/// let after_b = delta_b.apply(&after_a).unwrap(); /// let after_b = delta_b.apply(&after_a).unwrap();
/// assert_eq!("hello, AppFlowy", &after_b); /// assert_eq!("hello, AppFlowy", &after_b);
/// ``` /// ```
@ -233,9 +237,9 @@ where
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use lib_ot::core::PlainTextDeltaBuilder; /// use lib_ot::core::TextDeltaBuilder;
/// let s = "hello world"; /// let s = "hello world";
/// let delta = PlainTextDeltaBuilder::new().insert(s).build(); /// let delta = TextDeltaBuilder::new().insert(s).build();
/// let invert_delta = delta.invert_str(s); /// let invert_delta = delta.invert_str(s);
/// assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len); /// assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len);
/// assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len); /// assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len);
@ -555,7 +559,7 @@ fn transform_op_attribute<T: Attributes>(
} }
let left = left.as_ref().unwrap().get_attributes(); let left = left.as_ref().unwrap().get_attributes();
let right = right.as_ref().unwrap().get_attributes(); let right = right.as_ref().unwrap().get_attributes();
// TODO: replace with anyhow and thiserror. // TODO: replace with anyhow and this error.
Ok(left.transform(&right)?.0) Ok(left.transform(&right)?.0)
} }
@ -571,10 +575,10 @@ where
/// let json = r#"[ /// let json = r#"[
/// {"retain":7,"attributes":{"bold":null}} /// {"retain":7,"attributes":{"bold":null}}
/// ]"#; /// ]"#;
/// let delta = RichTextDelta::from_json_str(json).unwrap(); /// let delta = RichTextDelta::from_json(json).unwrap();
/// assert_eq!(delta.to_json_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#); /// assert_eq!(delta.json_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#);
/// ``` /// ```
pub fn from_json_str(json: &str) -> Result<Self, OTError> { pub fn from_json(json: &str) -> Result<Self, OTError> {
let delta = serde_json::from_str(json).map_err(|e| { let delta = serde_json::from_str(json).map_err(|e| {
tracing::trace!("Deserialize failed: {:?}", e); tracing::trace!("Deserialize failed: {:?}", e);
tracing::trace!("{:?}", json); tracing::trace!("{:?}", json);
@ -583,9 +587,10 @@ where
Ok(delta) Ok(delta)
} }
/// Deserialize the bytes into [Delta]. It requires the bytes is in utf8 format.
pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Result<Self, OTError> { pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Result<Self, OTError> {
let json = str::from_utf8(bytes.as_ref())?.to_owned(); let json = str::from_utf8(bytes.as_ref())?.to_owned();
let val = Self::from_json_str(&json)?; let val = Self::from_json(&json)?;
Ok(val) Ok(val)
} }
} }
@ -594,16 +599,19 @@ impl<T> Delta<T>
where where
T: Attributes + serde::Serialize, T: Attributes + serde::Serialize,
{ {
pub fn to_json_str(&self) -> String { /// Serialize the [Delta] into a String in JSON format
pub fn json_str(&self) -> String {
serde_json::to_string(self).unwrap_or_else(|_| "".to_owned()) serde_json::to_string(self).unwrap_or_else(|_| "".to_owned())
} }
pub fn content_str(&self) -> Result<String, OTError> { /// Get the content the [Delta] represents.
pub fn content(&self) -> Result<String, OTError> {
self.apply("") self.apply("")
} }
pub fn to_json_bytes(&self) -> Bytes { /// Serial the [Delta] into a String in Bytes format
let json = self.to_json_str(); pub fn json_bytes(&self) -> Bytes {
let json = self.json_str();
Bytes::from(json.into_bytes()) Bytes::from(json.into_bytes())
} }
} }

View File

@ -7,17 +7,26 @@ use std::ops::{Deref, DerefMut};
pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize; pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
/// Retain the 'n' characters with the attributes. Use 'retain' instead if you don't /// [DeltaIterator] is used to iterate over a delta.
/// need any attributes.
/// # Examples /// # Examples
/// ///
/// You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/delta) out for more information.
///
/// ``` /// ```
/// use lib_ot::rich_text::{RichTextAttribute, RichTextDelta, RichTextDeltaBuilder}; /// use lib_ot::core::{DeltaIterator, Interval, Operation};
/// use lib_ot::rich_text::RichTextDelta;
/// let mut delta = RichTextDelta::default();
/// delta.add(Operation::insert("123"));
/// delta.add(Operation::insert("4"));
/// assert_eq!(
/// DeltaIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
/// vec![Operation::insert("12")]
/// );
/// ///
/// let mut attribute = RichTextAttribute::Bold(true); /// assert_eq!(
/// let delta = RichTextDeltaBuilder::new().retain_with_attributes(7, attribute.into()).build(); /// DeltaIterator::from_interval(&delta, Interval::new(1, 3)).ops(),
/// /// vec![Operation::insert("23")]
/// assert_eq!(delta.to_json_str(), r#"[{"retain":7,"attributes":{"bold":true}}]"#); /// );
/// ``` /// ```
pub struct DeltaIterator<'a, T: Attributes> { pub struct DeltaIterator<'a, T: Attributes> {
cursor: DeltaCursor<'a, T>, cursor: DeltaCursor<'a, T>,

View File

@ -1,9 +1,9 @@
mod delta; mod delta;
mod flowy_str;
mod interval; mod interval;
mod operation; mod operation;
mod ot_str;
pub use delta::*; pub use delta::*;
pub use flowy_str::*;
pub use interval::*; pub use interval::*;
pub use operation::*; pub use operation::*;
pub use ot_str::*;

View File

@ -1,5 +1,5 @@
use crate::core::flowy_str::OTString;
use crate::core::interval::Interval; use crate::core::interval::Interval;
use crate::core::ot_str::OTString;
use crate::errors::OTError; use crate::errors::OTError;
use serde::{Deserialize, Serialize, __private::Formatter}; use serde::{Deserialize, Serialize, __private::Formatter};
use std::fmt::Display; use std::fmt::Display;
@ -21,11 +21,11 @@ pub trait OperationTransform {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use lib_ot::core::{OperationTransform, PlainTextDeltaBuilder}; /// use lib_ot::core::{OperationTransform, TextDeltaBuilder};
/// let document = PlainTextDeltaBuilder::new().build(); /// let document = TextDeltaBuilder::new().build();
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build(); /// let delta = TextDeltaBuilder::new().insert("abc").build();
/// let new_document = document.compose(&delta).unwrap(); /// let new_document = document.compose(&delta).unwrap();
/// assert_eq!(new_document.content_str().unwrap(), "abc".to_owned()); /// assert_eq!(new_document.content().unwrap(), "abc".to_owned());
/// ``` /// ```
fn compose(&self, other: &Self) -> Result<Self, OTError> fn compose(&self, other: &Self) -> Result<Self, OTError>
where where
@ -50,9 +50,9 @@ pub trait OperationTransform {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use lib_ot::core::{OperationTransform, PlainTextDeltaBuilder}; /// use lib_ot::core::{OperationTransform, TextDeltaBuilder};
/// let original_document = PlainTextDeltaBuilder::new().build(); /// let original_document = TextDeltaBuilder::new().build();
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build(); /// let delta = TextDeltaBuilder::new().insert("abc").build();
/// ///
/// let undo_delta = delta.invert(&original_document); /// let undo_delta = delta.invert(&original_document);
/// let new_document = original_document.compose(&delta).unwrap(); /// let new_document = original_document.compose(&delta).unwrap();
@ -67,8 +67,8 @@ pub trait OperationTransform {
/// Each operation can carry attributes. For example, the [RichTextAttributes] has a list of key/value attributes. /// Each operation can carry attributes. For example, the [RichTextAttributes] has a list of key/value attributes.
/// Such as { bold: true, italic: true }. /// Such as { bold: true, italic: true }.
/// ///
/// Because [Operation] is generic over the T, so you must specify the T. For example, the [PlainTextDelta]. It use ///Because [Operation] is generic over the T, so you must specify the T. For example, the [TextDelta] uses
/// use [PhantomAttributes] as the T. [PhantomAttributes] does nothing, just a phantom. ///[PhantomAttributes] as the T. [PhantomAttributes] does nothing, just a phantom.
/// ///
pub trait Attributes: Default + Display + Eq + PartialEq + Clone + Debug + OperationTransform { pub trait Attributes: Default + Display + Eq + PartialEq + Clone + Debug + OperationTransform {
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
@ -90,6 +90,8 @@ pub trait Attributes: Default + Display + Eq + PartialEq + Clone + Debug + Opera
/// * Retain /// * Retain
/// * Insert /// * Insert
/// ///
/// You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/delta) out for more information.
///
/// The [T] should support serde if you want to serialize/deserialize the operation /// The [T] should support serde if you want to serialize/deserialize the operation
/// to json string. You could check out the operation_serde.rs for more information. /// to json string. You could check out the operation_serde.rs for more information.
/// ///

View File

@ -1,5 +1,5 @@
use crate::core::flowy_str::OTString;
use crate::core::operation::{Attributes, Insert, Operation, Retain}; use crate::core::operation::{Attributes, Insert, Operation, Retain};
use crate::core::ot_str::OTString;
use serde::{ use serde::{
de, de,
de::{MapAccess, SeqAccess, Visitor}, de::{MapAccess, SeqAccess, Visitor},

View File

@ -86,8 +86,8 @@ impl OTString {
/// assert_eq!(iter.skip(OTString::from("ab一二").utf16_len()).next().unwrap(), "👋".to_string()); /// assert_eq!(iter.skip(OTString::from("ab一二").utf16_len()).next().unwrap(), "👋".to_string());
/// ``` /// ```
#[allow(dead_code)] #[allow(dead_code)]
pub fn utf16_code_point_iter(&self) -> FlowyUtf16CodePointIterator { pub fn utf16_code_point_iter(&self) -> OTUtf16CodePointIterator {
FlowyUtf16CodePointIterator::new(self, 0) OTUtf16CodePointIterator::new(self, 0)
} }
} }
@ -218,21 +218,21 @@ impl<'a> Iterator for Utf16CodeUnitIterator<'a> {
} }
} }
pub struct FlowyUtf16CodePointIterator<'a> { pub struct OTUtf16CodePointIterator<'a> {
s: &'a OTString, s: &'a OTString,
offset: usize, offset: usize,
} }
impl<'a> FlowyUtf16CodePointIterator<'a> { impl<'a> OTUtf16CodePointIterator<'a> {
pub fn new(s: &'a OTString, offset: usize) -> Self { pub fn new(s: &'a OTString, offset: usize) -> Self {
FlowyUtf16CodePointIterator { s, offset } OTUtf16CodePointIterator { s, offset }
} }
} }
use crate::core::interval::Interval; use crate::core::interval::Interval;
use std::str; use std::str;
impl<'a> Iterator for FlowyUtf16CodePointIterator<'a> { impl<'a> Iterator for OTUtf16CodePointIterator<'a> {
type Item = String; type Item = String;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -278,8 +278,8 @@ pub fn len_utf8_from_first_byte(b: u8) -> usize {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::core::flowy_str::OTString;
use crate::core::interval::Interval; use crate::core::interval::Interval;
use crate::core::ot_str::OTString;
#[test] #[test]
fn flowy_str_code_unit() { fn flowy_str_code_unit() {