1use std::{
2 borrow::Cow,
3 cmp::Ordering,
4 collections::HashMap,
5 ops::{ControlFlow, Deref},
6 sync::{Arc, Mutex},
7};
8
9use loro::{
10 cursor::CannotFindRelativePosition, ChangeTravelError, CounterSpan, DocAnalysis,
11 FrontiersNotIncluded, IdSpan, JsonSchema, Lamport, LoroDoc as InnerLoroDoc, LoroEncodeError,
12 LoroError, LoroResult, PeerID, StyleConfig, Timestamp, ID,
13};
14
15use crate::{
16 event::{DiffBatch, DiffEvent, Subscriber},
17 AbsolutePosition, Configure, ContainerID, ContainerIdLike, Cursor, Frontiers, Index,
18 JsonPathError, LoroCounter, LoroList, LoroMap, LoroMovableList, LoroText, LoroTree, LoroValue,
19 StyleConfigMap, SubscribeJsonPathCallback, ValueOrContainer, VersionRange, VersionVector,
20 VersionVectorDiff,
21};
22
23#[inline]
25pub fn decode_import_blob_meta(
26 bytes: &[u8],
27 check_checksum: bool,
28) -> LoroResult<ImportBlobMetadata> {
29 let s = InnerLoroDoc::decode_import_blob_meta(bytes, check_checksum)?;
30 Ok(s.into())
31}
32
33pub struct LoroDoc {
34 pub(crate) doc: InnerLoroDoc,
35}
36
37impl LoroDoc {
38 pub fn new() -> Self {
39 Self {
40 doc: InnerLoroDoc::new(),
41 }
42 }
43
44 pub fn fork(&self) -> Arc<Self> {
45 let doc = self.doc.fork();
46 Arc::new(LoroDoc { doc })
47 }
48
49 pub fn fork_at(&self, frontiers: &Frontiers) -> LoroResult<Arc<Self>> {
50 let doc = self.doc.fork_at(&frontiers.into())?;
51 Ok(Arc::new(LoroDoc { doc }))
52 }
53
54 #[inline]
56 pub fn config(&self) -> Arc<Configure> {
57 Arc::new(self.doc.config().clone().into())
58 }
59
60 #[inline]
74 pub fn get_change(&self, id: ID) -> Option<ChangeMeta> {
75 self.doc.get_change(id).map(|x| x.into())
76 }
77
78 #[inline]
88 pub fn set_record_timestamp(&self, record: bool) {
89 self.doc.set_record_timestamp(record);
90 }
91
92 #[inline]
100 pub fn set_change_merge_interval(&self, interval: i64) {
101 self.doc.set_change_merge_interval(interval);
102 }
103
104 #[inline]
112 pub fn config_text_style(&self, text_style: Arc<StyleConfigMap>) {
113 self.doc.config_text_style(text_style.as_ref().to_loro())
114 }
115
116 pub fn config_default_text_style(&self, text_style: Option<StyleConfig>) {
125 self.doc.config_default_text_style(text_style);
126 }
127
128 #[inline]
135 pub fn attach(&self) {
136 self.doc.attach()
137 }
138
139 #[inline]
148 pub fn checkout(&self, frontiers: &Frontiers) -> LoroResult<()> {
149 self.doc.checkout(&frontiers.into())
150 }
151
152 #[inline]
161 pub fn checkout_to_latest(&self) {
162 self.doc.checkout_to_latest()
163 }
164
165 #[inline]
169 pub fn cmp_with_frontiers(&self, other: &Frontiers) -> Ordering {
170 self.doc.cmp_with_frontiers(&other.into())
171 }
172
173 pub fn cmp_frontiers(
175 &self,
176 a: &Frontiers,
177 b: &Frontiers,
178 ) -> Result<Option<Ordering>, FrontiersNotIncluded> {
179 self.doc.cmp_frontiers(&a.into(), &b.into())
180 }
181
182 #[inline]
188 pub fn detach(&self) {
189 self.doc.detach()
190 }
191
192 #[inline]
196 pub fn import_batch(&self, bytes: &[Vec<u8>]) -> Result<ImportStatus, LoroError> {
197 let status = self.doc.import_batch(bytes)?;
198 Ok(status.into())
199 }
200
201 pub fn get_movable_list(&self, id: Arc<dyn ContainerIdLike>) -> Arc<LoroMovableList> {
202 Arc::new(LoroMovableList {
203 inner: self.doc.get_movable_list(loro::ContainerID::from(
204 id.as_container_id(crate::ContainerType::MovableList),
205 )),
206 })
207 }
208
209 pub fn try_get_movable_list(
210 &self,
211 id: Arc<dyn ContainerIdLike>,
212 ) -> Option<Arc<LoroMovableList>> {
213 self.doc
214 .try_get_movable_list(loro::ContainerID::from(
215 id.as_container_id(crate::ContainerType::MovableList),
216 ))
217 .map(|inner| Arc::new(LoroMovableList { inner }))
218 }
219
220 pub fn get_list(&self, id: Arc<dyn ContainerIdLike>) -> Arc<LoroList> {
221 Arc::new(LoroList {
222 inner: self.doc.get_list(loro::ContainerID::from(
223 id.as_container_id(crate::ContainerType::List),
224 )),
225 })
226 }
227
228 pub fn try_get_list(&self, id: Arc<dyn ContainerIdLike>) -> Option<Arc<LoroList>> {
229 self.doc
230 .try_get_list(loro::ContainerID::from(
231 id.as_container_id(crate::ContainerType::List),
232 ))
233 .map(|inner| Arc::new(LoroList { inner }))
234 }
235
236 pub fn get_map(&self, id: Arc<dyn ContainerIdLike>) -> Arc<LoroMap> {
237 Arc::new(LoroMap {
238 inner: self.doc.get_map(loro::ContainerID::from(
239 id.as_container_id(crate::ContainerType::Map),
240 )),
241 })
242 }
243
244 pub fn try_get_map(&self, id: Arc<dyn ContainerIdLike>) -> Option<Arc<LoroMap>> {
245 self.doc
246 .try_get_map(loro::ContainerID::from(
247 id.as_container_id(crate::ContainerType::Map),
248 ))
249 .map(|inner| Arc::new(LoroMap { inner }))
250 }
251
252 pub fn get_text(&self, id: Arc<dyn ContainerIdLike>) -> Arc<LoroText> {
253 Arc::new(LoroText {
254 inner: self.doc.get_text(loro::ContainerID::from(
255 id.as_container_id(crate::ContainerType::Text),
256 )),
257 })
258 }
259
260 pub fn try_get_text(&self, id: Arc<dyn ContainerIdLike>) -> Option<Arc<LoroText>> {
261 self.doc
262 .try_get_text(loro::ContainerID::from(
263 id.as_container_id(crate::ContainerType::Text),
264 ))
265 .map(|inner| Arc::new(LoroText { inner }))
266 }
267
268 pub fn get_tree(&self, id: Arc<dyn ContainerIdLike>) -> Arc<LoroTree> {
269 Arc::new(LoroTree {
270 inner: self.doc.get_tree(loro::ContainerID::from(
271 id.as_container_id(crate::ContainerType::Tree),
272 )),
273 })
274 }
275
276 pub fn try_get_tree(&self, id: Arc<dyn ContainerIdLike>) -> Option<Arc<LoroTree>> {
277 self.doc
278 .try_get_tree(loro::ContainerID::from(
279 id.as_container_id(crate::ContainerType::Tree),
280 ))
281 .map(|inner| Arc::new(LoroTree { inner }))
282 }
283
284 pub fn get_counter(&self, id: Arc<dyn ContainerIdLike>) -> Arc<LoroCounter> {
285 Arc::new(LoroCounter {
286 inner: self.doc.get_counter(loro::ContainerID::from(
287 id.as_container_id(crate::ContainerType::Counter),
288 )),
289 })
290 }
291
292 pub fn try_get_counter(&self, id: Arc<dyn ContainerIdLike>) -> Option<Arc<LoroCounter>> {
293 self.doc
294 .try_get_counter(loro::ContainerID::from(
295 id.as_container_id(crate::ContainerType::Counter),
296 ))
297 .map(|inner| Arc::new(LoroCounter { inner }))
298 }
299
300 pub fn get_container(&self, id: &ContainerID) -> Option<Arc<dyn ValueOrContainer>> {
301 self.doc
302 .get_container(id.clone().into())
303 .map(|c| Arc::new(loro::ValueOrContainer::Container(c)) as Arc<dyn ValueOrContainer>)
304 }
305
306 #[inline]
316 pub fn commit(&self) {
317 self.doc.commit()
318 }
319
320 pub fn commit_with(&self, options: CommitOptions) {
321 self.doc.commit_with(options.into())
322 }
323
324 pub fn set_next_commit_message(&self, msg: &str) {
328 self.doc.set_next_commit_message(msg)
329 }
330
331 pub fn set_next_commit_origin(&self, origin: &str) {
335 self.doc.set_next_commit_origin(origin)
336 }
337
338 pub fn set_next_commit_timestamp(&self, timestamp: i64) {
343 self.doc.set_next_commit_timestamp(timestamp)
344 }
345
346 pub fn set_next_commit_options(&self, options: CommitOptions) {
350 self.doc.set_next_commit_options(options.into())
351 }
352
353 pub fn clear_next_commit_options(&self) {
355 self.doc.clear_next_commit_options()
356 }
357
358 #[inline]
361 pub fn is_detached(&self) -> bool {
362 self.doc.is_detached()
363 }
364
365 #[inline]
367 pub fn import(&self, bytes: &[u8]) -> Result<ImportStatus, LoroError> {
368 let status = self.doc.import_with(bytes, "")?;
369 Ok(status.into())
370 }
371
372 #[inline]
377 pub fn import_with(&self, bytes: &[u8], origin: &str) -> Result<ImportStatus, LoroError> {
378 let status = self.doc.import_with(bytes, origin)?;
379 Ok(status.into())
380 }
381
382 pub fn import_json_updates(&self, json: &str) -> Result<ImportStatus, LoroError> {
383 let status = self.doc.import_json_updates(json)?;
384 Ok(status.into())
385 }
386
387 #[inline]
389 pub fn export_json_updates(&self, start_vv: &VersionVector, end_vv: &VersionVector) -> String {
390 let json = self
391 .doc
392 .export_json_updates(&start_vv.into(), &end_vv.into());
393 serde_json::to_string(&json).unwrap()
394 }
395
396 #[inline]
401 pub fn export_json_updates_without_peer_compression(
402 &self,
403 start_vv: &VersionVector,
404 end_vv: &VersionVector,
405 ) -> String {
406 serde_json::to_string(
407 &self
408 .doc
409 .export_json_updates_without_peer_compression(&start_vv.into(), &end_vv.into()),
410 )
411 .unwrap()
412 }
413
414 pub fn redact_json_updates(
427 &self,
428 json: &str,
429 version_range: &VersionRange,
430 ) -> Result<String, LoroError> {
431 let mut schema: JsonSchema =
432 serde_json::from_str(json).map_err(|_e| LoroError::InvalidJsonSchema)?;
433 loro::json::redact(&mut schema, version_range.into())
434 .map_err(|e| LoroError::Unknown(e.to_string().into_boxed_str()))?;
435 Ok(serde_json::to_string(&schema).unwrap())
436 }
437
438 pub fn export_json_in_id_span(&self, id_span: IdSpan) -> Vec<String> {
441 self.doc
442 .export_json_in_id_span(id_span)
443 .into_iter()
444 .map(|x| serde_json::to_string(&x).unwrap())
445 .collect()
446 }
447
448 #[inline]
450 pub fn export_updates(&self, vv: &VersionVector) -> Result<Vec<u8>, LoroEncodeError> {
451 self.doc.export(loro::ExportMode::Updates {
452 from: Cow::Owned(vv.into()),
453 })
454 }
455
456 #[inline]
458 pub fn export_snapshot(&self) -> Result<Vec<u8>, LoroEncodeError> {
459 self.doc.export(loro::ExportMode::Snapshot)
460 }
461
462 pub fn export_snapshot_at(&self, frontiers: &Frontiers) -> Result<Vec<u8>, LoroEncodeError> {
463 self.doc.export(loro::ExportMode::SnapshotAt {
464 version: Cow::Owned(frontiers.into()),
465 })
466 }
467
468 pub fn frontiers_to_vv(&self, frontiers: &Frontiers) -> Option<Arc<VersionVector>> {
469 self.doc
470 .frontiers_to_vv(&frontiers.into())
471 .map(|v| Arc::new(v.into()))
472 }
473
474 pub fn minimize_frontiers(&self, frontiers: &Frontiers) -> FrontiersOrID {
475 match self.doc.minimize_frontiers(&frontiers.into()) {
476 Ok(f) => FrontiersOrID {
477 frontiers: Some(Arc::new(f.into())),
478 id: None,
479 },
480 Err(id) => FrontiersOrID {
481 frontiers: None,
482 id: Some(id),
483 },
484 }
485 }
486
487 pub fn vv_to_frontiers(&self, vv: &VersionVector) -> Arc<Frontiers> {
488 Arc::new(self.doc.vv_to_frontiers(&vv.into()).into())
489 }
490
491 pub fn oplog_vv(&self) -> Arc<VersionVector> {
495 Arc::new(self.doc.oplog_vv().into())
496 }
497
498 pub fn state_vv(&self) -> Arc<VersionVector> {
499 Arc::new(self.doc.state_vv().into())
500 }
501
502 #[inline]
506 pub fn shallow_since_vv(&self) -> Arc<VersionVector> {
507 Arc::new(loro::VersionVector::from_im_vv(&self.doc.shallow_since_vv()).into())
508 }
509
510 #[inline]
512 pub fn len_ops(&self) -> u64 {
513 self.doc.len_ops() as u64
514 }
515
516 #[inline]
518 pub fn len_changes(&self) -> u64 {
519 self.doc.len_changes() as u64
520 }
521
522 #[inline]
524 pub fn get_value(&self) -> LoroValue {
525 self.doc.get_value().into()
526 }
527
528 pub fn get_deep_value(&self) -> LoroValue {
529 self.doc.get_deep_value().into()
530 }
531
532 pub fn get_deep_value_with_id(&self) -> LoroValue {
534 self.doc.get_deep_value_with_id().into()
535 }
536
537 pub fn oplog_frontiers(&self) -> Arc<Frontiers> {
538 Arc::new(self.doc.oplog_frontiers().into())
539 }
540
541 pub fn state_frontiers(&self) -> Arc<Frontiers> {
542 Arc::new(self.doc.state_frontiers().into())
543 }
544
545 #[inline]
547 pub fn peer_id(&self) -> PeerID {
548 self.doc.peer_id()
549 }
550
551 #[inline]
556 pub fn set_peer_id(&self, peer: PeerID) -> LoroResult<()> {
557 self.doc.set_peer_id(peer)
558 }
559
560 pub fn subscribe(
561 &self,
562 container_id: &ContainerID,
563 subscriber: Arc<dyn Subscriber>,
564 ) -> Arc<Subscription> {
565 Arc::new(
566 self.doc
567 .subscribe(
568 &(container_id.into()),
569 Arc::new(move |e| {
570 subscriber.on_diff(DiffEvent::from(e));
571 }),
572 )
573 .into(),
574 )
575 }
576
577 pub fn subscribe_root(&self, subscriber: Arc<dyn Subscriber>) -> Arc<Subscription> {
578 Arc::new(
580 self.doc
581 .subscribe_root(Arc::new(move |e| {
582 subscriber.on_diff(DiffEvent::from(e));
583 }))
584 .into(),
585 )
586 }
587
588 pub fn subscribe_local_update(
590 &self,
591 callback: Arc<dyn LocalUpdateCallback>,
592 ) -> Arc<Subscription> {
593 let s = self.doc.subscribe_local_update(Box::new(move |update| {
594 callback.on_local_update(update.to_vec());
596 true
597 }));
598 Arc::new(Subscription(Mutex::new(Some(s))))
599 }
600
601 #[inline]
604 pub fn check_state_correctness_slow(&self) {
605 self.doc.check_state_correctness_slow()
606 }
607
608 pub fn get_by_path(&self, path: &[Index]) -> Option<Arc<dyn ValueOrContainer>> {
609 self.doc
610 .get_by_path(&path.iter().map(|v| v.clone().into()).collect::<Vec<_>>())
611 .map(|x| Arc::new(x) as Arc<dyn ValueOrContainer>)
612 }
613
614 pub fn get_by_str_path(&self, path: &str) -> Option<Arc<dyn ValueOrContainer>> {
667 self.doc
668 .get_by_str_path(path)
669 .map(|v| Arc::new(v) as Arc<dyn ValueOrContainer>)
670 }
671
672 pub fn get_cursor_pos(
673 &self,
674 cursor: &Cursor,
675 ) -> Result<PosQueryResult, CannotFindRelativePosition> {
676 let loro::cursor::PosQueryResult { update, current } = self.doc.get_cursor_pos(cursor)?;
677 Ok(PosQueryResult {
678 current: AbsolutePosition {
679 pos: current.pos as u32,
680 side: current.side,
681 },
682 update: update.map(|x| Arc::new(x.into())),
683 })
684 }
685
686 #[inline]
688 pub fn has_history_cache(&self) -> bool {
689 self.doc.has_history_cache()
690 }
691
692 #[inline]
697 pub fn free_history_cache(&self) {
698 self.doc.free_history_cache()
699 }
700
701 #[inline]
703 pub fn free_diff_calculator(&self) {
704 self.doc.free_diff_calculator()
705 }
706
707 #[inline]
711 pub fn compact_change_store(&self) {
712 self.doc.compact_change_store()
713 }
714
715 pub fn export(&self, mode: ExportMode) -> Result<Vec<u8>, LoroEncodeError> {
717 self.doc.export(mode.into())
718 }
719
720 pub fn export_updates_in_range(&self, spans: &[IdSpan]) -> Result<Vec<u8>, LoroEncodeError> {
721 self.doc.export(loro::ExportMode::UpdatesInRange {
722 spans: Cow::Borrowed(spans),
723 })
724 }
725
726 pub fn export_shallow_snapshot(
727 &self,
728 frontiers: &Frontiers,
729 ) -> Result<Vec<u8>, LoroEncodeError> {
730 self.doc
731 .export(loro::ExportMode::ShallowSnapshot(Cow::Owned(
732 frontiers.into(),
733 )))
734 }
735
736 pub fn export_state_only(
737 &self,
738 frontiers: Option<Arc<Frontiers>>,
739 ) -> Result<Vec<u8>, LoroEncodeError> {
740 self.doc
741 .export(loro::ExportMode::StateOnly(frontiers.map(|x| {
742 let a = x.as_ref();
743 Cow::Owned(loro::Frontiers::from(a))
744 })))
745 }
746
747 pub fn analyze(&self) -> DocAnalysis {
752 self.doc.analyze()
753 }
754
755 pub fn get_path_to_container(&self, id: &ContainerID) -> Option<Vec<ContainerPath>> {
757 self.doc.get_path_to_container(&id.into()).map(|x| {
758 x.into_iter()
759 .map(|(id, idx)| ContainerPath {
760 id: id.into(),
761 path: (&idx).into(),
762 })
763 .collect()
764 })
765 }
766
767 #[inline]
783 pub fn jsonpath(&self, path: &str) -> Result<Vec<Arc<dyn ValueOrContainer>>, JsonPathError> {
784 self.doc.jsonpath(path).map(|vec| {
785 vec.into_iter()
786 .map(|v| Arc::new(v) as Arc<dyn ValueOrContainer>)
787 .collect()
788 })
789 }
790
791 #[inline]
796 pub fn subscribe_jsonpath(
797 &self,
798 path: &str,
799 callback: Arc<dyn JsonPathSubscriber>,
800 ) -> LoroResult<Arc<Subscription>> {
801 let callback: SubscribeJsonPathCallback = Arc::new(move || {
802 callback.on_jsonpath_changed();
803 });
804
805 self.doc
806 .subscribe_jsonpath(path, callback)
807 .map(|subscription| Arc::new(subscription.into()))
808 }
809
810 pub fn travel_change_ancestors(
811 &self,
812 ids: &[ID],
813 f: Arc<dyn ChangeAncestorsTraveler>,
814 ) -> Result<(), ChangeTravelError> {
815 self.doc
816 .travel_change_ancestors(ids, &mut |change| match f.travel(change.into()) {
817 true => ControlFlow::Continue(()),
818 false => ControlFlow::Break(()),
819 })
820 }
821
822 pub fn get_changed_containers_in(&self, id: ID, len: u32) -> Vec<ContainerID> {
823 self.doc
824 .get_changed_containers_in(id, len as usize)
825 .into_iter()
826 .map(|x| x.into())
827 .collect()
828 }
829
830 pub fn is_shallow(&self) -> bool {
831 self.doc.is_shallow()
832 }
833
834 pub fn get_pending_txn_len(&self) -> u32 {
835 self.doc.get_pending_txn_len() as u32
836 }
837
838 #[inline]
840 pub fn find_id_spans_between(&self, from: &Frontiers, to: &Frontiers) -> VersionVectorDiff {
841 self.doc
842 .find_id_spans_between(&from.into(), &to.into())
843 .into()
844 }
845
846 #[inline]
852 pub fn revert_to(&self, version: &Frontiers) -> LoroResult<()> {
853 self.doc.revert_to(&version.into())
854 }
855
856 #[inline]
860 pub fn apply_diff(&self, diff: &DiffBatch) -> LoroResult<()> {
861 self.doc.apply_diff(diff.clone().into())
862 }
863
864 #[inline]
866 pub fn diff(&self, a: &Frontiers, b: &Frontiers) -> LoroResult<Arc<DiffBatch>> {
867 self.doc
868 .diff(&a.into(), &b.into())
869 .map(|x| Arc::new(x.into()))
870 }
871
872 pub fn has_container(&self, id: &ContainerID) -> bool {
877 self.doc.has_container(&id.into())
878 }
879
880 pub fn subscribe_first_commit_from_peer(
886 &self,
887 subscriber: Arc<dyn FirstCommitFromPeerCallback>,
888 ) -> Arc<Subscription> {
889 let subscriber: loro::FirstCommitFromPeerCallback = Box::new(move |e| {
890 subscriber.on_first_commit_from_peer(FirstCommitFromPeerPayload { peer: e.peer });
891 true
892 });
893 Arc::new(self.doc.subscribe_first_commit_from_peer(subscriber).into())
894 }
895
896 pub fn subscribe_pre_commit(&self, callback: Arc<dyn PreCommitCallback>) -> Arc<Subscription> {
901 let subscriber: loro::PreCommitCallback = Box::new(move |e| {
902 callback.on_pre_commit(PreCommitCallbackPayload {
903 change_meta: e.change_meta.clone().into(),
904 origin: e.origin.clone(),
905 modifier: Arc::new(ChangeModifier(e.modifier.clone())),
906 });
907 true
908 });
909 Arc::new(self.doc.subscribe_pre_commit(subscriber).into())
910 }
911
912 pub fn set_hide_empty_root_containers(&self, hide: bool) {
914 self.doc.set_hide_empty_root_containers(hide);
915 }
916
917 pub fn delete_root_container(&self, cid: ContainerID) {
925 self.doc.delete_root_container(cid.into());
926 }
927}
928
929#[uniffi::trait_interface]
930pub trait ChangeAncestorsTraveler: Sync + Send {
931 fn travel(&self, change: ChangeMeta) -> bool;
932}
933
934impl Default for LoroDoc {
935 fn default() -> Self {
936 Self::new()
937 }
938}
939
940impl Deref for LoroDoc {
941 type Target = InnerLoroDoc;
942 fn deref(&self) -> &Self::Target {
943 &self.doc
944 }
945}
946
947pub struct ChangeMeta {
948 pub lamport: Lamport,
950 pub id: ID,
952 pub timestamp: Timestamp,
955 pub message: Option<String>,
957 pub deps: Arc<Frontiers>,
959 pub len: u32,
961}
962
963impl From<loro::ChangeMeta> for ChangeMeta {
964 fn from(value: loro::ChangeMeta) -> Self {
965 Self {
966 lamport: value.lamport,
967 id: value.id,
968 timestamp: value.timestamp,
969 message: value.message.map(|x| (*x).to_string()),
970 deps: Arc::new(value.deps.into()),
971 len: value.len as u32,
972 }
973 }
974}
975
976pub struct ImportBlobMetadata {
977 pub partial_start_vv: Arc<VersionVector>,
983 pub partial_end_vv: Arc<VersionVector>,
989 pub start_timestamp: i64,
990 pub start_frontiers: Arc<Frontiers>,
991 pub end_timestamp: i64,
992 pub change_num: u32,
993 pub mode: String,
994}
995
996impl From<loro::ImportBlobMetadata> for ImportBlobMetadata {
997 fn from(value: loro::ImportBlobMetadata) -> Self {
998 Self {
999 partial_start_vv: Arc::new(value.partial_start_vv.into()),
1000 partial_end_vv: Arc::new(value.partial_end_vv.into()),
1001 start_timestamp: value.start_timestamp,
1002 start_frontiers: Arc::new(value.start_frontiers.into()),
1003 end_timestamp: value.end_timestamp,
1004 change_num: value.change_num,
1005 mode: value.mode.to_string(),
1006 }
1007 }
1008}
1009
1010pub struct CommitOptions {
1011 pub origin: Option<String>,
1012 pub immediate_renew: bool,
1013 pub timestamp: Option<Timestamp>,
1014 pub commit_msg: Option<String>,
1015}
1016
1017impl From<CommitOptions> for loro::CommitOptions {
1018 fn from(value: CommitOptions) -> Self {
1019 loro::CommitOptions {
1020 origin: value.origin.map(|x| x.into()),
1021 immediate_renew: value.immediate_renew,
1022 timestamp: value.timestamp,
1023 commit_msg: value.commit_msg.map(|x| x.into()),
1024 }
1025 }
1026}
1027
1028pub trait JsonSchemaLike {
1029 fn to_json_schema(&self) -> LoroResult<JsonSchema>;
1030}
1031
1032impl<T: TryInto<JsonSchema> + Clone> JsonSchemaLike for T {
1033 fn to_json_schema(&self) -> LoroResult<JsonSchema> {
1034 self.clone()
1035 .try_into()
1036 .map_err(|_| LoroError::InvalidJsonSchema)
1037 }
1038}
1039
1040#[uniffi::trait_interface]
1041pub trait JsonPathSubscriber: Sync + Send {
1042 fn on_jsonpath_changed(&self);
1043}
1044
1045#[uniffi::trait_interface]
1046pub trait LocalUpdateCallback: Sync + Send {
1047 fn on_local_update(&self, update: Vec<u8>);
1048}
1049
1050#[uniffi::trait_interface]
1051pub trait FirstCommitFromPeerCallback: Sync + Send {
1052 fn on_first_commit_from_peer(&self, e: FirstCommitFromPeerPayload);
1053}
1054
1055pub struct FirstCommitFromPeerPayload {
1056 pub peer: PeerID,
1057}
1058
1059#[uniffi::trait_interface]
1060pub trait PreCommitCallback: Sync + Send {
1061 fn on_pre_commit(&self, e: PreCommitCallbackPayload);
1062}
1063
1064pub struct PreCommitCallbackPayload {
1065 pub change_meta: ChangeMeta,
1066 pub origin: String,
1067 pub modifier: Arc<ChangeModifier>,
1068}
1069
1070pub struct ChangeModifier(loro::ChangeModifier);
1071
1072impl ChangeModifier {
1073 pub fn set_message(&self, msg: &str) {
1074 self.0.set_message(msg);
1075 }
1076 pub fn set_timestamp(&self, timestamp: Timestamp) {
1077 self.0.set_timestamp(timestamp);
1078 }
1079}
1080
1081#[uniffi::trait_interface]
1082pub trait Unsubscriber: Sync + Send {
1083 fn on_unsubscribe(&self);
1084}
1085
1086pub struct Subscription(pub(crate) Mutex<Option<loro::Subscription>>);
1089
1090impl Subscription {
1091 pub fn detach(self: Arc<Self>) {
1095 let s = self.0.lock().unwrap().take().unwrap();
1096 s.detach();
1097 }
1098
1099 pub fn unsubscribe(self: Arc<Self>) {
1101 let s = self.0.lock().unwrap().take().unwrap();
1102 s.unsubscribe();
1103 }
1104}
1105
1106impl std::fmt::Debug for Subscription {
1107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1108 f.write_str("Subscription")
1109 }
1110}
1111
1112impl From<loro::Subscription> for Subscription {
1113 fn from(value: loro::Subscription) -> Self {
1114 Self(Mutex::new(Some(value)))
1115 }
1116}
1117
1118pub struct PosQueryResult {
1119 pub update: Option<Arc<Cursor>>,
1120 pub current: AbsolutePosition,
1121}
1122
1123pub enum ExportMode {
1124 Snapshot,
1125 Updates { from: Arc<VersionVector> },
1126 UpdatesInRange { spans: Vec<IdSpan> },
1127 ShallowSnapshot { frontiers: Arc<Frontiers> },
1128 StateOnly { frontiers: Option<Arc<Frontiers>> },
1129 SnapshotAt { frontiers: Arc<Frontiers> },
1130}
1131
1132impl From<ExportMode> for loro::ExportMode<'static> {
1133 fn from(value: ExportMode) -> Self {
1134 match value {
1135 ExportMode::Snapshot => loro::ExportMode::Snapshot,
1136 ExportMode::Updates { from } => loro::ExportMode::Updates {
1137 from: Cow::Owned(from.as_ref().into()),
1138 },
1139 ExportMode::UpdatesInRange { spans } => loro::ExportMode::UpdatesInRange {
1140 spans: Cow::Owned(spans),
1141 },
1142 ExportMode::ShallowSnapshot { frontiers } => {
1143 loro::ExportMode::ShallowSnapshot(Cow::Owned(frontiers.as_ref().into()))
1144 }
1145 ExportMode::StateOnly { frontiers } => {
1146 loro::ExportMode::StateOnly(frontiers.map(|x| Cow::Owned(x.as_ref().into())))
1147 }
1148 ExportMode::SnapshotAt { frontiers } => loro::ExportMode::SnapshotAt {
1149 version: Cow::Owned(frontiers.as_ref().into()),
1150 },
1151 }
1152 }
1153}
1154
1155pub struct ContainerPath {
1156 pub id: ContainerID,
1157 pub path: Index,
1158}
1159
1160pub struct ImportStatus {
1161 pub success: HashMap<u64, CounterSpan>,
1162 pub pending: Option<HashMap<u64, CounterSpan>>,
1163}
1164
1165impl From<loro::ImportStatus> for ImportStatus {
1166 fn from(value: loro::ImportStatus) -> Self {
1167 let a = &value.success;
1168 Self {
1169 success: vr_to_map(a),
1170 pending: value.pending.as_ref().map(vr_to_map),
1171 }
1172 }
1173}
1174
1175fn vr_to_map(a: &loro::VersionRange) -> HashMap<u64, CounterSpan> {
1176 a.iter()
1177 .map(|x| {
1178 (
1179 *x.0,
1180 CounterSpan {
1181 start: x.1 .0,
1182 end: x.1 .1,
1183 },
1184 )
1185 })
1186 .collect()
1187}
1188
1189pub struct FrontiersOrID {
1190 pub frontiers: Option<Arc<Frontiers>>,
1191 pub id: Option<ID>,
1192}