2022-01-02 22:23:33 +08:00
|
|
|
use crate::{
|
2022-01-21 21:41:24 +08:00
|
|
|
entities::{
|
2022-07-01 20:32:11 +08:00
|
|
|
folder::{FolderDelta, FolderInfo},
|
2022-01-21 21:41:24 +08:00
|
|
|
revision::{RepeatedRevision, Revision},
|
2022-07-18 11:58:39 +08:00
|
|
|
text_block::TextBlockInfoPB,
|
2022-01-21 21:41:24 +08:00
|
|
|
},
|
2022-01-02 22:23:33 +08:00
|
|
|
errors::{CollaborateError, CollaborateResult},
|
2022-07-18 11:58:39 +08:00
|
|
|
protobuf::{FolderInfo as FolderInfoPB, RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB},
|
2022-01-02 22:23:33 +08:00
|
|
|
};
|
2022-03-04 18:11:12 +08:00
|
|
|
use dissimilar::Chunk;
|
|
|
|
use lib_ot::core::{DeltaBuilder, FlowyStr};
|
2022-01-02 22:23:33 +08:00
|
|
|
use lib_ot::{
|
2022-01-20 23:51:11 +08:00
|
|
|
core::{Attributes, Delta, OperationTransformable, NEW_LINE, WHITESPACE},
|
2022-01-02 22:23:33 +08:00
|
|
|
rich_text::RichTextDelta,
|
|
|
|
};
|
2022-01-20 23:51:11 +08:00
|
|
|
use serde::de::DeserializeOwned;
|
2022-01-03 12:20:06 +08:00
|
|
|
use std::{
|
|
|
|
convert::TryInto,
|
|
|
|
sync::atomic::{AtomicI64, Ordering::SeqCst},
|
|
|
|
};
|
2021-09-25 21:47:02 +08:00
|
|
|
|
|
|
|
#[inline]
|
2022-01-23 12:14:00 +08:00
|
|
|
pub fn find_newline(s: &str) -> Option<usize> {
|
|
|
|
s.find(NEW_LINE)
|
|
|
|
}
|
2021-09-25 21:47:02 +08:00
|
|
|
|
|
|
|
#[inline]
|
2022-01-23 12:14:00 +08:00
|
|
|
pub fn is_newline(s: &str) -> bool {
|
|
|
|
s == NEW_LINE
|
|
|
|
}
|
2021-09-25 21:47:02 +08:00
|
|
|
|
|
|
|
#[inline]
|
2022-01-23 12:14:00 +08:00
|
|
|
pub fn is_whitespace(s: &str) -> bool {
|
|
|
|
s == WHITESPACE
|
|
|
|
}
|
2021-09-25 21:47:02 +08:00
|
|
|
|
|
|
|
#[inline]
|
2022-01-23 12:14:00 +08:00
|
|
|
pub fn contain_newline(s: &str) -> bool {
|
|
|
|
s.contains(NEW_LINE)
|
|
|
|
}
|
2021-09-25 21:47:02 +08:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn md5<T: AsRef<[u8]>>(data: T) -> String {
|
|
|
|
let md5 = format!("{:x}", md5::compute(data));
|
|
|
|
md5
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2021-11-13 11:11:24 +08:00
|
|
|
pub struct RevIdCounter(pub AtomicI64);
|
2021-09-25 21:47:02 +08:00
|
|
|
|
|
|
|
impl RevIdCounter {
|
2022-01-23 12:14:00 +08:00
|
|
|
pub fn new(n: i64) -> Self {
|
|
|
|
Self(AtomicI64::new(n))
|
|
|
|
}
|
2021-09-25 21:47:02 +08:00
|
|
|
pub fn next(&self) -> i64 {
|
|
|
|
let _ = self.0.fetch_add(1, SeqCst);
|
|
|
|
self.value()
|
|
|
|
}
|
2022-01-23 12:14:00 +08:00
|
|
|
pub fn value(&self) -> i64 {
|
|
|
|
self.0.load(SeqCst)
|
|
|
|
}
|
2021-10-04 17:38:56 +08:00
|
|
|
|
2022-01-23 12:14:00 +08:00
|
|
|
pub fn set(&self, n: i64) {
|
|
|
|
let _ = self.0.fetch_update(SeqCst, SeqCst, |_| Some(n));
|
|
|
|
}
|
2021-09-25 21:47:02 +08:00
|
|
|
}
|
2022-01-02 22:23:33 +08:00
|
|
|
|
2022-03-01 23:38:26 +08:00
|
|
|
#[tracing::instrument(level = "trace", skip(revisions), err)]
|
2022-01-20 23:51:11 +08:00
|
|
|
pub fn make_delta_from_revisions<T>(revisions: Vec<Revision>) -> CollaborateResult<Delta<T>>
|
|
|
|
where
|
|
|
|
T: Attributes + DeserializeOwned,
|
|
|
|
{
|
|
|
|
let mut delta = Delta::<T>::new();
|
2022-01-02 22:23:33 +08:00
|
|
|
for revision in revisions {
|
2022-01-25 20:37:48 +08:00
|
|
|
if revision.delta_data.is_empty() {
|
|
|
|
tracing::warn!("revision delta_data is empty");
|
|
|
|
}
|
|
|
|
|
2022-01-20 23:51:11 +08:00
|
|
|
let revision_delta = Delta::<T>::from_bytes(revision.delta_data).map_err(|e| {
|
2022-01-02 22:23:33 +08:00
|
|
|
let err_msg = format!("Deserialize remote revision failed: {:?}", e);
|
|
|
|
CollaborateError::internal().context(err_msg)
|
|
|
|
})?;
|
2022-01-06 12:39:32 +08:00
|
|
|
delta = delta.compose(&revision_delta)?;
|
2022-01-02 22:23:33 +08:00
|
|
|
}
|
2022-01-06 12:39:32 +08:00
|
|
|
Ok(delta)
|
2022-01-02 22:23:33 +08:00
|
|
|
}
|
2022-01-03 12:20:06 +08:00
|
|
|
|
2022-01-20 23:51:11 +08:00
|
|
|
pub fn make_delta_from_revision_pb<T>(revisions: Vec<RevisionPB>) -> CollaborateResult<Delta<T>>
|
|
|
|
where
|
|
|
|
T: Attributes + DeserializeOwned,
|
|
|
|
{
|
2022-01-15 11:20:28 +08:00
|
|
|
let mut new_delta = Delta::<T>::new();
|
2022-01-03 12:20:06 +08:00
|
|
|
for revision in revisions {
|
2022-01-15 11:20:28 +08:00
|
|
|
let delta = Delta::<T>::from_bytes(revision.delta_data).map_err(|e| {
|
2022-01-03 12:20:06 +08:00
|
|
|
let err_msg = format!("Deserialize remote revision failed: {:?}", e);
|
|
|
|
CollaborateError::internal().context(err_msg)
|
|
|
|
})?;
|
|
|
|
new_delta = new_delta.compose(&delta)?;
|
|
|
|
}
|
|
|
|
Ok(new_delta)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn repeated_revision_from_revision_pbs(revisions: Vec<RevisionPB>) -> CollaborateResult<RepeatedRevision> {
|
|
|
|
let repeated_revision_pb = repeated_revision_pb_from_revisions(revisions);
|
|
|
|
repeated_revision_from_repeated_revision_pb(repeated_revision_pb)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn repeated_revision_pb_from_revisions(revisions: Vec<RevisionPB>) -> RepeatedRevisionPB {
|
|
|
|
let mut repeated_revision_pb = RepeatedRevisionPB::new();
|
|
|
|
repeated_revision_pb.set_items(revisions.into());
|
|
|
|
repeated_revision_pb
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn repeated_revision_from_repeated_revision_pb(
|
2022-02-24 21:49:18 +08:00
|
|
|
repeated_revision: RepeatedRevisionPB,
|
2022-01-03 12:20:06 +08:00
|
|
|
) -> CollaborateResult<RepeatedRevision> {
|
2022-02-24 21:49:18 +08:00
|
|
|
repeated_revision
|
2022-01-03 12:20:06 +08:00
|
|
|
.try_into()
|
|
|
|
.map_err(|e| CollaborateError::internal().context(format!("Cast repeated revision failed: {:?}", e)))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pair_rev_id_from_revision_pbs(revisions: &[RevisionPB]) -> (i64, i64) {
|
|
|
|
let mut rev_id = 0;
|
|
|
|
revisions.iter().for_each(|revision| {
|
|
|
|
if rev_id < revision.rev_id {
|
|
|
|
rev_id = revision.rev_id;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if rev_id > 0 {
|
|
|
|
(rev_id - 1, rev_id)
|
|
|
|
} else {
|
|
|
|
(0, rev_id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pair_rev_id_from_revisions(revisions: &[Revision]) -> (i64, i64) {
|
|
|
|
let mut rev_id = 0;
|
|
|
|
revisions.iter().for_each(|revision| {
|
|
|
|
if rev_id < revision.rev_id {
|
|
|
|
rev_id = revision.rev_id;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if rev_id > 0 {
|
|
|
|
(rev_id - 1, rev_id)
|
|
|
|
} else {
|
|
|
|
(0, rev_id)
|
|
|
|
}
|
|
|
|
}
|
2022-01-11 22:23:19 +08:00
|
|
|
|
|
|
|
#[inline]
|
2022-01-21 21:41:24 +08:00
|
|
|
pub fn make_folder_from_revisions_pb(
|
|
|
|
folder_id: &str,
|
|
|
|
revisions: RepeatedRevisionPB,
|
|
|
|
) -> Result<Option<FolderInfo>, CollaborateError> {
|
|
|
|
match make_folder_pb_from_revisions_pb(folder_id, revisions)? {
|
|
|
|
None => Ok(None),
|
2022-02-24 21:49:18 +08:00
|
|
|
Some(pb) => {
|
|
|
|
let folder_info: FolderInfo = pb.try_into().map_err(|e| CollaborateError::internal().context(e))?;
|
2022-01-21 21:41:24 +08:00
|
|
|
Ok(Some(folder_info))
|
2022-01-24 17:35:58 +08:00
|
|
|
}
|
2022-01-21 21:41:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn make_folder_pb_from_revisions_pb(
|
|
|
|
folder_id: &str,
|
|
|
|
mut revisions: RepeatedRevisionPB,
|
|
|
|
) -> Result<Option<FolderInfoPB>, CollaborateError> {
|
|
|
|
let revisions = revisions.take_items();
|
|
|
|
if revisions.is_empty() {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut folder_delta = FolderDelta::new();
|
|
|
|
let mut base_rev_id = 0;
|
|
|
|
let mut rev_id = 0;
|
|
|
|
for revision in revisions {
|
|
|
|
base_rev_id = revision.base_rev_id;
|
|
|
|
rev_id = revision.rev_id;
|
|
|
|
if revision.delta_data.is_empty() {
|
|
|
|
tracing::warn!("revision delta_data is empty");
|
|
|
|
}
|
|
|
|
let delta = FolderDelta::from_bytes(revision.delta_data)?;
|
|
|
|
folder_delta = folder_delta.compose(&delta)?;
|
|
|
|
}
|
|
|
|
|
2022-03-05 22:30:42 +08:00
|
|
|
let text = folder_delta.to_delta_str();
|
2022-01-21 21:41:24 +08:00
|
|
|
let mut folder_info = FolderInfoPB::new();
|
|
|
|
folder_info.set_folder_id(folder_id.to_owned());
|
|
|
|
folder_info.set_text(text);
|
|
|
|
folder_info.set_base_rev_id(base_rev_id);
|
|
|
|
folder_info.set_rev_id(rev_id);
|
|
|
|
Ok(Some(folder_info))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn make_document_info_from_revisions_pb(
|
|
|
|
doc_id: &str,
|
|
|
|
revisions: RepeatedRevisionPB,
|
2022-07-18 11:58:39 +08:00
|
|
|
) -> Result<Option<TextBlockInfoPB>, CollaborateError> {
|
2022-01-21 21:41:24 +08:00
|
|
|
match make_document_info_pb_from_revisions_pb(doc_id, revisions)? {
|
|
|
|
None => Ok(None),
|
2022-02-24 21:49:18 +08:00
|
|
|
Some(pb) => {
|
2022-07-18 11:58:39 +08:00
|
|
|
let document_info: TextBlockInfoPB = pb.try_into().map_err(|e| {
|
2022-01-21 21:41:24 +08:00
|
|
|
CollaborateError::internal().context(format!("Deserialize document info from pb failed: {}", e))
|
|
|
|
})?;
|
|
|
|
Ok(Some(document_info))
|
2022-01-24 17:35:58 +08:00
|
|
|
}
|
2022-01-21 21:41:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn make_document_info_pb_from_revisions_pb(
|
2022-01-11 22:23:19 +08:00
|
|
|
doc_id: &str,
|
|
|
|
mut revisions: RepeatedRevisionPB,
|
2022-07-18 11:58:39 +08:00
|
|
|
) -> Result<Option<crate::protobuf::TextBlockInfoPB>, CollaborateError> {
|
2022-01-11 22:23:19 +08:00
|
|
|
let revisions = revisions.take_items();
|
|
|
|
if revisions.is_empty() {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut document_delta = RichTextDelta::new();
|
|
|
|
let mut base_rev_id = 0;
|
|
|
|
let mut rev_id = 0;
|
|
|
|
for revision in revisions {
|
|
|
|
base_rev_id = revision.base_rev_id;
|
|
|
|
rev_id = revision.rev_id;
|
|
|
|
|
|
|
|
if revision.delta_data.is_empty() {
|
|
|
|
tracing::warn!("revision delta_data is empty");
|
|
|
|
}
|
|
|
|
|
|
|
|
let delta = RichTextDelta::from_bytes(revision.delta_data)?;
|
|
|
|
document_delta = document_delta.compose(&delta)?;
|
|
|
|
}
|
|
|
|
|
2022-03-05 22:30:42 +08:00
|
|
|
let text = document_delta.to_delta_str();
|
2022-07-18 11:58:39 +08:00
|
|
|
let mut block_info = crate::protobuf::TextBlockInfoPB::new();
|
2022-03-02 21:12:21 +08:00
|
|
|
block_info.set_block_id(doc_id.to_owned());
|
2022-02-25 22:27:44 +08:00
|
|
|
block_info.set_text(text);
|
|
|
|
block_info.set_base_rev_id(base_rev_id);
|
|
|
|
block_info.set_rev_id(rev_id);
|
|
|
|
Ok(Some(block_info))
|
2022-01-11 22:23:19 +08:00
|
|
|
}
|
2022-01-21 21:41:24 +08:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn rev_id_from_str(s: &str) -> Result<i64, CollaborateError> {
|
|
|
|
let rev_id = s
|
|
|
|
.to_owned()
|
|
|
|
.parse::<i64>()
|
|
|
|
.map_err(|e| CollaborateError::internal().context(format!("Parse rev_id from {} failed. {}", s, e)))?;
|
|
|
|
Ok(rev_id)
|
|
|
|
}
|
2022-03-04 18:11:12 +08:00
|
|
|
|
|
|
|
pub fn cal_diff<T: Attributes>(old: String, new: String) -> Option<Delta<T>> {
|
|
|
|
let chunks = dissimilar::diff(&old, &new);
|
|
|
|
let mut delta_builder = DeltaBuilder::<T>::new();
|
|
|
|
for chunk in &chunks {
|
|
|
|
match chunk {
|
|
|
|
Chunk::Equal(s) => {
|
|
|
|
delta_builder = delta_builder.retain(FlowyStr::from(*s).utf16_size());
|
|
|
|
}
|
|
|
|
Chunk::Delete(s) => {
|
|
|
|
delta_builder = delta_builder.delete(FlowyStr::from(*s).utf16_size());
|
|
|
|
}
|
|
|
|
Chunk::Insert(s) => {
|
|
|
|
delta_builder = delta_builder.insert(*s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let delta = delta_builder.build();
|
|
|
|
if delta.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(delta)
|
|
|
|
}
|
|
|
|
}
|