util.rs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. use crate::{
  2. entities::{
  3. folder_info::{FolderDelta, FolderInfo},
  4. revision::{RepeatedRevision, Revision},
  5. text_block_info::TextBlockInfo,
  6. },
  7. errors::{CollaborateError, CollaborateResult},
  8. protobuf::{
  9. FolderInfo as FolderInfoPB, RepeatedRevision as RepeatedRevisionPB, Revision as RevisionPB,
  10. TextBlockInfo as TextBlockInfoPB,
  11. },
  12. };
  13. use dissimilar::Chunk;
  14. use lib_ot::core::{DeltaBuilder, FlowyStr};
  15. use lib_ot::{
  16. core::{Attributes, Delta, OperationTransformable, NEW_LINE, WHITESPACE},
  17. rich_text::RichTextDelta,
  18. };
  19. use serde::de::DeserializeOwned;
  20. use std::{
  21. convert::TryInto,
  22. sync::atomic::{AtomicI64, Ordering::SeqCst},
  23. };
  24. #[inline]
  25. pub fn find_newline(s: &str) -> Option<usize> {
  26. s.find(NEW_LINE)
  27. }
  28. #[inline]
  29. pub fn is_newline(s: &str) -> bool {
  30. s == NEW_LINE
  31. }
  32. #[inline]
  33. pub fn is_whitespace(s: &str) -> bool {
  34. s == WHITESPACE
  35. }
  36. #[inline]
  37. pub fn contain_newline(s: &str) -> bool {
  38. s.contains(NEW_LINE)
  39. }
  40. #[inline]
  41. pub fn md5<T: AsRef<[u8]>>(data: T) -> String {
  42. let md5 = format!("{:x}", md5::compute(data));
  43. md5
  44. }
  45. #[derive(Debug)]
  46. pub struct RevIdCounter(pub AtomicI64);
  47. impl RevIdCounter {
  48. pub fn new(n: i64) -> Self {
  49. Self(AtomicI64::new(n))
  50. }
  51. pub fn next(&self) -> i64 {
  52. let _ = self.0.fetch_add(1, SeqCst);
  53. self.value()
  54. }
  55. pub fn value(&self) -> i64 {
  56. self.0.load(SeqCst)
  57. }
  58. pub fn set(&self, n: i64) {
  59. let _ = self.0.fetch_update(SeqCst, SeqCst, |_| Some(n));
  60. }
  61. }
  62. #[tracing::instrument(level = "trace", skip(revisions), err)]
  63. pub fn make_delta_from_revisions<T>(revisions: Vec<Revision>) -> CollaborateResult<Delta<T>>
  64. where
  65. T: Attributes + DeserializeOwned,
  66. {
  67. let mut delta = Delta::<T>::new();
  68. for revision in revisions {
  69. if revision.delta_data.is_empty() {
  70. tracing::warn!("revision delta_data is empty");
  71. }
  72. let revision_delta = Delta::<T>::from_bytes(revision.delta_data).map_err(|e| {
  73. let err_msg = format!("Deserialize remote revision failed: {:?}", e);
  74. CollaborateError::internal().context(err_msg)
  75. })?;
  76. delta = delta.compose(&revision_delta)?;
  77. }
  78. Ok(delta)
  79. }
  80. pub fn make_delta_from_revision_pb<T>(revisions: Vec<RevisionPB>) -> CollaborateResult<Delta<T>>
  81. where
  82. T: Attributes + DeserializeOwned,
  83. {
  84. let mut new_delta = Delta::<T>::new();
  85. for revision in revisions {
  86. let delta = Delta::<T>::from_bytes(revision.delta_data).map_err(|e| {
  87. let err_msg = format!("Deserialize remote revision failed: {:?}", e);
  88. CollaborateError::internal().context(err_msg)
  89. })?;
  90. new_delta = new_delta.compose(&delta)?;
  91. }
  92. Ok(new_delta)
  93. }
  94. pub fn repeated_revision_from_revision_pbs(revisions: Vec<RevisionPB>) -> CollaborateResult<RepeatedRevision> {
  95. let repeated_revision_pb = repeated_revision_pb_from_revisions(revisions);
  96. repeated_revision_from_repeated_revision_pb(repeated_revision_pb)
  97. }
  98. pub fn repeated_revision_pb_from_revisions(revisions: Vec<RevisionPB>) -> RepeatedRevisionPB {
  99. let mut repeated_revision_pb = RepeatedRevisionPB::new();
  100. repeated_revision_pb.set_items(revisions.into());
  101. repeated_revision_pb
  102. }
  103. pub fn repeated_revision_from_repeated_revision_pb(
  104. repeated_revision: RepeatedRevisionPB,
  105. ) -> CollaborateResult<RepeatedRevision> {
  106. repeated_revision
  107. .try_into()
  108. .map_err(|e| CollaborateError::internal().context(format!("Cast repeated revision failed: {:?}", e)))
  109. }
  110. pub fn pair_rev_id_from_revision_pbs(revisions: &[RevisionPB]) -> (i64, i64) {
  111. let mut rev_id = 0;
  112. revisions.iter().for_each(|revision| {
  113. if rev_id < revision.rev_id {
  114. rev_id = revision.rev_id;
  115. }
  116. });
  117. if rev_id > 0 {
  118. (rev_id - 1, rev_id)
  119. } else {
  120. (0, rev_id)
  121. }
  122. }
  123. pub fn pair_rev_id_from_revisions(revisions: &[Revision]) -> (i64, i64) {
  124. let mut rev_id = 0;
  125. revisions.iter().for_each(|revision| {
  126. if rev_id < revision.rev_id {
  127. rev_id = revision.rev_id;
  128. }
  129. });
  130. if rev_id > 0 {
  131. (rev_id - 1, rev_id)
  132. } else {
  133. (0, rev_id)
  134. }
  135. }
  136. #[inline]
  137. pub fn make_folder_from_revisions_pb(
  138. folder_id: &str,
  139. revisions: RepeatedRevisionPB,
  140. ) -> Result<Option<FolderInfo>, CollaborateError> {
  141. match make_folder_pb_from_revisions_pb(folder_id, revisions)? {
  142. None => Ok(None),
  143. Some(pb) => {
  144. let folder_info: FolderInfo = pb.try_into().map_err(|e| CollaborateError::internal().context(e))?;
  145. Ok(Some(folder_info))
  146. }
  147. }
  148. }
  149. #[inline]
  150. pub fn make_folder_pb_from_revisions_pb(
  151. folder_id: &str,
  152. mut revisions: RepeatedRevisionPB,
  153. ) -> Result<Option<FolderInfoPB>, CollaborateError> {
  154. let revisions = revisions.take_items();
  155. if revisions.is_empty() {
  156. return Ok(None);
  157. }
  158. let mut folder_delta = FolderDelta::new();
  159. let mut base_rev_id = 0;
  160. let mut rev_id = 0;
  161. for revision in revisions {
  162. base_rev_id = revision.base_rev_id;
  163. rev_id = revision.rev_id;
  164. if revision.delta_data.is_empty() {
  165. tracing::warn!("revision delta_data is empty");
  166. }
  167. let delta = FolderDelta::from_bytes(revision.delta_data)?;
  168. folder_delta = folder_delta.compose(&delta)?;
  169. }
  170. let text = folder_delta.to_delta_str();
  171. let mut folder_info = FolderInfoPB::new();
  172. folder_info.set_folder_id(folder_id.to_owned());
  173. folder_info.set_text(text);
  174. folder_info.set_base_rev_id(base_rev_id);
  175. folder_info.set_rev_id(rev_id);
  176. Ok(Some(folder_info))
  177. }
  178. #[inline]
  179. pub fn make_document_info_from_revisions_pb(
  180. doc_id: &str,
  181. revisions: RepeatedRevisionPB,
  182. ) -> Result<Option<TextBlockInfo>, CollaborateError> {
  183. match make_document_info_pb_from_revisions_pb(doc_id, revisions)? {
  184. None => Ok(None),
  185. Some(pb) => {
  186. let document_info: TextBlockInfo = pb.try_into().map_err(|e| {
  187. CollaborateError::internal().context(format!("Deserialize document info from pb failed: {}", e))
  188. })?;
  189. Ok(Some(document_info))
  190. }
  191. }
  192. }
  193. #[inline]
  194. pub fn make_document_info_pb_from_revisions_pb(
  195. doc_id: &str,
  196. mut revisions: RepeatedRevisionPB,
  197. ) -> Result<Option<TextBlockInfoPB>, CollaborateError> {
  198. let revisions = revisions.take_items();
  199. if revisions.is_empty() {
  200. return Ok(None);
  201. }
  202. let mut document_delta = RichTextDelta::new();
  203. let mut base_rev_id = 0;
  204. let mut rev_id = 0;
  205. for revision in revisions {
  206. base_rev_id = revision.base_rev_id;
  207. rev_id = revision.rev_id;
  208. if revision.delta_data.is_empty() {
  209. tracing::warn!("revision delta_data is empty");
  210. }
  211. let delta = RichTextDelta::from_bytes(revision.delta_data)?;
  212. document_delta = document_delta.compose(&delta)?;
  213. }
  214. let text = document_delta.to_delta_str();
  215. let mut block_info = TextBlockInfoPB::new();
  216. block_info.set_block_id(doc_id.to_owned());
  217. block_info.set_text(text);
  218. block_info.set_base_rev_id(base_rev_id);
  219. block_info.set_rev_id(rev_id);
  220. Ok(Some(block_info))
  221. }
  222. #[inline]
  223. pub fn rev_id_from_str(s: &str) -> Result<i64, CollaborateError> {
  224. let rev_id = s
  225. .to_owned()
  226. .parse::<i64>()
  227. .map_err(|e| CollaborateError::internal().context(format!("Parse rev_id from {} failed. {}", s, e)))?;
  228. Ok(rev_id)
  229. }
  230. pub fn cal_diff<T: Attributes>(old: String, new: String) -> Option<Delta<T>> {
  231. let chunks = dissimilar::diff(&old, &new);
  232. let mut delta_builder = DeltaBuilder::<T>::new();
  233. for chunk in &chunks {
  234. match chunk {
  235. Chunk::Equal(s) => {
  236. delta_builder = delta_builder.retain(FlowyStr::from(*s).utf16_size());
  237. }
  238. Chunk::Delete(s) => {
  239. delta_builder = delta_builder.delete(FlowyStr::from(*s).utf16_size());
  240. }
  241. Chunk::Insert(s) => {
  242. delta_builder = delta_builder.insert(*s);
  243. }
  244. }
  245. }
  246. let delta = delta_builder.build();
  247. if delta.is_empty() {
  248. None
  249. } else {
  250. Some(delta)
  251. }
  252. }