util.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. use crate::errors::{CollaborateError, CollaborateResult};
  2. use crate::server_folder::FolderOperations;
  3. use dissimilar::Chunk;
  4. use flowy_http_model::document::DocumentPayload;
  5. use flowy_http_model::folder::FolderInfo;
  6. use flowy_http_model::revision::Revision;
  7. use lib_ot::core::{DeltaOperationBuilder, OTString, OperationAttributes};
  8. use lib_ot::{
  9. core::{DeltaOperations, OperationTransform, NEW_LINE, WHITESPACE},
  10. text_delta::DeltaTextOperations,
  11. };
  12. use serde::de::DeserializeOwned;
  13. #[inline]
  14. pub fn find_newline(s: &str) -> Option<usize> {
  15. s.find(NEW_LINE)
  16. }
  17. #[inline]
  18. pub fn is_newline(s: &str) -> bool {
  19. s == NEW_LINE
  20. }
  21. #[inline]
  22. pub fn is_whitespace(s: &str) -> bool {
  23. s == WHITESPACE
  24. }
  25. #[inline]
  26. pub fn contain_newline(s: &str) -> bool {
  27. s.contains(NEW_LINE)
  28. }
  29. #[tracing::instrument(level = "trace", skip(revisions), err)]
  30. pub fn make_operations_from_revisions<T>(revisions: Vec<Revision>) -> CollaborateResult<DeltaOperations<T>>
  31. where
  32. T: OperationAttributes + DeserializeOwned + OperationAttributes + serde::Serialize,
  33. {
  34. let mut new_operations = DeltaOperations::<T>::new();
  35. for revision in revisions {
  36. if revision.bytes.is_empty() {
  37. return Err(CollaborateError::unexpected_empty_revision().context("Unexpected Empty revision"));
  38. }
  39. let operations = DeltaOperations::<T>::from_bytes(revision.bytes).map_err(|e| {
  40. let err_msg = format!("Deserialize revision failed: {:?}", e);
  41. CollaborateError::internal().context(err_msg)
  42. })?;
  43. new_operations = new_operations.compose(&operations)?;
  44. }
  45. Ok(new_operations)
  46. }
  47. pub fn recover_operation_from_revisions<T>(
  48. revisions: Vec<Revision>,
  49. validator: impl Fn(&DeltaOperations<T>) -> bool,
  50. ) -> Option<DeltaOperations<T>>
  51. where
  52. T: OperationAttributes + DeserializeOwned + OperationAttributes,
  53. {
  54. let mut new_operations = DeltaOperations::<T>::new();
  55. for revision in revisions {
  56. if let Ok(operations) = DeltaOperations::<T>::from_bytes(revision.bytes) {
  57. match new_operations.compose(&operations) {
  58. Ok(composed_operations) => {
  59. if validator(&composed_operations) {
  60. new_operations = composed_operations;
  61. } else {
  62. break;
  63. }
  64. }
  65. Err(_) => break,
  66. }
  67. } else {
  68. break;
  69. }
  70. }
  71. return if new_operations.is_empty() {
  72. None
  73. } else {
  74. Some(new_operations)
  75. };
  76. }
  77. pub fn pair_rev_id_from_revision_pbs(revisions: &[Revision]) -> (i64, i64) {
  78. let mut rev_id = 0;
  79. revisions.iter().for_each(|revision| {
  80. if rev_id < revision.rev_id {
  81. rev_id = revision.rev_id;
  82. }
  83. });
  84. if rev_id > 0 {
  85. (rev_id - 1, rev_id)
  86. } else {
  87. (0, rev_id)
  88. }
  89. }
  90. #[inline]
  91. pub fn make_folder_from_revisions_pb(
  92. folder_id: &str,
  93. revisions: Vec<Revision>,
  94. ) -> Result<Option<FolderInfo>, CollaborateError> {
  95. if revisions.is_empty() {
  96. return Ok(None);
  97. }
  98. let mut folder_delta = FolderOperations::new();
  99. let mut base_rev_id = 0;
  100. let mut rev_id = 0;
  101. for revision in revisions {
  102. base_rev_id = revision.base_rev_id;
  103. rev_id = revision.rev_id;
  104. if revision.bytes.is_empty() {
  105. tracing::warn!("revision delta_data is empty");
  106. }
  107. let delta = FolderOperations::from_bytes(revision.bytes)?;
  108. folder_delta = folder_delta.compose(&delta)?;
  109. }
  110. let text = folder_delta.json_str();
  111. Ok(Some(FolderInfo {
  112. folder_id: folder_id.to_string(),
  113. text,
  114. rev_id,
  115. base_rev_id,
  116. }))
  117. }
  118. #[inline]
  119. pub fn make_document_from_revision_pbs(
  120. doc_id: &str,
  121. revisions: Vec<Revision>,
  122. ) -> Result<Option<DocumentPayload>, CollaborateError> {
  123. if revisions.is_empty() {
  124. return Ok(None);
  125. }
  126. let mut delta = DeltaTextOperations::new();
  127. let mut base_rev_id = 0;
  128. let mut rev_id = 0;
  129. for revision in revisions {
  130. base_rev_id = revision.base_rev_id;
  131. rev_id = revision.rev_id;
  132. if revision.bytes.is_empty() {
  133. tracing::warn!("revision delta_data is empty");
  134. }
  135. let new_delta = DeltaTextOperations::from_bytes(revision.bytes)?;
  136. delta = delta.compose(&new_delta)?;
  137. }
  138. Ok(Some(DocumentPayload {
  139. doc_id: doc_id.to_owned(),
  140. data: delta.json_bytes().to_vec(),
  141. rev_id,
  142. base_rev_id,
  143. }))
  144. }
  145. #[inline]
  146. pub fn rev_id_from_str(s: &str) -> Result<i64, CollaborateError> {
  147. let rev_id = s
  148. .to_owned()
  149. .parse::<i64>()
  150. .map_err(|e| CollaborateError::internal().context(format!("Parse rev_id from {} failed. {}", s, e)))?;
  151. Ok(rev_id)
  152. }
  153. pub fn cal_diff<T: OperationAttributes>(old: String, new: String) -> Option<DeltaOperations<T>> {
  154. let chunks = dissimilar::diff(&old, &new);
  155. let mut delta_builder = DeltaOperationBuilder::<T>::new();
  156. for chunk in &chunks {
  157. match chunk {
  158. Chunk::Equal(s) => {
  159. delta_builder = delta_builder.retain(OTString::from(*s).utf16_len());
  160. }
  161. Chunk::Delete(s) => {
  162. delta_builder = delta_builder.delete(OTString::from(*s).utf16_len());
  163. }
  164. Chunk::Insert(s) => {
  165. delta_builder = delta_builder.insert(s);
  166. }
  167. }
  168. }
  169. let delta = delta_builder.build();
  170. if delta.is_empty() {
  171. None
  172. } else {
  173. Some(delta)
  174. }
  175. }