loro/
lib.rs

1#![doc = include_str!("../README.md")]
2#![allow(clippy::uninlined_format_args)]
3#![warn(missing_docs)]
4#![warn(missing_debug_implementations)]
5use event::DiffBatch;
6use event::{DiffEvent, Subscriber};
7pub use loro_common::InternalString;
8pub use loro_internal::cursor::CannotFindRelativePosition;
9use loro_internal::cursor::Cursor;
10use loro_internal::cursor::PosQueryResult;
11use loro_internal::cursor::Side;
12pub use loro_internal::encoding::ImportStatus;
13use loro_internal::handler::{HandlerTrait, ValueOrHandler};
14pub use loro_internal::loro::ChangeTravelError;
15pub use loro_internal::pre_commit::{
16    ChangeModifier, FirstCommitFromPeerCallback, FirstCommitFromPeerPayload, PreCommitCallback,
17    PreCommitCallbackPayload,
18};
19pub use loro_internal::sync;
20pub use loro_internal::undo::{OnPop, UndoItemMeta, UndoOrRedo};
21use loro_internal::version::shrink_frontiers;
22pub use loro_internal::version::ImVersionVector;
23use loro_internal::DocState;
24use loro_internal::LoroDoc as InnerLoroDoc;
25use loro_internal::OpLog;
26use loro_internal::{
27    handler::Handler as InnerHandler, ListHandler as InnerListHandler,
28    MapHandler as InnerMapHandler, MovableListHandler as InnerMovableListHandler,
29    TextHandler as InnerTextHandler, TreeHandler as InnerTreeHandler,
30    UnknownHandler as InnerUnknownHandler,
31};
32use rustc_hash::FxHashSet;
33use std::cmp::Ordering;
34use std::ops::ControlFlow;
35use std::ops::Deref;
36use std::ops::Range;
37use std::sync::Arc;
38use tracing::info;
39
40pub use loro_internal::diff::diff_impl::UpdateOptions;
41pub use loro_internal::diff::diff_impl::UpdateTimeoutError;
42pub use loro_internal::subscription::LocalUpdateCallback;
43pub use loro_internal::subscription::PeerIdUpdateCallback;
44pub use loro_internal::ChangeMeta;
45pub use loro_internal::LORO_VERSION;
46pub mod event;
47pub use loro_internal::awareness;
48pub use loro_internal::change::Timestamp;
49pub use loro_internal::configure::Configure;
50pub use loro_internal::configure::{StyleConfig, StyleConfigMap};
51pub use loro_internal::container::richtext::ExpandType;
52pub use loro_internal::container::{ContainerID, ContainerType, IntoContainerId};
53pub use loro_internal::cursor;
54pub use loro_internal::delta::{TreeDeltaItem, TreeDiff, TreeDiffItem, TreeExternalDiff};
55pub use loro_internal::encoding::ImportBlobMetadata;
56pub use loro_internal::encoding::{EncodedBlobMode, ExportMode};
57pub use loro_internal::event::{EventTriggerKind, Index};
58pub use loro_internal::handler::TextDelta;
59pub use loro_internal::json;
60pub use loro_internal::json::{
61    FutureOp as JsonFutureOp, FutureOpWrapper as JsonFutureOpWrapper, JsonChange, JsonOp,
62    JsonOpContent, JsonSchema, ListOp as JsonListOp, MapOp as JsonMapOp,
63    MovableListOp as JsonMovableListOp, TextOp as JsonTextOp, TreeOp as JsonTreeOp,
64};
65pub use loro_internal::kv_store::{KvStore, MemKvStore};
66pub use loro_internal::loro::CommitOptions;
67pub use loro_internal::loro::DocAnalysis;
68pub use loro_internal::oplog::FrontiersNotIncluded;
69pub use loro_internal::undo;
70pub use loro_internal::version::{Frontiers, VersionRange, VersionVector, VersionVectorDiff};
71pub use loro_internal::ApplyDiff;
72pub use loro_internal::Subscription;
73pub use loro_internal::UndoManager as InnerUndoManager;
74pub use loro_internal::{loro_value, to_value};
75pub use loro_internal::{
76    Counter, CounterSpan, FractionalIndex, IdLp, IdSpan, Lamport, PeerID, TreeID, TreeParentId, ID,
77};
78pub use loro_internal::{
79    LoroBinaryValue, LoroEncodeError, LoroError, LoroListValue, LoroMapValue, LoroResult,
80    LoroStringValue, LoroTreeError, LoroValue, ToJson,
81};
82pub use loro_kv_store as kv_store;
83
84#[cfg(feature = "jsonpath")]
85pub use loro_internal::jsonpath;
86#[cfg(feature = "jsonpath")]
87pub use loro_internal::jsonpath::SubscribeJsonPathCallback;
88
89#[cfg(feature = "counter")]
90mod counter;
91#[cfg(feature = "counter")]
92pub use counter::LoroCounter;
93
94/// `LoroDoc` is the entry for the whole document.
95/// When it's dropped, all the associated [`Container`]s will be invalidated.
96///
97/// **Important:** Loro is a pure library and does not handle network protocols.
98/// It is the responsibility of the user to manage the storage, loading, and synchronization
99/// of the bytes exported by Loro in a manner suitable for their specific environment.
100#[derive(Debug)]
101pub struct LoroDoc {
102    doc: InnerLoroDoc,
103    // This field is here to prevent some weird issues in debug mode
104    #[cfg(debug_assertions)]
105    _temp: u8,
106}
107
108impl Default for LoroDoc {
109    fn default() -> Self {
110        Self::new()
111    }
112}
113
114impl Clone for LoroDoc {
115    /// This creates a reference clone, not a deep clone. The cloned doc will share the same
116    /// underlying doc as the original one.
117    ///
118    /// For deep clone, please use the `.fork()` method.
119    fn clone(&self) -> Self {
120        let doc = self.doc.clone();
121        LoroDoc::_new(doc)
122    }
123}
124
125impl LoroDoc {
126    #[inline(always)]
127    fn _new(doc: InnerLoroDoc) -> Self {
128        Self {
129            doc,
130            #[cfg(debug_assertions)]
131            _temp: 0,
132        }
133    }
134
135    /// Create a new `LoroDoc` instance.
136    #[inline]
137    pub fn new() -> Self {
138        let doc = InnerLoroDoc::default();
139        doc.start_auto_commit();
140
141        LoroDoc::_new(doc)
142    }
143
144    /// Duplicate the document with a different PeerID
145    ///
146    /// The time complexity and space complexity of this operation are both O(n),
147    ///
148    /// When called in detached mode, it will fork at the current state frontiers.
149    /// It will have the same effect as `fork_at(&self.state_frontiers())`.
150    #[inline]
151    pub fn fork(&self) -> Self {
152        let doc = self.doc.fork();
153        LoroDoc::_new(doc)
154    }
155
156    /// Fork the document at the given frontiers.
157    ///
158    /// The created doc will only contain the history before the specified frontiers.
159    pub fn fork_at(&self, frontiers: &Frontiers) -> LoroDoc {
160        let new_doc = self.doc.fork_at(frontiers);
161        new_doc.start_auto_commit();
162        LoroDoc::_new(new_doc)
163    }
164
165    /// Get the configurations of the document.
166    #[inline]
167    pub fn config(&self) -> &Configure {
168        self.doc.config()
169    }
170
171    /// Get `Change` at the given id.
172    ///
173    /// `Change` is a grouped continuous operations that share the same id, timestamp, commit message.
174    ///
175    /// - The id of the `Change` is the id of its first op.
176    /// - The second op's id is `{ peer: change.id.peer, counter: change.id.counter + 1 }`
177    ///
178    /// The same applies on `Lamport`:
179    ///
180    /// - The lamport of the `Change` is the lamport of its first op.
181    /// - The second op's lamport is `change.lamport + 1`
182    ///
183    /// The length of the `Change` is how many operations it contains
184    pub fn get_change(&self, id: ID) -> Option<ChangeMeta> {
185        let change = self.doc.oplog().lock().unwrap().get_change_at(id)?;
186        Some(ChangeMeta::from_change(&change))
187    }
188
189    /// Decodes the metadata for an imported blob from the provided bytes.
190    ///
191    /// # Example
192    /// ```
193    /// use loro::{LoroDoc, ExportMode};
194    ///
195    /// let doc = LoroDoc::new();
196    /// doc.get_text("t").insert(0, "Hello").unwrap();
197    /// let updates = doc.export(ExportMode::all_updates()).unwrap();
198    /// let meta = LoroDoc::decode_import_blob_meta(&updates, true).unwrap();
199    /// assert!(meta.change_num >= 1);
200    /// ```
201    #[inline]
202    pub fn decode_import_blob_meta(
203        bytes: &[u8],
204        check_checksum: bool,
205    ) -> LoroResult<ImportBlobMetadata> {
206        InnerLoroDoc::decode_import_blob_meta(bytes, check_checksum)
207    }
208
209    /// Set whether to record the timestamp of each change. Default is `false`.
210    ///
211    /// If enabled, the Unix timestamp will be recorded for each change automatically.
212    /// You can also set a timestamp explicitly via [`set_next_commit_timestamp`].
213    ///
214    /// Important: this is a runtime configuration. It is not serialized into updates or
215    /// snapshots. You must reapply it for each new `LoroDoc` you create or load.
216    ///
217    /// NOTE: Timestamps are forced to be in ascending order. If you commit a new change with
218    /// a timestamp earlier than the latest, the largest existing timestamp will be used instead.
219    ///
220    /// # Example
221    /// ```
222    /// use loro::LoroDoc;
223    /// let doc = LoroDoc::new();
224    /// doc.set_record_timestamp(true);
225    /// doc.get_text("t").insert(0, "hi").unwrap();
226    /// doc.commit();
227    /// ```
228    #[inline]
229    pub fn set_record_timestamp(&self, record: bool) {
230        self.doc.set_record_timestamp(record);
231    }
232
233    /// Enables editing in detached mode, which is disabled by default.
234    ///
235    /// The doc enter detached mode after calling `detach` or checking out a non-latest version.
236    ///
237    /// # Important Notes:
238    ///
239    /// - This mode uses a different PeerID for each checkout.
240    /// - Ensure no concurrent operations share the same PeerID if set manually.
241    /// - Importing does not affect the document's state or version; changes are
242    ///   recorded in the [OpLog] only. Call `checkout` to apply changes.
243    ///
244    /// # Example
245    /// ```
246    /// use loro::LoroDoc;
247    ///
248    /// let doc = LoroDoc::new();
249    /// let v0 = doc.state_frontiers();
250    /// // Make some edits…
251    /// doc.get_text("t").insert(0, "Hello").unwrap();
252    /// doc.commit();
253    ///
254    /// // Travel back and enable detached editing
255    /// doc.checkout(&v0).unwrap();
256    /// assert!(doc.is_detached());
257    /// doc.set_detached_editing(true);
258    /// doc.get_text("t").insert(0, "old").unwrap();
259    /// // Later, re-attach to see latest again
260    /// doc.attach();
261    /// ```
262    #[inline]
263    pub fn set_detached_editing(&self, enable: bool) {
264        self.doc.set_detached_editing(enable);
265    }
266
267    /// Whether editing the doc in detached mode is allowed, which is disabled by
268    /// default.
269    ///
270    /// The doc enter detached mode after calling `detach` or checking out a non-latest version.
271    ///
272    /// # Important Notes:
273    ///
274    /// - This mode uses a different PeerID for each checkout.
275    /// - Ensure no concurrent operations share the same PeerID if set manually.
276    /// - Importing does not affect the document's state or version; changes are
277    ///   recorded in the [OpLog] only. Call `checkout` to apply changes.
278    #[inline]
279    pub fn is_detached_editing_enabled(&self) -> bool {
280        self.doc.is_detached_editing_enabled()
281    }
282
283    /// Set the interval of mergeable changes, **in seconds**.
284    ///
285    /// If two continuous local changes are within the interval, they will be merged into one change.
286    /// The default value is 1000 seconds.
287    ///
288    /// By default, we record timestamps in seconds for each change. So if the merge interval is 1, and changes A and B
289    /// have timestamps of 3 and 4 respectively, then they will be merged into one change.
290    #[inline]
291    pub fn set_change_merge_interval(&self, interval: i64) {
292        self.doc.set_change_merge_interval(interval);
293    }
294
295    /// Set the rich text format configuration of the document.
296    ///
297    /// Configure the `expand` behavior for marks used by [`LoroText::mark`]/[`LoroText::unmark`].
298    /// This controls how marks grow when text is inserted at their boundaries.
299    ///
300    /// - `after` (default): inserts just after the range expand the mark
301    /// - `before`: inserts just before the range expand the mark
302    /// - `both`: inserts on either side expand the mark
303    /// - `none`: do not expand at boundaries
304    ///
305    /// # Example
306    /// ```
307    /// use loro::{LoroDoc, StyleConfigMap, StyleConfig, ExpandType};
308    /// let doc = LoroDoc::new();
309    /// let mut styles = StyleConfigMap::new();
310    /// styles.insert("bold".into(), StyleConfig { expand: ExpandType::After });
311    /// doc.config_text_style(styles);
312    /// ```
313    #[inline]
314    pub fn config_text_style(&self, text_style: StyleConfigMap) {
315        self.doc.config_text_style(text_style)
316    }
317
318    /// Configures the default text style for the document.
319    ///
320    /// This method sets the default text style configuration for the document when using LoroText.
321    /// If `None` is provided, the default style is reset.
322    ///
323    /// # Parameters
324    ///
325    /// - `text_style`: The style configuration to set as the default. `None` to reset.
326    ///
327    /// # Example
328    /// ```
329    /// use loro::{LoroDoc, StyleConfig, ExpandType};
330    /// let doc = LoroDoc::new();
331    /// doc.config_default_text_style(Some(StyleConfig { expand: ExpandType::After }));
332    /// ```
333    pub fn config_default_text_style(&self, text_style: Option<StyleConfig>) {
334        self.doc.config_default_text_style(text_style);
335    }
336
337    /// Attach the document state to the latest known version.
338    ///
339    /// > The document becomes detached during a `checkout` operation.
340    /// > Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
341    /// > In a detached state, the document is not editable, and any `import` operations will be
342    /// > recorded in the `OpLog` without being applied to the `DocState`.
343    #[inline]
344    pub fn attach(&self) {
345        self.doc.attach()
346    }
347
348    /// Checkout the `DocState` to a specific version.
349    ///
350    /// The document becomes detached during a `checkout` operation.
351    /// Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
352    /// In a detached state, the document is not editable, and any `import` operations will be
353    /// recorded in the `OpLog` without being applied to the `DocState`.
354    ///
355    /// You should call `attach` (or `checkout_to_latest`) to reattach the `DocState` to the latest version of `OpLog`.
356    /// If you need to edit while detached, enable [`set_detached_editing(true)`], but note it uses a different
357    /// PeerID per checkout.
358    #[inline]
359    pub fn checkout(&self, frontiers: &Frontiers) -> LoroResult<()> {
360        self.doc.checkout(frontiers)
361    }
362
363    /// Checkout the `DocState` to the latest version.
364    ///
365    /// > The document becomes detached during a `checkout` operation.
366    /// > Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
367    /// > In a detached state, the document is not editable, and any `import` operations will be
368    /// > recorded in the `OpLog` without being applied to the `DocState`.
369    ///
370    /// This has the same effect as `attach`.
371    #[inline]
372    pub fn checkout_to_latest(&self) {
373        self.doc.checkout_to_latest()
374    }
375
376    /// Compare the frontiers with the current OpLog's version.
377    ///
378    /// If `other` contains any version that's not contained in the current OpLog, return [Ordering::Less].
379    #[inline]
380    pub fn cmp_with_frontiers(&self, other: &Frontiers) -> Ordering {
381        self.doc.cmp_with_frontiers(other)
382    }
383
384    /// Compare two frontiers.
385    ///
386    /// If the frontiers are not included in the document, return [`FrontiersNotIncluded`].
387    #[inline]
388    pub fn cmp_frontiers(
389        &self,
390        a: &Frontiers,
391        b: &Frontiers,
392    ) -> Result<Option<Ordering>, FrontiersNotIncluded> {
393        self.doc.cmp_frontiers(a, b)
394    }
395
396    /// Force the document enter the detached mode.
397    ///
398    /// In this mode, importing new updates only records them in the OpLog; the [loro_internal::DocState] is not updated until you reattach.
399    ///
400    /// Learn more at https://loro.dev/docs/advanced/doc_state_and_oplog#attacheddetached-status
401    #[inline]
402    pub fn detach(&self) {
403        self.doc.detach()
404    }
405
406    /// Import a batch of updates/snapshot.
407    ///
408    /// The data can be in arbitrary order. The import result will be the same.
409    /// Auto-commit: same as [`import`], this finalizes the current transaction first.
410    ///
411    /// # Example
412    /// ```
413    /// use loro::{LoroDoc, ExportMode};
414    /// let a = LoroDoc::new();
415    /// a.get_text("t").insert(0, "A").unwrap();
416    /// let u1 = a.export(ExportMode::all_updates()).unwrap();
417    /// a.get_text("t").insert(1, "B").unwrap();
418    /// let u2 = a.export(ExportMode::all_updates()).unwrap();
419    ///
420    /// let b = LoroDoc::new();
421    /// let status = b.import_batch(&[u2, u1]).unwrap(); // arbitrary order
422    /// assert!(status.pending.is_none());
423    /// ```
424    #[inline]
425    pub fn import_batch(&self, bytes: &[Vec<u8>]) -> LoroResult<ImportStatus> {
426        self.doc.import_batch(bytes)
427    }
428
429    /// Get a [Container] by container id.
430    #[inline]
431    pub fn get_container(&self, id: ContainerID) -> Option<Container> {
432        self.doc.get_handler(id).map(Container::from_handler)
433    }
434
435    /// Get a [LoroMovableList] by container id.
436    ///
437    /// If the provided id is string, it will be converted into a root container id with the name of the string.
438    #[inline]
439    pub fn get_movable_list<I: IntoContainerId>(&self, id: I) -> LoroMovableList {
440        LoroMovableList {
441            handler: self.doc.get_movable_list(id),
442        }
443    }
444
445    /// Get a [LoroList] by container id.
446    ///
447    /// If the provided id is string, it will be converted into a root container id with the name of the string.
448    /// Note: creating/accessing a root container does not record history; creating nested
449    /// containers (e.g., `Map::insert_container`) does.
450    #[inline]
451    pub fn get_list<I: IntoContainerId>(&self, id: I) -> LoroList {
452        LoroList {
453            handler: self.doc.get_list(id),
454        }
455    }
456
457    /// Get a [LoroMap] by container id.
458    ///
459    /// If the provided id is string, it will be converted into a root container id with the name of the string.
460    /// Note: creating/accessing a root container does not record history; creating nested
461    /// containers (e.g., `Map::insert_container`) does.
462    #[inline]
463    pub fn get_map<I: IntoContainerId>(&self, id: I) -> LoroMap {
464        LoroMap {
465            handler: self.doc.get_map(id),
466        }
467    }
468
469    /// Get a [LoroText] by container id.
470    ///
471    /// If the provided id is string, it will be converted into a root container id with the name of the string.
472    /// Note: creating/accessing a root container does not record history; creating nested
473    /// containers (e.g., `Map::insert_container`) does.
474    #[inline]
475    pub fn get_text<I: IntoContainerId>(&self, id: I) -> LoroText {
476        LoroText {
477            handler: self.doc.get_text(id),
478        }
479    }
480
481    /// Get a [LoroTree] by container id.
482    ///
483    /// If the provided id is string, it will be converted into a root container id with the name of the string.
484    /// Note: creating/accessing a root container does not record history; creating nested
485    /// containers (e.g., `Map::insert_container`) does.
486    #[inline]
487    pub fn get_tree<I: IntoContainerId>(&self, id: I) -> LoroTree {
488        LoroTree {
489            handler: self.doc.get_tree(id),
490        }
491    }
492
493    #[cfg(feature = "counter")]
494    /// Get a [LoroCounter] by container id.
495    ///
496    /// If the provided id is string, it will be converted into a root container id with the name of the string.
497    #[inline]
498    pub fn get_counter<I: IntoContainerId>(&self, id: I) -> LoroCounter {
499        LoroCounter {
500            handler: self.doc.get_counter(id),
501        }
502    }
503
504    /// Commit the cumulative auto commit transaction.
505    ///
506    /// There is a transaction behind every operation.
507    /// The events will be emitted after a transaction is committed. A transaction is committed when:
508    ///
509    /// - `doc.commit()` is called.
510    /// - `doc.export(mode)` is called.
511    /// - `doc.import(data)` is called.
512    /// - `doc.checkout(version)` is called.
513    ///
514    /// Note: Loro transactions are not ACID database transactions. There is no rollback or
515    /// isolation; they are a grouping mechanism for events/history. For interactive undo/redo,
516    /// use [`UndoManager`].
517    ///
518    /// Empty-commit behavior: this method is an explicit commit. If the pending
519    /// transaction is empty, any previously set next-commit options (message/timestamp/origin)
520    /// are swallowed and will not carry over.
521    #[inline]
522    pub fn commit(&self) {
523        self.doc.commit_then_renew();
524    }
525
526    /// Commit the cumulative auto commit transaction with custom options.
527    ///
528    /// There is a transaction behind every operation.
529    /// It will automatically commit when users invoke export or import.
530    /// The event will be sent after a transaction is committed
531    ///
532    /// See also: [`set_next_commit_message`], [`set_next_commit_origin`],
533    /// [`set_next_commit_timestamp`]. Commit messages are persisted and replicate to peers;
534    /// origins are local-only metadata.
535    ///
536    /// Empty-commit behavior: this method is an explicit commit. If the pending
537    /// transaction is empty, the provided options are swallowed and will not carry over.
538    /// For implicit commits triggered by `export`/`checkout` (commit barriers),
539    /// message/timestamp/origin from an empty transaction are preserved for the next commit.
540    #[inline]
541    pub fn commit_with(&self, options: CommitOptions) {
542        self.doc.commit_with(options);
543    }
544
545    /// Set commit message for the current uncommitted changes
546    ///
547    /// It will be persisted.
548    pub fn set_next_commit_message(&self, msg: &str) {
549        self.doc.set_next_commit_message(msg)
550    }
551
552    /// Set `origin` for the current uncommitted changes, it can be used to track the source of changes in an event.
553    ///
554    /// It will NOT be persisted.
555    pub fn set_next_commit_origin(&self, origin: &str) {
556        self.doc.set_next_commit_origin(origin)
557    }
558
559    /// Set the timestamp of the next commit.
560    ///
561    /// It will be persisted and stored in the `OpLog`.
562    /// You can get the timestamp from the [`Change`] type.
563    pub fn set_next_commit_timestamp(&self, timestamp: Timestamp) {
564        self.doc.set_next_commit_timestamp(timestamp)
565    }
566
567    /// Set the options of the next commit.
568    ///
569    /// It will be used when the next commit is performed.
570    ///
571    /// # Example
572    /// ```
573    /// use loro::{LoroDoc, CommitOptions};
574    /// let doc = LoroDoc::new();
575    /// doc.set_next_commit_options(CommitOptions::new().origin("ui").commit_msg("tagged"));
576    /// doc.get_text("t").insert(0, "x").unwrap();
577    /// doc.commit();
578    /// ```
579    pub fn set_next_commit_options(&self, options: CommitOptions) {
580        self.doc.set_next_commit_options(options);
581    }
582
583    /// Clear the options of the next commit.
584    pub fn clear_next_commit_options(&self) {
585        self.doc.clear_next_commit_options();
586    }
587
588    /// Whether the document is in detached mode, where the [loro_internal::DocState] is not
589    /// synchronized with the latest version of the [loro_internal::OpLog].
590    #[inline]
591    pub fn is_detached(&self) -> bool {
592        self.doc.is_detached()
593    }
594
595    /// Create a new `LoroDoc` from a snapshot.
596    ///
597    /// The snapshot is created via [`LoroDoc::export`] with [`ExportMode::Snapshot`].
598    ///
599    /// # Example
600    /// ```
601    /// use loro::{LoroDoc, ExportMode};
602    ///
603    /// let doc = LoroDoc::new();
604    /// let text = doc.get_text("text");
605    /// text.insert(0, "Hello").unwrap();
606    /// let snapshot = doc.export(ExportMode::Snapshot).unwrap();
607    ///
608    /// let restored = LoroDoc::from_snapshot(&snapshot).unwrap();
609    /// assert_eq!(restored.get_deep_value(), doc.get_deep_value());
610    /// ```
611    pub fn from_snapshot(bytes: &[u8]) -> LoroResult<Self> {
612        let inner = InnerLoroDoc::from_snapshot(bytes)?;
613        inner.start_auto_commit();
614        Ok(Self::_new(inner))
615    }
616
617    /// Import data exported by [`LoroDoc::export`].
618    ///
619    /// Use [`ExportMode::Snapshot`] for full-state snapshots, or
620    /// [`ExportMode::all_updates`] / [`ExportMode::updates`] for updates.
621    ///
622    /// # Example
623    /// ```
624    /// use loro::{LoroDoc, ExportMode};
625    ///
626    /// let a = LoroDoc::new();
627    /// a.get_text("text").insert(0, "Hello").unwrap();
628    /// let updates = a.export(ExportMode::all_updates()).unwrap();
629    ///
630    /// let b = LoroDoc::new();
631    /// b.import(&updates).unwrap();
632    /// assert_eq!(a.get_deep_value(), b.get_deep_value());
633    /// ```
634    /// Pitfalls:
635    /// - Missing dependencies: check the returned [`ImportStatus`]. If `pending` is non-empty,
636    ///   fetch those missing ranges (e.g., using `export(ExportMode::updates(&doc.oplog_vv()))`) and re-import.
637    /// - Auto-commit: `import` finalizes the current transaction before applying incoming data.
638    #[inline]
639    pub fn import(&self, bytes: &[u8]) -> Result<ImportStatus, LoroError> {
640        self.doc.import_with(bytes, "".into())
641    }
642
643    /// Import data exported by [`LoroDoc::export`] and mark it with a custom origin.
644    ///
645    /// The `origin` string will be attached to the ensuing change event, which is handy
646    /// for telemetry or filtering.
647    /// Pitfalls:
648    /// - Same as [`import`]: verify `ImportStatus.pending` and fetch dependencies if needed.
649    #[inline]
650    pub fn import_with(&self, bytes: &[u8], origin: &str) -> Result<ImportStatus, LoroError> {
651        self.doc.import_with(bytes, origin.into())
652    }
653
654    /// Import the json schema updates.
655    ///
656    /// # Example
657    /// ```
658    /// use loro::{LoroDoc, VersionVector};
659    /// let a = LoroDoc::new();
660    /// a.get_text("t").insert(0, "hi").unwrap();
661    /// a.commit();
662    /// let json = a.export_json_updates(&VersionVector::default(), &a.oplog_vv());
663    ///
664    /// let b = LoroDoc::new();
665    /// b.import_json_updates(json).unwrap();
666    /// assert_eq!(a.get_deep_value(), b.get_deep_value());
667    /// ```
668    #[inline]
669    pub fn import_json_updates<T: TryInto<JsonSchema>>(
670        &self,
671        json: T,
672    ) -> Result<ImportStatus, LoroError> {
673        self.doc.import_json_updates(json)
674    }
675
676    /// Export the current state with json-string format of the document.
677    ///
678    /// # Example
679    /// ```
680    /// use loro::{LoroDoc, VersionVector};
681    /// let doc = LoroDoc::new();
682    /// let start = VersionVector::default();
683    /// let end = doc.oplog_vv();
684    /// let json = doc.export_json_updates(&start, &end);
685    /// ```
686    #[inline]
687    pub fn export_json_updates(
688        &self,
689        start_vv: &VersionVector,
690        end_vv: &VersionVector,
691    ) -> JsonSchema {
692        self.doc.export_json_updates(start_vv, end_vv, true)
693    }
694
695    /// Export the current state with json-string format of the document, without peer compression.
696    ///
697    /// Compared to [`export_json_updates`], this method does not compress the peer IDs in the updates.
698    /// So the operations are easier to be processed by application code.
699    ///
700    /// # Example
701    /// ```
702    /// use loro::{LoroDoc, VersionVector};
703    /// let doc = LoroDoc::new();
704    /// let start = VersionVector::default();
705    /// let end = doc.oplog_vv();
706    /// let json = doc.export_json_updates_without_peer_compression(&start, &end);
707    /// ```
708    #[inline]
709    pub fn export_json_updates_without_peer_compression(
710        &self,
711        start_vv: &VersionVector,
712        end_vv: &VersionVector,
713    ) -> JsonSchema {
714        self.doc.export_json_updates(start_vv, end_vv, false)
715    }
716
717    /// Exports changes within the specified ID span to JSON schema format.
718    ///
719    /// The JSON schema format produced by this method is identical to the one generated by `export_json_updates`.
720    /// It ensures deterministic output, making it ideal for hash calculations and integrity checks.
721    ///
722    /// This method can also export pending changes from the uncommitted transaction that have not yet been applied to the OpLog.
723    ///
724    /// This method will NOT trigger a new commit implicitly.
725    ///
726    /// # Example
727    /// ```
728    /// use loro::{LoroDoc, IdSpan};
729    ///
730    /// let doc = LoroDoc::new();
731    /// doc.set_peer_id(0).unwrap();
732    /// doc.get_text("text").insert(0, "a").unwrap();
733    /// doc.commit();
734    /// let doc_clone = doc.clone();
735    /// let _sub = doc.subscribe_pre_commit(Box::new(move |e| {
736    ///     let changes = doc_clone.export_json_in_id_span(IdSpan::new(
737    ///         0,
738    ///         0,
739    ///         e.change_meta.id.counter + e.change_meta.len as i32,
740    ///     ));
741    ///     // 2 because commit one and the uncommit one
742    ///     assert_eq!(changes.len(), 2);
743    ///     true
744    /// }));
745    /// doc.get_text("text").insert(0, "b").unwrap();
746    /// let changes = doc.export_json_in_id_span(IdSpan::new(0, 0, 2));
747    /// assert_eq!(changes.len(), 1);
748    /// doc.commit();
749    /// // change merged
750    /// assert_eq!(changes.len(), 1);
751    /// ```
752    pub fn export_json_in_id_span(&self, id_span: IdSpan) -> Vec<JsonChange> {
753        self.doc.export_json_in_id_span(id_span)
754    }
755
756    /// Convert `Frontiers` into `VersionVector`
757    ///
758    /// Returns `None` if the frontiers are not included by this doc's OpLog.
759    ///
760    /// # Example
761    /// ```
762    /// use loro::LoroDoc;
763    /// let doc = LoroDoc::new();
764    /// let f = doc.state_frontiers();
765    /// let vv = doc.frontiers_to_vv(&f);
766    /// assert!(vv.is_some());
767    /// ```
768    #[inline]
769    pub fn frontiers_to_vv(&self, frontiers: &Frontiers) -> Option<VersionVector> {
770        self.doc.frontiers_to_vv(frontiers)
771    }
772
773    /// Minimize the frontiers by removing the unnecessary entries.
774    ///
775    /// Returns `Err(ID)` if any frontier is not included by this doc's history.
776    ///
777    /// # Example
778    /// ```
779    /// use loro::LoroDoc;
780    /// let doc = LoroDoc::new();
781    /// let f = doc.state_frontiers();
782    /// let _minimized = doc.minimize_frontiers(&f).unwrap();
783    /// ```
784    pub fn minimize_frontiers(&self, frontiers: &Frontiers) -> Result<Frontiers, ID> {
785        self.with_oplog(|oplog| shrink_frontiers(frontiers, oplog.dag()))
786    }
787
788    /// Convert `VersionVector` into `Frontiers`
789    #[inline]
790    pub fn vv_to_frontiers(&self, vv: &VersionVector) -> Frontiers {
791        self.doc.vv_to_frontiers(vv)
792    }
793
794    /// Access the `OpLog`.
795    ///
796    /// NOTE: The API in `OpLog` is unstable. Keep the closure short; avoid calling methods
797    /// that might re-enter the document while holding the lock.
798    #[inline]
799    pub fn with_oplog<R>(&self, f: impl FnOnce(&OpLog) -> R) -> R {
800        let oplog = self.doc.oplog().lock().unwrap();
801        f(&oplog)
802    }
803
804    /// Access the `DocState`.
805    ///
806    /// NOTE: The API in `DocState` is unstable. Keep the closure short; avoid calling methods
807    /// that might re-enter the document while holding the lock.
808    #[inline]
809    pub fn with_state<R>(&self, f: impl FnOnce(&mut DocState) -> R) -> R {
810        let mut state = self.doc.app_state().lock().unwrap();
811        f(&mut state)
812    }
813
814    /// Get the `VersionVector` version of `OpLog`
815    #[inline]
816    pub fn oplog_vv(&self) -> VersionVector {
817        self.doc.oplog_vv()
818    }
819
820    /// Get the `VersionVector` version of `DocState`
821    #[inline]
822    pub fn state_vv(&self) -> VersionVector {
823        self.doc.state_vv()
824    }
825
826    /// The doc only contains the history since this version
827    ///
828    /// This is empty if the doc is not shallow.
829    ///
830    /// The ops included by the shallow history start version vector are not in the doc.
831    #[inline]
832    pub fn shallow_since_vv(&self) -> ImVersionVector {
833        self.doc.shallow_since_vv()
834    }
835
836    /// The doc only contains the history since this version
837    ///
838    /// This is empty if the doc is not shallow.
839    ///
840    /// The ops included by the shallow history start frontiers are not in the doc.
841    #[inline]
842    pub fn shallow_since_frontiers(&self) -> Frontiers {
843        self.doc.shallow_since_frontiers()
844    }
845
846    /// Get the total number of operations in the `OpLog`
847    #[inline]
848    pub fn len_ops(&self) -> usize {
849        self.doc.len_ops()
850    }
851
852    /// Get the total number of changes in the `OpLog`
853    #[inline]
854    pub fn len_changes(&self) -> usize {
855        self.doc.len_changes()
856    }
857
858    /// Get the shallow value of the document.
859    #[inline]
860    pub fn get_value(&self) -> LoroValue {
861        self.doc.get_value()
862    }
863
864    /// Get the entire state of the current DocState
865    #[inline]
866    pub fn get_deep_value(&self) -> LoroValue {
867        self.doc.get_deep_value()
868    }
869
870    /// Get the entire state of the current DocState with container id
871    pub fn get_deep_value_with_id(&self) -> LoroValue {
872        self.doc
873            .app_state()
874            .lock()
875            .unwrap()
876            .get_deep_value_with_id()
877    }
878
879    /// Get the `Frontiers` version of `OpLog`.
880    #[inline]
881    pub fn oplog_frontiers(&self) -> Frontiers {
882        self.doc.oplog_frontiers()
883    }
884
885    /// Get the `Frontiers` version of `DocState`.
886    ///
887    /// When detached or during checkout, `state_frontiers()` may differ from `oplog_frontiers()`.
888    /// Learn more about [`Frontiers`](https://loro.dev/docs/advanced/version_deep_dive).
889    ///
890    /// # Example
891    /// ```
892    /// use loro::LoroDoc;
893    /// let doc = LoroDoc::new();
894    /// let before = doc.state_frontiers();
895    /// doc.get_text("t").insert(0, "x").unwrap();
896    /// let after = doc.state_frontiers();
897    /// assert_ne!(before, after);
898    /// ```
899    #[inline]
900    pub fn state_frontiers(&self) -> Frontiers {
901        self.doc.state_frontiers()
902    }
903
904    /// Get the PeerID
905    #[inline]
906    pub fn peer_id(&self) -> PeerID {
907        self.doc.peer_id()
908    }
909
910    /// Change the PeerID
911    ///
912    /// Pitfalls:
913    /// - Never reuse the same PeerID across concurrent writers (multiple tabs/devices). Duplicate
914    ///   PeerIDs can produce conflicting OpIDs and corrupt the document.
915    /// - Do not assign a fixed PeerID to a user or device without strict single-ownership locking.
916    ///   Prefer the default random PeerID per process/session.
917    #[inline]
918    pub fn set_peer_id(&self, peer: PeerID) -> LoroResult<()> {
919        self.doc.set_peer_id(peer)
920    }
921
922    /// Subscribe the events of a container.
923    ///
924    /// The callback will be invoked after a transaction that change the container.
925    /// Returns a subscription that can be used to unsubscribe.
926    ///
927    /// The events will be emitted after a transaction is committed. A transaction is committed when:
928    ///
929    /// - `doc.commit()` is called.
930    /// - `doc.export(mode)` is called.
931    /// - `doc.import(data)` is called.
932    /// - `doc.checkout(version)` is called.
933    ///
934    /// # Example
935    ///
936    /// ```
937    /// # use loro::LoroDoc;
938    /// # use loro::ContainerTrait;
939    /// # use std::sync::{atomic::AtomicBool, Arc};
940    /// # use loro::{event::DiffEvent, LoroResult, TextDelta};
941    /// #
942    /// let doc = LoroDoc::new();
943    /// let text = doc.get_text("text");
944    /// let ran = Arc::new(AtomicBool::new(false));
945    /// let ran2 = ran.clone();
946    /// let sub = doc.subscribe(
947    ///     &text.id(),
948    ///     Arc::new(move |event| {
949    ///         assert!(event.triggered_by.is_local());
950    ///         for event in event.events {
951    ///             let delta = event.diff.as_text().unwrap();
952    ///             let d = TextDelta::Insert {
953    ///                 insert: "123".into(),
954    ///                 attributes: Default::default(),
955    ///             };
956    ///             assert_eq!(delta, &vec![d]);
957    ///             ran2.store(true, std::sync::atomic::Ordering::Relaxed);
958    ///         }
959    ///     }),
960    /// );
961    /// text.insert(0, "123").unwrap();
962    /// doc.commit();
963    /// assert!(ran.load(std::sync::atomic::Ordering::Relaxed));
964    /// // unsubscribe
965    /// sub.unsubscribe();
966    /// ```
967    #[inline]
968    pub fn subscribe(&self, container_id: &ContainerID, callback: Subscriber) -> Subscription {
969        self.doc.subscribe(
970            container_id,
971            Arc::new(move |e| {
972                callback(DiffEvent::from(e));
973            }),
974        )
975    }
976
977    /// Subscribe all the events.
978    ///
979    /// The callback will be invoked when any part of the [loro_internal::DocState] is changed.
980    /// Returns a subscription that can be used to unsubscribe.
981    ///
982    /// The events will be emitted after a transaction is committed. A transaction is committed when:
983    ///
984    /// - `doc.commit()` is called.
985    /// - `doc.export(mode)` is called.
986    /// - `doc.import(data)` is called.
987    /// - `doc.checkout(version)` is called.
988    #[inline]
989    pub fn subscribe_root(&self, callback: Subscriber) -> Subscription {
990        // self.doc.subscribe_root(callback)
991        self.doc.subscribe_root(Arc::new(move |e| {
992            callback(DiffEvent::from(e));
993        }))
994    }
995
996    /// Subscribe to local document updates.
997    ///
998    /// The callback receives encoded update bytes whenever local changes are committed.
999    /// This is useful for syncing changes to other document instances or persisting updates.
1000    ///
1001    /// **Auto-unsubscription**: If the callback returns `false`, the subscription will be
1002    /// automatically removed, providing a convenient way to implement one-time or conditional
1003    /// subscriptions in Rust.
1004    ///
1005    /// # Parameters
1006    /// - `callback`: Function that receives `&Vec<u8>` (encoded updates) and returns `bool`
1007    ///   - Return `true` to keep the subscription active
1008    ///   - Return `false` to automatically unsubscribe
1009    ///
1010    /// # Example
1011    /// ```rust
1012    /// use loro::LoroDoc;
1013    /// use std::sync::{Arc, Mutex};
1014    ///
1015    /// let doc = LoroDoc::new();
1016    /// let updates = Arc::new(Mutex::new(Vec::new()));
1017    /// let updates_clone = updates.clone();
1018    /// let count = Arc::new(Mutex::new(0));
1019    /// let count_clone = count.clone();
1020    ///
1021    /// // Subscribe and collect first 3 updates, then auto-unsubscribe
1022    /// let sub = doc.subscribe_local_update(Box::new(move |bytes| {
1023    ///     updates_clone.lock().unwrap().push(bytes.clone());
1024    ///     let mut c = count_clone.lock().unwrap();
1025    ///     *c += 1;
1026    ///     *c < 3  // Auto-unsubscribe after 3 updates
1027    /// }));
1028    ///
1029    /// doc.get_text("text").insert(0, "hello").unwrap();
1030    /// doc.commit();
1031    /// ```
1032    pub fn subscribe_local_update(&self, callback: LocalUpdateCallback) -> Subscription {
1033        self.doc.subscribe_local_update(callback)
1034    }
1035
1036    /// Subscribe to peer ID changes in the document.
1037    ///
1038    /// The callback is triggered whenever the document's peer ID is modified.
1039    /// This is useful for tracking identity changes and updating related state accordingly.
1040    ///
1041    /// **Auto-unsubscription**: If the callback returns `false`, the subscription will be
1042    /// automatically removed, providing a convenient way to implement one-time or conditional
1043    /// subscriptions in Rust.
1044    ///
1045    /// # Parameters
1046    /// - `callback`: Function that receives `&ID` (the new peer ID) and returns `bool`
1047    ///   - Return `true` to keep the subscription active
1048    ///   - Return `false` to automatically unsubscribe
1049    ///
1050    /// # Example
1051    /// ```rust
1052    /// use loro::LoroDoc;
1053    /// use std::sync::{Arc, Mutex};
1054    ///
1055    /// let doc = LoroDoc::new();
1056    /// let peer_changes = Arc::new(Mutex::new(Vec::new()));
1057    /// let changes_clone = peer_changes.clone();
1058    ///
1059    /// let sub = doc.subscribe_peer_id_change(Box::new(move |new_peer_id| {
1060    ///     changes_clone.lock().unwrap().push(*new_peer_id);
1061    ///     true  // Keep subscription active
1062    /// }));
1063    ///
1064    /// doc.set_peer_id(42).unwrap();
1065    /// doc.set_peer_id(100).unwrap();
1066    /// ```
1067    pub fn subscribe_peer_id_change(&self, callback: PeerIdUpdateCallback) -> Subscription {
1068        self.doc.subscribe_peer_id_change(callback)
1069    }
1070
1071    /// Check the correctness of the document state by comparing it with the state
1072    /// calculated by applying all the history.
1073    #[inline]
1074    pub fn check_state_correctness_slow(&self) {
1075        self.doc.check_state_diff_calc_consistency_slow()
1076    }
1077
1078    /// Get the handler by the path.
1079    #[inline]
1080    pub fn get_by_path(&self, path: &[Index]) -> Option<ValueOrContainer> {
1081        self.doc.get_by_path(path).map(ValueOrContainer::from)
1082    }
1083
1084    /// Get the handler by the string path.
1085    ///
1086    /// The path can be specified in different ways depending on the container type:
1087    ///
1088    /// For Tree:
1089    /// 1. Using node IDs: `tree/{node_id}/property`
1090    /// 2. Using indices: `tree/0/1/property`
1091    ///
1092    /// For List and MovableList:
1093    /// - Using indices: `list/0` or `list/1/property`
1094    ///
1095    /// For Map:
1096    /// - Using keys: `map/key` or `map/nested/property`
1097    ///
1098    /// For tree structures, index-based paths follow depth-first traversal order.
1099    /// The indices start from 0 and represent the position of a node among its siblings.
1100    ///
1101    /// # Examples
1102    /// ```
1103    /// # use loro::{LoroDoc, LoroValue};
1104    /// let doc = LoroDoc::new();
1105    ///
1106    /// // Tree example
1107    /// let tree = doc.get_tree("tree");
1108    /// let root = tree.create(None).unwrap();
1109    /// tree.get_meta(root).unwrap().insert("name", "root").unwrap();
1110    /// // Access tree by ID or index
1111    /// let name1 = doc.get_by_str_path(&format!("tree/{}/name", root)).unwrap().into_value().unwrap();
1112    /// let name2 = doc.get_by_str_path("tree/0/name").unwrap().into_value().unwrap();
1113    /// assert_eq!(name1, name2);
1114    ///
1115    /// // List example
1116    /// let list = doc.get_list("list");
1117    /// list.insert(0, "first").unwrap();
1118    /// list.insert(1, "second").unwrap();
1119    /// // Access list by index
1120    /// let item = doc.get_by_str_path("list/0");
1121    /// assert_eq!(item.unwrap().into_value().unwrap().into_string().unwrap(), "first".into());
1122    ///
1123    /// // Map example
1124    /// let map = doc.get_map("map");
1125    /// map.insert("key", "value").unwrap();
1126    /// // Access map by key
1127    /// let value = doc.get_by_str_path("map/key");
1128    /// assert_eq!(value.unwrap().into_value().unwrap().into_string().unwrap(), "value".into());
1129    ///
1130    /// // MovableList example
1131    /// let mlist = doc.get_movable_list("mlist");
1132    /// mlist.insert(0, "item").unwrap();
1133    /// // Access movable list by index
1134    /// let item = doc.get_by_str_path("mlist/0");
1135    /// assert_eq!(item.unwrap().into_value().unwrap().into_string().unwrap(), "item".into());
1136    /// ```
1137    #[inline]
1138    pub fn get_by_str_path(&self, path: &str) -> Option<ValueOrContainer> {
1139        self.doc.get_by_str_path(path).map(ValueOrContainer::from)
1140    }
1141
1142    /// Get the absolute position of the given cursor.
1143    ///
1144    /// # Example
1145    ///
1146    /// ```
1147    /// # use loro::{LoroDoc, ToJson};
1148    /// let doc = LoroDoc::new();
1149    /// let text = &doc.get_text("text");
1150    /// text.insert(0, "01234").unwrap();
1151    /// let pos = text.get_cursor(5, Default::default()).unwrap();
1152    /// assert_eq!(doc.get_cursor_pos(&pos).unwrap().current.pos, 5);
1153    /// text.insert(0, "01234").unwrap();
1154    /// assert_eq!(doc.get_cursor_pos(&pos).unwrap().current.pos, 10);
1155    /// text.delete(0, 10).unwrap();
1156    /// assert_eq!(doc.get_cursor_pos(&pos).unwrap().current.pos, 0);
1157    /// text.insert(0, "01234").unwrap();
1158    /// assert_eq!(doc.get_cursor_pos(&pos).unwrap().current.pos, 5);
1159    /// ```
1160    #[inline]
1161    pub fn get_cursor_pos(
1162        &self,
1163        cursor: &Cursor,
1164    ) -> Result<PosQueryResult, CannotFindRelativePosition> {
1165        self.doc.query_pos(cursor)
1166    }
1167
1168    /// Get the inner LoroDoc ref.
1169    #[inline]
1170    pub fn inner(&self) -> &InnerLoroDoc {
1171        &self.doc
1172    }
1173
1174    /// Whether the history cache is built.
1175    #[inline]
1176    pub fn has_history_cache(&self) -> bool {
1177        self.doc.has_history_cache()
1178    }
1179
1180    /// Free the history cache that is used for making checkout faster.
1181    ///
1182    /// If you use checkout that switching to an old/concurrent version, the history cache will be built.
1183    /// You can free it by calling this method.
1184    #[inline]
1185    pub fn free_history_cache(&self) {
1186        self.doc.free_history_cache()
1187    }
1188
1189    /// Free the cached diff calculator that is used for checkout.
1190    #[inline]
1191    pub fn free_diff_calculator(&self) {
1192        self.doc.free_diff_calculator()
1193    }
1194
1195    /// Encoded all ops and history cache to bytes and store them in the kv store.
1196    ///
1197    /// This will free up the memory that used by parsed ops
1198    #[inline]
1199    pub fn compact_change_store(&self) {
1200        self.doc.compact_change_store()
1201    }
1202
1203    /// Export the document in the given mode.
1204    ///
1205    /// Common modes:
1206    /// - [`ExportMode::Snapshot`]: full state + history
1207    /// - [`ExportMode::all_updates()`]: all known ops
1208    /// - [`ExportMode::updates(&VersionVector)`]: ops since a specific version
1209    /// - [`ExportMode::shallow_snapshot(..)`]: GC’d snapshot starting at frontiers
1210    /// - [`ExportMode::updates_in_range(..)`]: ops in specific ID spans
1211    ///
1212    /// Important notes:
1213    /// - Auto-commit: `export` finalizes the current transaction before producing bytes.
1214    /// - Shallow snapshots: peers cannot import updates from before the shallow start.
1215    /// - Performance: exporting fresh snapshots periodically can reduce import time for new peers.
1216    ///
1217    /// # Examples
1218    /// ```
1219    /// use loro::{ExportMode, LoroDoc};
1220    ///
1221    /// let doc = LoroDoc::new();
1222    /// doc.get_text("text").insert(0, "Hello").unwrap();
1223    ///
1224    /// // 1) Full snapshot
1225    /// let snapshot = doc.export(ExportMode::Snapshot).unwrap();
1226    ///
1227    /// // 2) All updates
1228    /// let all = doc.export(ExportMode::all_updates()).unwrap();
1229    ///
1230    /// // 3) Updates from another peer’s version vector
1231    /// let vv = doc.oplog_vv();
1232    /// let delta = doc.export(ExportMode::updates(&vv)).unwrap();
1233    /// assert!(!delta.is_empty());
1234    /// ```
1235    pub fn export(&self, mode: ExportMode) -> Result<Vec<u8>, LoroEncodeError> {
1236        self.doc.export(mode)
1237    }
1238
1239    /// Analyze the container info of the doc
1240    ///
1241    /// This is used for development and debugging. It can be slow.
1242    pub fn analyze(&self) -> DocAnalysis {
1243        self.doc.analyze()
1244    }
1245
1246    /// Get the path from the root to the container
1247    pub fn get_path_to_container(&self, id: &ContainerID) -> Option<Vec<(ContainerID, Index)>> {
1248        self.doc.get_path_to_container(id)
1249    }
1250
1251    /// Evaluate a JSONPath expression on the document and return matching values or handlers.
1252    ///
1253    /// This method allows querying the document structure using JSONPath syntax.
1254    /// It returns a vector of `ValueOrHandler` which can represent either primitive values
1255    /// or container handlers, depending on what the JSONPath expression matches.
1256    ///
1257    /// # Arguments
1258    ///
1259    /// * `path` - A string slice containing the JSONPath expression to evaluate.
1260    ///
1261    /// # Returns
1262    ///
1263    /// A `Result` containing either:
1264    /// - `Ok(Vec<ValueOrHandler>)`: A vector of matching values or handlers.
1265    /// - `Err(String)`: An error message if the JSONPath expression is invalid or evaluation fails.
1266    ///
1267    /// # Example
1268    ///
1269    /// ```
1270    /// # use loro::{LoroDoc, ToJson};
1271    ///
1272    /// let doc = LoroDoc::new();
1273    /// let map = doc.get_map("users");
1274    /// map.insert("alice", 30).unwrap();
1275    /// map.insert("bob", 25).unwrap();
1276    ///
1277    /// let result = doc.jsonpath("$.users.alice").unwrap();
1278    /// assert_eq!(result.len(), 1);
1279    /// assert_eq!(result[0].as_value().unwrap().to_json_value(), serde_json::json!(30));
1280    /// ```
1281    #[inline]
1282    #[cfg(feature = "jsonpath")]
1283    pub fn jsonpath(&self, path: &str) -> Result<Vec<ValueOrContainer>, JsonPathError> {
1284        self.doc
1285            .jsonpath(path)
1286            .map(|vec| vec.into_iter().map(ValueOrContainer::from).collect())
1287    }
1288
1289    /// Subscribe to updates that might affect the given JSONPath query.
1290    ///
1291    /// The callback:
1292    /// - may fire **false positives** (never false negatives) to stay lightweight;
1293    /// - does **not** include the query result so the caller can debounce/throttle
1294    ///   and run JSONPath themselves if desired;
1295    /// - can be debounced/throttled before executing an expensive JSONPath read.
1296    #[cfg(feature = "jsonpath")]
1297    pub fn subscribe_jsonpath(
1298        &self,
1299        jsonpath: &str,
1300        callback: SubscribeJsonPathCallback,
1301    ) -> LoroResult<Subscription> {
1302        self.doc.subscribe_jsonpath(jsonpath, callback)
1303    }
1304
1305    /// Get the number of operations in the pending transaction.
1306    ///
1307    /// The pending transaction is the one that is not committed yet. It will be committed
1308    /// after calling `doc.commit()`, `doc.export(mode)` or `doc.checkout(version)`.
1309    pub fn get_pending_txn_len(&self) -> usize {
1310        self.doc.get_pending_txn_len()
1311    }
1312
1313    /// Traverses the ancestors of the Change containing the given ID, including itself.
1314    ///
1315    /// This method visits all ancestors in causal order, from the latest to the oldest,
1316    /// based on their Lamport timestamps.
1317    ///
1318    /// # Arguments
1319    ///
1320    /// * `ids` - The IDs of the Change to start the traversal from.
1321    /// * `f` - A mutable function that is called for each ancestor. It can return `ControlFlow::Break(())` to stop the traversal.
1322    pub fn travel_change_ancestors(
1323        &self,
1324        ids: &[ID],
1325        f: &mut dyn FnMut(ChangeMeta) -> ControlFlow<()>,
1326    ) -> Result<(), ChangeTravelError> {
1327        self.doc.travel_change_ancestors(ids, f)
1328    }
1329
1330    /// Check if the doc contains the full history.
1331    pub fn is_shallow(&self) -> bool {
1332        self.doc.is_shallow()
1333    }
1334
1335    /// Gets container IDs modified in the given ID range.
1336    ///
1337    /// Pitfalls:
1338    /// - This method will implicitly commit the current transaction to ensure the change range is finalized.
1339    ///
1340    /// This method can be used in conjunction with `doc.travel_change_ancestors()` to traverse
1341    /// the history and identify all changes that affected specific containers.
1342    ///
1343    /// # Arguments
1344    ///
1345    /// * `id` - The starting ID of the change range
1346    /// * `len` - The length of the change range to check
1347    pub fn get_changed_containers_in(&self, id: ID, len: usize) -> FxHashSet<ContainerID> {
1348        self.doc.get_changed_containers_in(id, len)
1349    }
1350
1351    /// Find the operation id spans that between the `from` version and the `to` version.
1352    ///
1353    /// Useful for exporting just the changes in a range, e.g., in response to a subscription.
1354    ///
1355    /// # Example
1356    /// ```
1357    /// use loro::LoroDoc;
1358    /// let doc = LoroDoc::new();
1359    /// let a = doc.state_frontiers();
1360    /// doc.get_text("t").insert(0, "x").unwrap();
1361    /// doc.commit();
1362    /// let b = doc.state_frontiers();
1363    /// let spans = doc.find_id_spans_between(&a, &b);
1364    /// assert!(!spans.forward.is_empty());
1365    /// ```
1366    #[inline]
1367    pub fn find_id_spans_between(&self, from: &Frontiers, to: &Frontiers) -> VersionVectorDiff {
1368        self.doc.find_id_spans_between(from, to)
1369    }
1370
1371    /// Revert the current document state back to the target version
1372    ///
1373    /// Internally, it will generate a series of local operations that can revert the
1374    /// current doc to the target version. It will calculate the diff between the current
1375    /// state and the target state, and apply the diff to the current state.
1376    ///
1377    /// Pitfalls:
1378    /// - The target frontiers must be included by the document's history. If the document
1379    ///   is shallow and the target is before the shallow start, revert will fail.
1380    ///
1381    /// # Example
1382    /// ```
1383    /// use loro::LoroDoc;
1384    /// let doc = LoroDoc::new();
1385    /// let t = doc.get_text("text");
1386    /// t.insert(0, "Hello").unwrap();
1387    /// let v0 = doc.state_frontiers();
1388    /// t.insert(5, ", world").unwrap();
1389    /// doc.commit();
1390    /// doc.revert_to(&v0).unwrap();
1391    /// assert_eq!(t.to_string(), "Hello");
1392    /// ```
1393    #[inline]
1394    pub fn revert_to(&self, version: &Frontiers) -> LoroResult<()> {
1395        self.doc.revert_to(version)
1396    }
1397
1398    /// Apply a diff to the current document state.
1399    ///
1400    /// Internally, it will apply the diff to the current state.
1401    #[inline]
1402    pub fn apply_diff(&self, diff: DiffBatch) -> LoroResult<()> {
1403        self.doc.apply_diff(diff.into())
1404    }
1405
1406    /// Calculate the diff between two versions.
1407    ///
1408    /// # Example
1409    /// ```
1410    /// use loro::{LoroDoc};
1411    /// let doc = LoroDoc::new();
1412    /// let t = doc.get_text("text");
1413    /// let a = doc.state_frontiers();
1414    /// t.insert(0, "a").unwrap();
1415    /// let b = doc.state_frontiers();
1416    /// let diff = doc.diff(&a, &b).unwrap();
1417    /// assert!(diff.iter().next().is_some());
1418    /// ```
1419    #[inline]
1420    pub fn diff(&self, a: &Frontiers, b: &Frontiers) -> LoroResult<DiffBatch> {
1421        self.doc.diff(a, b).map(|x| x.into())
1422    }
1423
1424    /// Check if the doc contains the target container.
1425    ///
1426    /// A root container always exists, while a normal container exists
1427    /// if it has ever been created on the doc.
1428    ///
1429    /// # Examples
1430    /// ```
1431    /// use loro::{LoroDoc, LoroText, LoroList, ExportMode};
1432    ///
1433    /// let doc = LoroDoc::new();
1434    /// doc.set_peer_id(1);
1435    /// let map = doc.get_map("map");
1436    /// map.insert_container("text", LoroText::new()).unwrap();
1437    /// map.insert_container("list", LoroList::new()).unwrap();
1438    ///
1439    /// // Root map container exists
1440    /// assert!(doc.has_container(&"cid:root-map:Map".try_into().unwrap()));
1441    /// // Text container exists
1442    /// assert!(doc.has_container(&"cid:0@1:Text".try_into().unwrap()));
1443    /// // List container exists
1444    /// assert!(doc.has_container(&"cid:1@1:List".try_into().unwrap()));
1445    ///
1446    /// let doc2 = LoroDoc::new();
1447    /// // Containers exist as long as the history or doc state includes them
1448    /// doc.detach();
1449    /// doc2.import(&doc.export(ExportMode::all_updates()).unwrap()).unwrap();
1450    /// assert!(doc2.has_container(&"cid:root-map:Map".try_into().unwrap()));
1451    /// assert!(doc2.has_container(&"cid:0@1:Text".try_into().unwrap()));
1452    /// assert!(doc2.has_container(&"cid:1@1:List".try_into().unwrap()));
1453    /// ```
1454    pub fn has_container(&self, container_id: &ContainerID) -> bool {
1455        self.doc.has_container(container_id)
1456    }
1457
1458    /// Subscribe to the first commit from a peer. Operations performed on the `LoroDoc` within this callback
1459    /// will be merged into the current commit.
1460    ///
1461    /// Subscribe to the first commit event from each peer.
1462    ///
1463    /// The callback is triggered only once per peer when they make their first commit to the document locally.
1464    /// This is particularly useful for managing peer-to-user mappings or initialization logic.
1465    ///
1466    /// **Auto-unsubscription**: If the callback returns `false`, the subscription will be
1467    /// automatically removed, providing a convenient way to implement one-time or conditional
1468    /// subscriptions in Rust.
1469    ///
1470    /// # Parameters
1471    /// - `callback`: Function that receives `&FirstCommitFromPeerPayload` and returns `bool`
1472    ///   - Return `true` to keep the subscription active
1473    ///   - Return `false` to automatically unsubscribe
1474    ///
1475    /// # Use Cases
1476    /// - Initialize peer-specific data structures
1477    /// - Map peer IDs to user information
1478    ///
1479    /// # Example
1480    /// ```rust
1481    /// use loro::LoroDoc;
1482    /// use std::sync::{Arc, Mutex};
1483    ///
1484    /// let doc = LoroDoc::new();
1485    /// doc.set_peer_id(0).unwrap();
1486    ///
1487    /// let new_peers = Arc::new(Mutex::new(Vec::new()));
1488    /// let peers_clone = new_peers.clone();
1489    /// let peer_count = Arc::new(Mutex::new(0));
1490    /// let count_clone = peer_count.clone();
1491    ///
1492    /// // Track first 5 new peers, then auto-unsubscribe
1493    /// let sub = doc.subscribe_first_commit_from_peer(Box::new(move |payload| {
1494    ///     peers_clone.lock().unwrap().push(payload.peer);
1495    ///     let mut count = count_clone.lock().unwrap();
1496    ///     *count += 1;
1497    ///     *count < 5  // Auto-unsubscribe after tracking 5 peers
1498    /// }));
1499    ///
1500    /// // This will trigger the callback for peer 0
1501    /// doc.get_text("text").insert(0, "hello").unwrap();
1502    /// doc.commit();
1503    ///
1504    /// // Switch to a new peer and commit - triggers callback again
1505    /// doc.set_peer_id(1).unwrap();
1506    /// doc.get_text("text").insert(0, "world").unwrap();
1507    /// doc.commit();
1508    /// ```
1509    pub fn subscribe_first_commit_from_peer(
1510        &self,
1511        callback: FirstCommitFromPeerCallback,
1512    ) -> Subscription {
1513        self.doc.subscribe_first_commit_from_peer(callback)
1514    }
1515
1516    /// Subscribe to pre-commit events.
1517    ///
1518    /// The callback is triggered when changes are about to be committed but before they're
1519    /// applied to the OpLog. This allows you to modify commit metadata such as timestamps
1520    /// and messages, or perform validation before changes are finalized.
1521    ///
1522    /// **Auto-unsubscription**: If the callback returns `false`, the subscription will be
1523    /// automatically removed, providing a convenient way to implement one-time or conditional
1524    /// subscriptions in Rust.
1525    ///
1526    /// Pitfall: `commit()` can be triggered implicitly by `import`, `export`, and `checkout`.
1527    /// This hook still runs for those commits, which is helpful for annotating metadata
1528    /// even for implicit commits.
1529    ///
1530    /// # Parameters
1531    /// - `callback`: Function that receives `&PreCommitCallbackPayload` and returns `bool`
1532    ///   - Return `true` to keep the subscription active
1533    ///   - Return `false` to automatically unsubscribe
1534    /// - The payload contains:
1535    ///   - `change_meta`: Metadata about the commit
1536    ///   - `modifier`: Interface to modify commit properties
1537    ///
1538    /// # Use Cases
1539    /// - Add commit message prefixes or formatting
1540    /// - Adjust timestamps for consistent ordering
1541    /// - Log or audit commit operations
1542    /// - Implement commit validation or approval workflows
1543    ///
1544    /// # Example
1545    /// ```rust
1546    /// use loro::LoroDoc;
1547    /// use std::sync::{Arc, Mutex};
1548    ///
1549    /// let doc = LoroDoc::new();
1550    /// let commit_count = Arc::new(Mutex::new(0));
1551    /// let count_clone = commit_count.clone();
1552    ///
1553    /// // Add timestamps and auto-unsubscribe after 5 commits
1554    /// let sub = doc.subscribe_pre_commit(Box::new(move |payload| {
1555    ///     // Add a prefix to commit messages
1556    ///     let new_message = format!("Auto: {}", payload.change_meta.message());
1557    ///     payload.modifier.set_message(&new_message);
1558    ///
1559    ///     let mut count = count_clone.lock().unwrap();
1560    ///     *count += 1;
1561    ///     *count < 5  // Auto-unsubscribe after 5 commits
1562    /// }));
1563    ///
1564    /// doc.get_text("text").insert(0, "hello").unwrap();
1565    /// doc.commit();
1566    /// ```
1567    pub fn subscribe_pre_commit(&self, callback: PreCommitCallback) -> Subscription {
1568        self.doc.subscribe_pre_commit(callback)
1569    }
1570
1571    /// Delete all content from a root container and hide it from the document.
1572    ///
1573    /// When a root container is empty and hidden:
1574    /// - It won't show up in `get_deep_value()` results
1575    /// - It won't be included in document snapshots
1576    ///
1577    /// Only works on root containers (containers without parents).
1578    pub fn delete_root_container(&self, cid: ContainerID) {
1579        self.doc.delete_root_container(cid);
1580    }
1581
1582    /// Set whether to hide empty root containers.
1583    ///
1584    /// # Example
1585    /// ```
1586    /// use loro::LoroDoc;
1587    ///
1588    /// let doc = LoroDoc::new();
1589    /// let map = doc.get_map("map");
1590    /// dbg!(doc.get_deep_value()); // {"map": {}}
1591    /// doc.set_hide_empty_root_containers(true);
1592    /// dbg!(doc.get_deep_value()); // {}
1593    /// ```
1594    pub fn set_hide_empty_root_containers(&self, hide: bool) {
1595        self.doc.set_hide_empty_root_containers(hide);
1596    }
1597}
1598
1599/// It's used to prevent the user from implementing the trait directly.
1600#[allow(private_bounds)]
1601trait SealedTrait {}
1602
1603/// The common trait for all the containers.
1604/// It's used internally, you can't implement it directly.
1605#[allow(private_bounds)]
1606pub trait ContainerTrait: SealedTrait {
1607    /// The handler of the container.
1608    type Handler: HandlerTrait;
1609    /// Get the ID of the container.
1610    fn id(&self) -> ContainerID;
1611    /// Convert the container to a [Container].
1612    fn to_container(&self) -> Container;
1613    /// Convert the container to a handler.
1614    fn to_handler(&self) -> Self::Handler;
1615    /// Convert the handler to a container.
1616    fn from_handler(handler: Self::Handler) -> Self;
1617    /// Try to convert the container to the handler.
1618    fn try_from_container(container: Container) -> Option<Self>
1619    where
1620        Self: Sized;
1621    /// Whether the container is attached to a document.
1622    fn is_attached(&self) -> bool;
1623    /// If a detached container is attached, this method will return its corresponding attached handler.
1624    fn get_attached(&self) -> Option<Self>
1625    where
1626        Self: Sized;
1627    /// Whether the container is deleted.
1628    fn is_deleted(&self) -> bool;
1629    /// Get the doc of the container.
1630    fn doc(&self) -> Option<LoroDoc>;
1631    /// Subscribe to the container.
1632    ///
1633    /// If the Container is detached, this method will return `None`.
1634    fn subscribe(&self, callback: Subscriber) -> Option<Subscription> {
1635        self.doc().map(|doc| doc.subscribe(&self.id(), callback))
1636    }
1637}
1638
1639/// LoroList container. It's used to model arrays.
1640///
1641/// It can have sub containers.
1642///
1643/// Important: choose the right structure.
1644/// - Use `LoroList` for ordered collections where elements are appended/inserted/deleted.
1645/// - Use `LoroMovableList` when frequent reordering (drag-and-drop) is needed.
1646/// - Use `LoroMap` for keyed records and coordinates.
1647///
1648/// ```no_run
1649/// // Bad: coordinates in a list can diverge under concurrency
1650/// // let coord = doc.get_list("coord");
1651/// // coord.insert(0, 10).unwrap(); // x
1652/// // coord.insert(1, 20).unwrap(); // y
1653///
1654/// // Good: use a map for labeled fields
1655/// // let coord = doc.get_map("coord");
1656/// // coord.insert("x", 10).unwrap();
1657/// // coord.insert("y", 20).unwrap();
1658/// ```
1659///
1660/// ```
1661/// # use loro::{LoroDoc, ContainerType, ToJson};
1662/// # use serde_json::json;
1663/// let doc = LoroDoc::new();
1664/// let list = doc.get_list("list");
1665/// list.insert(0, 123).unwrap();
1666/// list.insert(1, "h").unwrap();
1667/// assert_eq!(
1668///     doc.get_deep_value().to_json_value(),
1669///     json!({
1670///         "list": [123, "h"]
1671///     })
1672/// );
1673/// ```
1674#[derive(Clone, Debug)]
1675pub struct LoroList {
1676    handler: InnerListHandler,
1677}
1678
1679impl SealedTrait for LoroList {}
1680impl ContainerTrait for LoroList {
1681    type Handler = InnerListHandler;
1682
1683    fn id(&self) -> ContainerID {
1684        self.handler.id()
1685    }
1686
1687    fn to_container(&self) -> Container {
1688        Container::List(self.clone())
1689    }
1690
1691    fn to_handler(&self) -> Self::Handler {
1692        self.handler.clone()
1693    }
1694
1695    fn from_handler(handler: Self::Handler) -> Self {
1696        Self { handler }
1697    }
1698
1699    fn is_attached(&self) -> bool {
1700        self.handler.is_attached()
1701    }
1702
1703    fn get_attached(&self) -> Option<Self> {
1704        self.handler.get_attached().map(Self::from_handler)
1705    }
1706
1707    fn try_from_container(container: Container) -> Option<Self> {
1708        container.into_list().ok()
1709    }
1710
1711    fn is_deleted(&self) -> bool {
1712        self.handler.is_deleted()
1713    }
1714
1715    fn doc(&self) -> Option<LoroDoc> {
1716        self.handler.doc().map(|doc| {
1717            doc.start_auto_commit();
1718            LoroDoc::_new(doc)
1719        })
1720    }
1721}
1722
1723impl LoroList {
1724    /// Create a new container that is detached from the document.
1725    ///
1726    /// The edits on a detached container will not be persisted.
1727    /// To attach the container to the document, please insert it into an attached container.
1728    pub fn new() -> Self {
1729        Self {
1730            handler: InnerListHandler::new_detached(),
1731        }
1732    }
1733
1734    /// Whether the container is attached to a document
1735    ///
1736    /// The edits on a detached container will not be persisted.
1737    /// To attach the container to the document, please insert it into an attached container.
1738    pub fn is_attached(&self) -> bool {
1739        self.handler.is_attached()
1740    }
1741
1742    /// Insert a value at the given position.
1743    pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
1744        self.handler.insert(pos, v)
1745    }
1746
1747    /// Delete values at the given position.
1748    #[inline]
1749    pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
1750        self.handler.delete(pos, len)
1751    }
1752
1753    /// Get the value at the given position.
1754    #[inline]
1755    pub fn get(&self, index: usize) -> Option<ValueOrContainer> {
1756        self.handler.get_(index).map(ValueOrContainer::from)
1757    }
1758
1759    /// Get the deep value of the container.
1760    #[inline]
1761    pub fn get_deep_value(&self) -> LoroValue {
1762        self.handler.get_deep_value()
1763    }
1764
1765    /// Get the shallow value of the container.
1766    ///
1767    /// This does not convert the state of sub-containers; instead, it represents them as [LoroValue::Container].
1768    #[inline]
1769    pub fn get_value(&self) -> LoroValue {
1770        self.handler.get_value()
1771    }
1772
1773    /// Pop the last element of the list.
1774    #[inline]
1775    pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
1776        self.handler.pop()
1777    }
1778
1779    /// Push a value to the list.
1780    #[inline]
1781    pub fn push(&self, v: impl Into<LoroValue>) -> LoroResult<()> {
1782        self.handler.push(v.into())
1783    }
1784
1785    /// Push a container to the list.
1786    #[inline]
1787    pub fn push_container<C: ContainerTrait>(&self, child: C) -> LoroResult<C> {
1788        let pos = self.handler.len();
1789        Ok(C::from_handler(
1790            self.handler.insert_container(pos, child.to_handler())?,
1791        ))
1792    }
1793
1794    /// Iterate over the elements of the list.
1795    pub fn for_each<I>(&self, mut f: I)
1796    where
1797        I: FnMut(ValueOrContainer),
1798    {
1799        self.handler.for_each(&mut |v| {
1800            f(ValueOrContainer::from(v));
1801        })
1802    }
1803
1804    /// Get the length of the list.
1805    #[inline]
1806    pub fn len(&self) -> usize {
1807        self.handler.len()
1808    }
1809
1810    /// Whether the list is empty.
1811    #[inline]
1812    pub fn is_empty(&self) -> bool {
1813        self.handler.is_empty()
1814    }
1815
1816    /// Insert a container with the given type at the given index.
1817    ///
1818    /// # Example
1819    ///
1820    /// ```
1821    /// # use loro::{LoroDoc, ContainerType, LoroText, ToJson};
1822    /// # use serde_json::json;
1823    /// let doc = LoroDoc::new();
1824    /// let list = doc.get_list("m");
1825    /// let text = list.insert_container(0, LoroText::new()).unwrap();
1826    /// text.insert(0, "12");
1827    /// text.insert(0, "0");
1828    /// assert_eq!(doc.get_deep_value().to_json_value(), json!({"m": ["012"]}));
1829    /// ```
1830    #[inline]
1831    pub fn insert_container<C: ContainerTrait>(&self, pos: usize, child: C) -> LoroResult<C> {
1832        Ok(C::from_handler(
1833            self.handler.insert_container(pos, child.to_handler())?,
1834        ))
1835    }
1836
1837    /// Get the cursor at the given position.
1838    ///
1839    /// Using "index" to denote cursor positions can be unstable, as positions may
1840    /// shift with document edits. To reliably represent a position or range within
1841    /// a document, it is more effective to leverage the unique ID of each item/character
1842    /// in a List CRDT or Text CRDT.
1843    ///
1844    /// Loro optimizes State metadata by not storing the IDs of deleted elements. This
1845    /// approach complicates tracking cursors since they rely on these IDs. The solution
1846    /// recalculates position by replaying relevant history to update stable positions
1847    /// accurately. To minimize the performance impact of history replay, the system
1848    /// updates cursor info to reference only the IDs of currently present elements,
1849    /// thereby reducing the need for replay.
1850    ///
1851    /// # Example
1852    ///
1853    /// ```
1854    /// use loro::LoroDoc;
1855    /// use loro_internal::cursor::Side;
1856    ///
1857    /// let doc = LoroDoc::new();
1858    /// let list = doc.get_list("list");
1859    /// list.insert(0, 0).unwrap();
1860    /// let cursor = list.get_cursor(0, Side::Middle).unwrap();
1861    /// assert_eq!(doc.get_cursor_pos(&cursor).unwrap().current.pos, 0);
1862    /// list.insert(0, 0).unwrap();
1863    /// assert_eq!(doc.get_cursor_pos(&cursor).unwrap().current.pos, 1);
1864    /// list.insert(0, 0).unwrap();
1865    /// list.insert(0, 0).unwrap();
1866    /// assert_eq!(doc.get_cursor_pos(&cursor).unwrap().current.pos, 3);
1867    /// list.insert(4, 0).unwrap();
1868    /// assert_eq!(doc.get_cursor_pos(&cursor).unwrap().current.pos, 3);
1869    /// ```
1870    pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
1871        self.handler.get_cursor(pos, side)
1872    }
1873
1874    /// Converts the LoroList to a Vec of LoroValue.
1875    ///
1876    /// This method unwraps the internal Arc and clones the data if necessary,
1877    /// returning a Vec containing all the elements of the LoroList as LoroValue.
1878    ///
1879    /// # Returns
1880    ///
1881    /// A Vec<LoroValue> containing all elements of the LoroList.
1882    ///
1883    /// # Example
1884    ///
1885    /// ```
1886    /// use loro::{LoroDoc, LoroValue};
1887    ///
1888    /// let doc = LoroDoc::new();
1889    /// let list = doc.get_list("my_list");
1890    /// list.insert(0, 1).unwrap();
1891    /// list.insert(1, "hello").unwrap();
1892    /// list.insert(2, true).unwrap();
1893    ///
1894    /// let vec = list.to_vec();
1895    /// ```
1896    pub fn to_vec(&self) -> Vec<LoroValue> {
1897        self.get_value().into_list().unwrap().unwrap()
1898    }
1899
1900    /// Delete all elements in the list.
1901    pub fn clear(&self) -> LoroResult<()> {
1902        self.handler.clear()
1903    }
1904
1905    /// Get the ID of the list item at the given position.
1906    pub fn get_id_at(&self, pos: usize) -> Option<ID> {
1907        self.handler.get_id_at(pos)
1908    }
1909}
1910
1911impl Default for LoroList {
1912    fn default() -> Self {
1913        Self::new()
1914    }
1915}
1916
1917/// LoroMap container.
1918///
1919/// It's LWW(Last-Write-Win) Map. It can support Multi-Value Map in the future.
1920///
1921/// # Example
1922/// ```
1923/// # use loro::{LoroDoc, ToJson, ExpandType, LoroText, LoroValue};
1924/// # use serde_json::json;
1925/// let doc = LoroDoc::new();
1926/// let map = doc.get_map("map");
1927/// map.insert("key", "value").unwrap();
1928/// map.insert("true", true).unwrap();
1929/// map.insert("null", LoroValue::Null).unwrap();
1930/// map.insert("deleted", LoroValue::Null).unwrap();
1931/// map.delete("deleted").unwrap();
1932/// let text = map
1933///    .insert_container("text", LoroText::new()).unwrap();
1934/// text.insert(0, "Hello world!").unwrap();
1935/// assert_eq!(
1936///     doc.get_deep_value().to_json_value(),
1937///     json!({
1938///        "map": {
1939///            "key": "value",
1940///            "true": true,
1941///            "null": null,
1942///            "text": "Hello world!"
1943///        }
1944///    })
1945/// );
1946/// ```
1947#[derive(Clone, Debug)]
1948pub struct LoroMap {
1949    handler: InnerMapHandler,
1950}
1951
1952impl SealedTrait for LoroMap {}
1953impl ContainerTrait for LoroMap {
1954    type Handler = InnerMapHandler;
1955
1956    fn id(&self) -> ContainerID {
1957        self.handler.id()
1958    }
1959
1960    fn to_container(&self) -> Container {
1961        Container::Map(self.clone())
1962    }
1963
1964    fn to_handler(&self) -> Self::Handler {
1965        self.handler.clone()
1966    }
1967
1968    fn from_handler(handler: Self::Handler) -> Self {
1969        Self { handler }
1970    }
1971
1972    fn is_attached(&self) -> bool {
1973        self.handler.is_attached()
1974    }
1975
1976    fn get_attached(&self) -> Option<Self> {
1977        self.handler.get_attached().map(Self::from_handler)
1978    }
1979
1980    fn try_from_container(container: Container) -> Option<Self> {
1981        container.into_map().ok()
1982    }
1983
1984    fn is_deleted(&self) -> bool {
1985        self.handler.is_deleted()
1986    }
1987
1988    fn doc(&self) -> Option<LoroDoc> {
1989        self.handler.doc().map(|doc| {
1990            doc.start_auto_commit();
1991            LoroDoc::_new(doc)
1992        })
1993    }
1994}
1995
1996impl LoroMap {
1997    /// Create a new container that is detached from the document.
1998    ///
1999    /// The edits on a detached container will not be persisted.
2000    /// To attach the container to the document, please insert it into an attached container.
2001    pub fn new() -> Self {
2002        Self {
2003            handler: InnerMapHandler::new_detached(),
2004        }
2005    }
2006
2007    /// Whether the container is attached to a document.
2008    pub fn is_attached(&self) -> bool {
2009        self.handler.is_attached()
2010    }
2011
2012    /// Delete a key-value pair from the map.
2013    pub fn delete(&self, key: &str) -> LoroResult<()> {
2014        self.handler.delete(key)
2015    }
2016
2017    /// Iterate over the key-value pairs of the map.
2018    pub fn for_each<I>(&self, mut f: I)
2019    where
2020        I: FnMut(&str, ValueOrContainer),
2021    {
2022        self.handler.for_each(|k, v| {
2023            f(k, ValueOrContainer::from(v));
2024        })
2025    }
2026
2027    /// Insert a key-value pair into the map.
2028    ///
2029    /// > **Note**: When calling `map.set(key, value)` on a LoroMap, if `map.get(key)` already returns `value`,
2030    /// > the operation will be a no-op (no operation recorded) to avoid unnecessary updates.
2031    pub fn insert(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
2032        self.handler.insert(key, value)
2033    }
2034
2035    /// Get the length of the map.
2036    pub fn len(&self) -> usize {
2037        self.handler.len()
2038    }
2039
2040    /// Whether the map is empty.
2041    pub fn is_empty(&self) -> bool {
2042        self.handler.is_empty()
2043    }
2044
2045    /// Get the value of the map with the given key.
2046    pub fn get(&self, key: &str) -> Option<ValueOrContainer> {
2047        self.handler.get_(key).map(ValueOrContainer::from)
2048    }
2049
2050    /// Insert a container with the given type at the given key.
2051    ///
2052    /// # Example
2053    ///
2054    /// ```
2055    /// # use loro::{LoroDoc, LoroText, ContainerType, ToJson};
2056    /// # use serde_json::json;
2057    /// let doc = LoroDoc::new();
2058    /// let map = doc.get_map("m");
2059    /// let text = map.insert_container("t", LoroText::new()).unwrap();
2060    /// text.insert(0, "12");
2061    /// text.insert(0, "0");
2062    /// assert_eq!(doc.get_deep_value().to_json_value(), json!({"m": {"t": "012"}}));
2063    /// ```
2064    ///
2065    /// Pitfalls:
2066    /// - Concurrently inserting different containers at the same map key on different peers
2067    ///   can result in one overwriting the other rather than merging. Prefer initializing
2068    ///   heavy/primary child containers when initializing the map.
2069    pub fn insert_container<C: ContainerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
2070        Ok(C::from_handler(
2071            self.handler.insert_container(key, child.to_handler())?,
2072        ))
2073    }
2074
2075    /// Get the shallow value of the map.
2076    ///
2077    /// It will not convert the state of sub-containers, but represent them as [LoroValue::Container].
2078    pub fn get_value(&self) -> LoroValue {
2079        self.handler.get_value()
2080    }
2081
2082    /// Get the deep value of the map.
2083    ///
2084    /// It will convert the state of sub-containers into a nested JSON value.
2085    pub fn get_deep_value(&self) -> LoroValue {
2086        self.handler.get_deep_value()
2087    }
2088
2089    /// Get or create a container with the given key.
2090    ///
2091    /// Pitfalls:
2092    /// - If other peers concurrently create a different container at the same key, their state
2093    ///   may be overwritten. See the note in [`insert_container`].
2094    pub fn get_or_create_container<C: ContainerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
2095        Ok(C::from_handler(
2096            self.handler
2097                .get_or_create_container(key, child.to_handler())?,
2098        ))
2099    }
2100
2101    /// Delete all key-value pairs in the map.
2102    pub fn clear(&self) -> LoroResult<()> {
2103        self.handler.clear()
2104    }
2105
2106    /// Get the keys of the map.
2107    pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
2108        self.handler.keys()
2109    }
2110
2111    /// Get the values of the map.
2112    pub fn values(&self) -> impl Iterator<Item = ValueOrContainer> + '_ {
2113        self.handler.values().map(ValueOrContainer::from)
2114    }
2115
2116    /// Get the peer id of the last editor on the given entry
2117    pub fn get_last_editor(&self, key: &str) -> Option<PeerID> {
2118        self.handler.get_last_editor(key)
2119    }
2120}
2121
2122impl Default for LoroMap {
2123    fn default() -> Self {
2124        Self::new()
2125    }
2126}
2127
2128/// LoroText container. It's used to model plaintext/richtext.
2129///
2130/// Indexing and lengths:
2131/// - Rust APIs default to Unicode scalar positions for `insert`/`delete` and `slice`.
2132/// - For byte-based integration, use `insert_utf8`/`delete_utf8`.
2133/// - For UTF-16 code unit offsets (e.g., integrating with JavaScript), use
2134///   `insert_utf16`/`delete_utf16`/`slice_utf16` and related helpers.
2135/// - You can inspect `len_unicode`, `len_utf8`, and `len_utf16` depending on your needs.
2136///
2137/// # Example (emoji)
2138/// ```
2139/// use loro::LoroDoc;
2140/// let doc = LoroDoc::new();
2141/// let text = doc.get_text("text");
2142/// text.insert(0, "Hello 😀 World").unwrap();
2143/// assert_eq!(text.len_unicode(), 13); // visible characters
2144/// assert!(text.len_utf16() >= text.len_unicode()); // emoji may count as 2 in UTF-16
2145/// // Delete the emoji safely by Unicode indices
2146/// let start = 6; // after "Hello "
2147/// text.delete(start, 1).unwrap();
2148/// assert_eq!(text.to_string(), "Hello  World");
2149/// ```
2150#[derive(Clone, Debug)]
2151pub struct LoroText {
2152    handler: InnerTextHandler,
2153}
2154
2155impl SealedTrait for LoroText {}
2156impl ContainerTrait for LoroText {
2157    type Handler = InnerTextHandler;
2158
2159    fn id(&self) -> ContainerID {
2160        self.handler.id()
2161    }
2162
2163    fn to_container(&self) -> Container {
2164        Container::Text(self.clone())
2165    }
2166
2167    fn to_handler(&self) -> Self::Handler {
2168        self.handler.clone()
2169    }
2170
2171    fn from_handler(handler: Self::Handler) -> Self {
2172        Self { handler }
2173    }
2174
2175    fn is_attached(&self) -> bool {
2176        self.handler.is_attached()
2177    }
2178
2179    fn get_attached(&self) -> Option<Self> {
2180        self.handler.get_attached().map(Self::from_handler)
2181    }
2182
2183    fn try_from_container(container: Container) -> Option<Self> {
2184        container.into_text().ok()
2185    }
2186
2187    fn is_deleted(&self) -> bool {
2188        self.handler.is_deleted()
2189    }
2190
2191    fn doc(&self) -> Option<LoroDoc> {
2192        self.handler.doc().map(|doc| {
2193            doc.start_auto_commit();
2194            LoroDoc::_new(doc)
2195        })
2196    }
2197}
2198
2199impl LoroText {
2200    /// Create a new container that is detached from the document.
2201    ///
2202    /// The edits on a detached container will not be persisted.
2203    /// To attach the container to the document, please insert it into an attached container.
2204    pub fn new() -> Self {
2205        Self {
2206            handler: InnerTextHandler::new_detached(),
2207        }
2208    }
2209
2210    /// Whether the container is attached to a document
2211    ///
2212    /// The edits on a detached container will not be persisted.
2213    /// To attach the container to the document, please insert it into an attached container.
2214    pub fn is_attached(&self) -> bool {
2215        self.handler.is_attached()
2216    }
2217
2218    /// Iterate over contiguous text chunks.
2219    ///
2220    /// The callback function will be called for each contiguous text segment (internal span),
2221    /// not necessarily a single character. If you need per-character iteration, iterate the
2222    /// returned `&str` within the callback.
2223    /// If the callback returns `false`, the iteration will stop.
2224    ///
2225    /// Limitation: you cannot access or alter the doc state when iterating.
2226    /// If you need to access or alter the doc state, please use `to_string` instead.
2227    pub fn iter(&self, callback: impl FnMut(&str) -> bool) {
2228        self.handler.iter(callback);
2229    }
2230
2231    /// Insert a string at the given unicode position.
2232    pub fn insert(&self, pos: usize, s: &str) -> LoroResult<()> {
2233        self.handler.insert_unicode(pos, s)
2234    }
2235
2236    /// Insert a string at the given utf-8 position.
2237    pub fn insert_utf8(&self, pos: usize, s: &str) -> LoroResult<()> {
2238        self.handler.insert_utf8(pos, s)
2239    }
2240
2241    /// Insert a string at the given UTF-16 code unit position.
2242    ///
2243    /// This is useful when working with JavaScript or other UTF-16-based index systems.
2244    pub fn insert_utf16(&self, pos: usize, s: &str) -> LoroResult<()> {
2245        self.handler.insert_utf16(pos, s)
2246    }
2247
2248    /// Delete a range of text at the given unicode position with unicode length.
2249    pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
2250        self.handler.delete_unicode(pos, len)
2251    }
2252
2253    /// Delete a range of text at the given utf-8 position with utf-8 length.
2254    pub fn delete_utf8(&self, pos: usize, len: usize) -> LoroResult<()> {
2255        self.handler.delete_utf8(pos, len)
2256    }
2257
2258    /// Delete a range of text at the given UTF-16 code unit position with UTF-16 length.
2259    pub fn delete_utf16(&self, pos: usize, len: usize) -> LoroResult<()> {
2260        self.handler.delete_utf16(pos, len)
2261    }
2262
2263    /// Get a string slice at the given Unicode range
2264    pub fn slice(&self, start_index: usize, end_index: usize) -> LoroResult<String> {
2265        self.handler
2266            .slice(start_index, end_index, cursor::PosType::Unicode)
2267    }
2268
2269    /// Get a string slice using UTF-16 code unit offsets.
2270    pub fn slice_utf16(&self, start_index: usize, end_index: usize) -> LoroResult<String> {
2271        self.handler.slice_utf16(start_index, end_index)
2272    }
2273
2274    /// Get the rich-text delta within a range.
2275    ///
2276    /// The range is expressed in the coordinate system selected by `pos_type`:
2277    /// Unicode scalar indices (`PosType::Unicode`), UTF-8 bytes, UTF-16 units, etc.
2278    /// The returned [`TextDelta`] segments contain only inserts and preserve the
2279    /// attributes active inside the slice.
2280    ///
2281    /// # Errors
2282    /// Returns [`LoroError::OutOfBound`](crate::LoroError::OutOfBound) when the range
2283    /// exceeds the current text length in the chosen coordinate system, or
2284    /// [`LoroError::EndIndexLessThanStartIndex`](crate::LoroError::EndIndexLessThanStartIndex)
2285    /// if `end < start`.
2286    ///
2287    /// # Examples
2288    /// ```
2289    /// use loro::{LoroDoc, TextDelta};
2290    /// use loro::cursor::PosType;
2291    ///
2292    /// let doc = LoroDoc::new();
2293    /// let text = doc.get_text("text");
2294    /// text.insert(0, "A😀BC").unwrap();
2295    /// text.mark(0..2, "bold", true).unwrap(); // A and 😀
2296    ///
2297    /// let delta = text.slice_delta(1, 3, PosType::Unicode).unwrap();
2298    /// assert_eq!(delta.len(), 2);
2299    /// if let TextDelta::Insert { insert, attributes } = &delta[0] {
2300    ///     assert_eq!(insert, "😀");
2301    ///     assert_eq!(attributes.as_ref().unwrap().get("bold").unwrap(), &true.into());
2302    /// } else {
2303    ///     unreachable!();
2304    /// }
2305    /// if let TextDelta::Insert { insert, attributes } = &delta[1] {
2306    ///     assert_eq!(insert, "B");
2307    ///     assert!(attributes.is_none());
2308    /// } else {
2309    ///     unreachable!();
2310    /// }
2311    /// ```
2312    pub fn slice_delta(
2313        &self,
2314        start: usize,
2315        end: usize,
2316        pos_type: cursor::PosType,
2317    ) -> LoroResult<Vec<TextDelta>> {
2318        self.handler.slice_delta(start, end, pos_type)
2319    }
2320
2321    /// Get the characters at given unicode position.
2322    pub fn char_at(&self, pos: usize) -> LoroResult<char> {
2323        self.handler.char_at(pos, cursor::PosType::Unicode)
2324    }
2325
2326    /// Delete specified character and insert string at the same position at given unicode position.
2327    pub fn splice(&self, pos: usize, len: usize, s: &str) -> LoroResult<String> {
2328        self.handler.splice(pos, len, s, cursor::PosType::Unicode)
2329    }
2330
2331    /// Delete specified range and insert a string at the same UTF-16 position.
2332    pub fn splice_utf16(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
2333        self.handler.splice_utf16(pos, len, s)
2334    }
2335
2336    /// Whether the text container is empty.
2337    pub fn is_empty(&self) -> bool {
2338        self.handler.is_empty()
2339    }
2340
2341    /// Get the length of the text container in UTF-8.
2342    pub fn len_utf8(&self) -> usize {
2343        self.handler.len_utf8()
2344    }
2345
2346    /// Get the length of the text container in Unicode.
2347    pub fn len_unicode(&self) -> usize {
2348        self.handler.len_unicode()
2349    }
2350
2351    /// Get the length of the text container in UTF-16.
2352    pub fn len_utf16(&self) -> usize {
2353        self.handler.len_utf16()
2354    }
2355
2356    /// Update the current text based on the provided text.
2357    ///
2358    /// It will calculate the minimal difference and apply it to the current text.
2359    /// It uses Myers' diff algorithm to compute the optimal difference.
2360    ///
2361    /// This could take a long time for large texts (e.g. > 50_000 characters).
2362    /// In that case, you should use `updateByLine` instead.
2363    ///
2364    /// # Example
2365    /// ```rust
2366    /// use loro::LoroDoc;
2367    ///
2368    /// let doc = LoroDoc::new();
2369    /// let text = doc.get_text("text");
2370    /// text.insert(0, "Hello").unwrap();
2371    /// text.update("Hello World", Default::default()).unwrap();
2372    /// assert_eq!(text.to_string(), "Hello World");
2373    /// ```
2374    ///
2375    pub fn update(&self, text: &str, options: UpdateOptions) -> Result<(), UpdateTimeoutError> {
2376        self.handler.update(text, options)
2377    }
2378
2379    /// Update the current text based on the provided text.
2380    ///
2381    /// This update calculation is line-based, which will be more efficient but less precise.
2382    pub fn update_by_line(
2383        &self,
2384        text: &str,
2385        options: UpdateOptions,
2386    ) -> Result<(), UpdateTimeoutError> {
2387        self.handler.update_by_line(text, options)
2388    }
2389
2390    /// Apply a [delta](https://quilljs.com/docs/delta/) to the text container.
2391    pub fn apply_delta(&self, delta: &[TextDelta]) -> LoroResult<()> {
2392        self.handler.apply_delta(delta)
2393    }
2394
2395    /// Mark a range of text with a key-value pair.
2396    ///
2397    /// The range uses Unicode scalar indices; use [`mark_utf8`] for UTF-8 byte offsets.
2398    ///
2399    /// You can use it to create a highlight, make a range of text bold, or add a link to a range of text.
2400    ///
2401    /// You can specify the `expand` option to set the behavior when inserting text at the boundary of the range.
2402    ///
2403    /// - `after`(default): when inserting text right after the given range, the mark will be expanded to include the inserted text
2404    /// - `before`: when inserting text right before the given range, the mark will be expanded to include the inserted text
2405    /// - `none`: the mark will not be expanded to include the inserted text at the boundaries
2406    /// - `both`: when inserting text either right before or right after the given range, the mark will be expanded to include the inserted text
2407    ///
2408    /// *You should make sure that a key is always associated with the same expand type.*
2409    pub fn mark(
2410        &self,
2411        range: Range<usize>,
2412        key: &str,
2413        value: impl Into<LoroValue>,
2414    ) -> LoroResult<()> {
2415        self.handler.mark(
2416            range.start,
2417            range.end,
2418            key,
2419            value.into(),
2420            cursor::PosType::Unicode,
2421        )
2422    }
2423
2424    /// Mark a range of text with a key-value pair using UTF-8 byte offsets.
2425    pub fn mark_utf8(
2426        &self,
2427        range: Range<usize>,
2428        key: &str,
2429        value: impl Into<LoroValue>,
2430    ) -> LoroResult<()> {
2431        self.handler.mark(
2432            range.start,
2433            range.end,
2434            key,
2435            value.into(),
2436            cursor::PosType::Bytes,
2437        )
2438    }
2439
2440    /// Mark a range of text with a key-value pair using UTF-16 code unit offsets.
2441    pub fn mark_utf16(
2442        &self,
2443        range: Range<usize>,
2444        key: &str,
2445        value: impl Into<LoroValue>,
2446    ) -> LoroResult<()> {
2447        self.handler.mark(
2448            range.start,
2449            range.end,
2450            key,
2451            value.into(),
2452            cursor::PosType::Utf16,
2453        )
2454    }
2455
2456    /// Unmark a range of text with a key and a value.
2457    ///
2458    /// You can use it to remove highlights, bolds or links
2459    ///
2460    /// You can specify the `expand` option to set the behavior when inserting text at the boundary of the range.
2461    ///
2462    /// **Note: You should specify the same expand type as when you mark the text.**
2463    ///
2464    /// - `after`(default): when inserting text right after the given range, the mark will be expanded to include the inserted text
2465    /// - `before`: when inserting text right before the given range, the mark will be expanded to include the inserted text
2466    /// - `none`: the mark will not be expanded to include the inserted text at the boundaries
2467    /// - `both`: when inserting text either right before or right after the given range, the mark will be expanded to include the inserted text
2468    ///
2469    /// *You should make sure that a key is always associated with the same expand type.*
2470    ///
2471    /// Note: you cannot delete unmergeable annotations like comments by this method.
2472    pub fn unmark(&self, range: Range<usize>, key: &str) -> LoroResult<()> {
2473        self.handler
2474            .unmark(range.start, range.end, key, cursor::PosType::Unicode)
2475    }
2476
2477    /// Unmark a range of text with a key and a value using UTF-16 code unit offsets.
2478    pub fn unmark_utf16(&self, range: Range<usize>, key: &str) -> LoroResult<()> {
2479        self.handler
2480            .unmark(range.start, range.end, key, cursor::PosType::Utf16)
2481    }
2482
2483    /// Get the text in [Delta](https://quilljs.com/docs/delta/) format.
2484    ///
2485    /// # Example
2486    /// ```
2487    /// use loro::{LoroDoc, ToJson, ExpandType, TextDelta};
2488    /// use serde_json::json;
2489    /// use rustc_hash::FxHashMap;
2490    ///
2491    /// let doc = LoroDoc::new();
2492    /// let text = doc.get_text("text");
2493    /// text.insert(0, "Hello world!").unwrap();
2494    /// text.mark(0..5, "bold", true).unwrap();
2495    /// assert_eq!(
2496    ///     text.to_delta(),
2497    ///     vec![
2498    ///         TextDelta::Insert {
2499    ///             insert: "Hello".to_string(),
2500    ///             attributes: Some(FxHashMap::from_iter([("bold".to_string(), true.into())])),
2501    ///         },
2502    ///         TextDelta::Insert {
2503    ///             insert: " world!".to_string(),
2504    ///             attributes: None,
2505    ///         },
2506    ///     ]
2507    /// );
2508    /// text.unmark(3..5, "bold").unwrap();
2509    /// assert_eq!(
2510    ///     text.to_delta(),
2511    ///     vec![
2512    ///         TextDelta::Insert {
2513    ///             insert: "Hel".to_string(),
2514    ///             attributes: Some(FxHashMap::from_iter([("bold".to_string(), true.into())])),
2515    ///         },
2516    ///         TextDelta::Insert {
2517    ///             insert: "lo world!".to_string(),
2518    ///             attributes: None,
2519    ///         },
2520    ///     ]
2521    /// );
2522    /// ```
2523    pub fn to_delta(&self) -> Vec<TextDelta> {
2524        let delta = self.handler.get_richtext_value().into_list().unwrap();
2525        delta
2526            .iter()
2527            .map(|x| {
2528                let map = x.as_map().unwrap();
2529                let insert = map.get("insert").unwrap().as_string().unwrap().to_string();
2530                let attributes = map
2531                    .get("attributes")
2532                    .map(|v| v.as_map().unwrap().deref().clone());
2533                TextDelta::Insert { insert, attributes }
2534            })
2535            .collect()
2536    }
2537
2538    /// Get the rich text value in [Delta](https://quilljs.com/docs/delta/) format.
2539    ///
2540    /// # Example
2541    /// ```
2542    /// # use loro::{LoroDoc, ToJson, ExpandType, TextDelta};
2543    /// # use serde_json::json;
2544    ///
2545    /// let doc = LoroDoc::new();
2546    /// let text = doc.get_text("text");
2547    /// text.insert(0, "Hello world!").unwrap();
2548    /// text.mark(0..5, "bold", true).unwrap();
2549    /// assert_eq!(
2550    ///     text.get_richtext_value().to_json_value(),
2551    ///     json!([
2552    ///         { "insert": "Hello", "attributes": {"bold": true} },
2553    ///         { "insert": " world!" },
2554    ///     ])
2555    /// );
2556    /// text.unmark(3..5, "bold").unwrap();
2557    /// assert_eq!(
2558    ///     text.get_richtext_value().to_json_value(),
2559    ///     json!([
2560    ///         { "insert": "Hel", "attributes": {"bold": true} },
2561    ///         { "insert": "lo world!" },
2562    ///    ])
2563    /// );
2564    /// ```
2565    pub fn get_richtext_value(&self) -> LoroValue {
2566        self.handler.get_richtext_value()
2567    }
2568
2569    /// Get the text content of the text container.
2570    #[allow(clippy::inherent_to_string)]
2571    pub fn to_string(&self) -> String {
2572        self.handler.to_string()
2573    }
2574
2575    /// Get the cursor at the given position in the given Unicode position.
2576    ///
2577    /// Using "index" to denote cursor positions can be unstable, as positions may
2578    /// shift with document edits. To reliably represent a position or range within
2579    /// a document, it is more effective to leverage the unique ID of each item/character
2580    /// in a List CRDT or Text CRDT.
2581    ///
2582    /// Loro optimizes State metadata by not storing the IDs of deleted elements. This
2583    /// approach complicates tracking cursors since they rely on these IDs. The solution
2584    /// recalculates position by replaying relevant history to update stable positions
2585    /// accurately. To minimize the performance impact of history replay, the system
2586    /// updates cursor info to reference only the IDs of currently present elements,
2587    /// thereby reducing the need for replay.
2588    ///
2589    /// # Example
2590    ///
2591    /// ```
2592    /// # use loro::{LoroDoc, ToJson};
2593    /// let doc = LoroDoc::new();
2594    /// let text = &doc.get_text("text");
2595    /// text.insert(0, "01234").unwrap();
2596    /// let pos = text.get_cursor(5, Default::default()).unwrap();
2597    /// assert_eq!(doc.get_cursor_pos(&pos).unwrap().current.pos, 5);
2598    /// text.insert(0, "01234").unwrap();
2599    /// assert_eq!(doc.get_cursor_pos(&pos).unwrap().current.pos, 10);
2600    /// text.delete(0, 10).unwrap();
2601    /// assert_eq!(doc.get_cursor_pos(&pos).unwrap().current.pos, 0);
2602    /// text.insert(0, "01234").unwrap();
2603    /// assert_eq!(doc.get_cursor_pos(&pos).unwrap().current.pos, 5);
2604    /// ```
2605    pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
2606        self.handler.get_cursor(pos, side)
2607    }
2608
2609    /// Whether the text container is deleted.
2610    pub fn is_deleted(&self) -> bool {
2611        self.handler.is_deleted()
2612    }
2613
2614    /// Convert a position between coordinate systems (Unicode, UTF-16, UTF-8 bytes, Event).
2615    ///
2616    /// Returns `None` when the position is out of bounds or the conversion isn't supported.
2617    pub fn convert_pos(
2618        &self,
2619        index: usize,
2620        from: cursor::PosType,
2621        to: cursor::PosType,
2622    ) -> Option<usize> {
2623        self.handler.convert_pos(index, from, to)
2624    }
2625
2626    /// Push a string to the end of the text container.
2627    pub fn push_str(&self, s: &str) -> LoroResult<()> {
2628        self.handler.push_str(s)
2629    }
2630
2631    /// Get the editor of the text at the given position.
2632    ///
2633    /// Returns `None` if the position is out of bounds or attribution is unavailable.
2634    ///
2635    /// # Example
2636    /// ```
2637    /// use loro::LoroDoc;
2638    /// let doc = LoroDoc::new();
2639    /// let t = doc.get_text("t");
2640    /// t.insert(0, "hi").unwrap();
2641    /// let who = t.get_editor_at_unicode_pos(0);
2642    /// assert!(who.is_some());
2643    /// ```
2644    pub fn get_editor_at_unicode_pos(&self, pos: usize) -> Option<PeerID> {
2645        self.handler
2646            .get_cursor(pos, Side::Middle)
2647            .map(|x| x.id.unwrap().peer)
2648    }
2649}
2650
2651impl Default for LoroText {
2652    fn default() -> Self {
2653        Self::new()
2654    }
2655}
2656
2657/// LoroTree container. It's used to model movable trees.
2658///
2659/// You may use it to model directories, outline or other movable hierarchical data.
2660///
2661/// Learn more at https://loro.dev/docs/tutorial/tree
2662#[derive(Clone, Debug)]
2663pub struct LoroTree {
2664    handler: InnerTreeHandler,
2665}
2666
2667impl SealedTrait for LoroTree {}
2668impl ContainerTrait for LoroTree {
2669    type Handler = InnerTreeHandler;
2670
2671    fn id(&self) -> ContainerID {
2672        self.handler.id()
2673    }
2674
2675    fn to_container(&self) -> Container {
2676        Container::Tree(self.clone())
2677    }
2678
2679    fn to_handler(&self) -> Self::Handler {
2680        self.handler.clone()
2681    }
2682
2683    fn from_handler(handler: Self::Handler) -> Self {
2684        Self { handler }
2685    }
2686
2687    fn is_attached(&self) -> bool {
2688        self.handler.is_attached()
2689    }
2690
2691    fn get_attached(&self) -> Option<Self> {
2692        self.handler.get_attached().map(Self::from_handler)
2693    }
2694
2695    fn try_from_container(container: Container) -> Option<Self> {
2696        container.into_tree().ok()
2697    }
2698
2699    fn is_deleted(&self) -> bool {
2700        self.handler.is_deleted()
2701    }
2702    fn doc(&self) -> Option<LoroDoc> {
2703        self.handler.doc().map(|doc| {
2704            doc.start_auto_commit();
2705            LoroDoc::_new(doc)
2706        })
2707    }
2708}
2709
2710/// A tree node in the [LoroTree].
2711#[derive(Debug, Clone)]
2712pub struct TreeNode {
2713    /// ID of the tree node.
2714    pub id: TreeID,
2715    /// ID of the parent tree node.
2716    /// If the node is deleted this value is TreeParentId::Deleted.
2717    /// If you checkout to a version before the node is created, this value is TreeParentId::Unexist.
2718    pub parent: TreeParentId,
2719    /// Fraction index of the node
2720    pub fractional_index: FractionalIndex,
2721    /// The current index of the node in its parent's children list.
2722    pub index: usize,
2723}
2724
2725impl LoroTree {
2726    /// Create a new container that is detached from the document.
2727    ///
2728    /// The edits on a detached container will not be persisted.
2729    /// To attach the container to the document, please insert it into an attached container.
2730    pub fn new() -> Self {
2731        Self {
2732            handler: InnerTreeHandler::new_detached(),
2733        }
2734    }
2735
2736    /// Whether the container is attached to a document
2737    ///
2738    /// The edits on a detached container will not be persisted.
2739    /// To attach the container to the document, please insert it into an attached container.
2740    pub fn is_attached(&self) -> bool {
2741        self.handler.is_attached()
2742    }
2743
2744    /// Create a new tree node and return the [`TreeID`].
2745    ///
2746    /// If the `parent` is `None`, the created node is the root of a tree.
2747    /// Otherwise, the created node is a child of the parent tree node.
2748    ///
2749    /// # Example
2750    ///
2751    /// ```rust
2752    /// use loro::LoroDoc;
2753    ///
2754    /// let doc = LoroDoc::new();
2755    /// let tree = doc.get_tree("tree");
2756    /// // create a root
2757    /// let root = tree.create(None).unwrap();
2758    /// // create a new child
2759    /// let child = tree.create(root).unwrap();
2760    /// ```
2761    pub fn create<T: Into<TreeParentId>>(&self, parent: T) -> LoroResult<TreeID> {
2762        self.handler.create(parent.into())
2763    }
2764
2765    /// Get the root nodes of the forest.
2766    pub fn roots(&self) -> Vec<TreeID> {
2767        self.handler.roots()
2768    }
2769
2770    /// Create a new tree node at the given index and return the [`TreeID`].
2771    ///
2772    /// If the `parent` is `None`, the created node is the root of a tree.
2773    /// If the `index` is greater than the number of children of the parent, error will be returned.
2774    ///
2775    /// # Example
2776    ///
2777    /// ```rust
2778    /// use loro::LoroDoc;
2779    ///
2780    /// let doc = LoroDoc::new();
2781    /// let tree = doc.get_tree("tree");
2782    /// // enable generate fractional index
2783    /// tree.enable_fractional_index(0);
2784    /// // create a root
2785    /// let root = tree.create(None).unwrap();
2786    /// // create a new child at index 0
2787    /// let child = tree.create_at(root, 0).unwrap();
2788    /// ```
2789    pub fn create_at<T: Into<TreeParentId>>(&self, parent: T, index: usize) -> LoroResult<TreeID> {
2790        if !self.handler.is_fractional_index_enabled() {
2791            return Err(LoroTreeError::FractionalIndexNotEnabled.into());
2792        }
2793        self.handler.create_at(parent.into(), index)
2794    }
2795
2796    /// Move the `target` node to be a child of the `parent` node.
2797    ///
2798    /// If the `parent` is `None`, the `target` node will be a root.
2799    ///
2800    /// # Example
2801    ///
2802    /// ```rust
2803    /// use loro::LoroDoc;
2804    ///
2805    /// let doc = LoroDoc::new();
2806    /// let tree = doc.get_tree("tree");
2807    /// let root = tree.create(None).unwrap();
2808    /// let root2 = tree.create(None).unwrap();
2809    /// // move `root2` to be a child of `root`.
2810    /// tree.mov(root2, root).unwrap();
2811    /// ```
2812    pub fn mov<T: Into<TreeParentId>>(&self, target: TreeID, parent: T) -> LoroResult<()> {
2813        self.handler.mov(target, parent.into())
2814    }
2815
2816    /// Move the `target` node to be a child of the `parent` node at the given index.
2817    /// If the `parent` is `None`, the `target` node will be a root.
2818    ///
2819    /// # Example
2820    ///
2821    /// ```rust
2822    /// use loro::LoroDoc;
2823    ///
2824    /// let doc = LoroDoc::new();
2825    /// let tree = doc.get_tree("tree");
2826    /// // enable generate fractional index
2827    /// tree.enable_fractional_index(0);
2828    /// let root = tree.create(None).unwrap();
2829    /// let root2 = tree.create(None).unwrap();
2830    /// // move `root2` to be a child of `root` at index 0.
2831    /// tree.mov_to(root2, root, 0).unwrap();
2832    /// ```
2833    pub fn mov_to<T: Into<TreeParentId>>(
2834        &self,
2835        target: TreeID,
2836        parent: T,
2837        to: usize,
2838    ) -> LoroResult<()> {
2839        if !self.handler.is_fractional_index_enabled() {
2840            return Err(LoroTreeError::FractionalIndexNotEnabled.into());
2841        }
2842        self.handler.move_to(target, parent.into(), to)
2843    }
2844
2845    /// Move the `target` node to be a child after the `after` node with the same parent.
2846    ///
2847    /// # Example
2848    ///
2849    /// ```rust
2850    /// use loro::LoroDoc;
2851    ///
2852    /// let doc = LoroDoc::new();
2853    /// let tree = doc.get_tree("tree");
2854    /// // enable generate fractional index
2855    /// tree.enable_fractional_index(0);
2856    /// let root = tree.create(None).unwrap();
2857    /// let root2 = tree.create(None).unwrap();
2858    /// // move `root` to be a child after `root2`.
2859    /// tree.mov_after(root, root2).unwrap();
2860    /// ```
2861    pub fn mov_after(&self, target: TreeID, after: TreeID) -> LoroResult<()> {
2862        if !self.handler.is_fractional_index_enabled() {
2863            return Err(LoroTreeError::FractionalIndexNotEnabled.into());
2864        }
2865        self.handler.mov_after(target, after)
2866    }
2867
2868    /// Move the `target` node to be a child before the `before` node with the same parent.
2869    ///
2870    /// # Example
2871    ///
2872    /// ```rust
2873    /// use loro::LoroDoc;
2874    ///
2875    /// let doc = LoroDoc::new();
2876    /// let tree = doc.get_tree("tree");
2877    /// // enable generate fractional index
2878    /// tree.enable_fractional_index(0);
2879    /// let root = tree.create(None).unwrap();
2880    /// let root2 = tree.create(None).unwrap();
2881    /// // move `root` to be a child before `root2`.
2882    /// tree.mov_before(root, root2).unwrap();
2883    /// ```
2884    pub fn mov_before(&self, target: TreeID, before: TreeID) -> LoroResult<()> {
2885        if !self.handler.is_fractional_index_enabled() {
2886            return Err(LoroTreeError::FractionalIndexNotEnabled.into());
2887        }
2888        self.handler.mov_before(target, before)
2889    }
2890
2891    /// Delete a tree node.
2892    ///
2893    /// Note: If the deleted node has children, the children do not appear in the state
2894    /// rather than actually being deleted.
2895    ///
2896    /// # Example
2897    ///
2898    /// ```rust
2899    /// use loro::LoroDoc;
2900    ///
2901    /// let doc = LoroDoc::new();
2902    /// let tree = doc.get_tree("tree");
2903    /// let root = tree.create(None).unwrap();
2904    /// tree.delete(root).unwrap();
2905    /// ```
2906    pub fn delete(&self, target: TreeID) -> LoroResult<()> {
2907        self.handler.delete(target)
2908    }
2909
2910    /// Get the associated metadata map handler of a tree node.
2911    ///
2912    /// # Example
2913    /// ```rust
2914    /// use loro::LoroDoc;
2915    ///
2916    /// let doc = LoroDoc::new();
2917    /// let tree = doc.get_tree("tree");
2918    /// let root = tree.create(None).unwrap();
2919    /// let root_meta = tree.get_meta(root).unwrap();
2920    /// root_meta.insert("color", "red");
2921    /// ```
2922    pub fn get_meta(&self, target: TreeID) -> LoroResult<LoroMap> {
2923        self.handler
2924            .get_meta(target)
2925            .map(|h| LoroMap { handler: h })
2926    }
2927
2928    /// Return the parent of target node.
2929    ///
2930    /// - If the target node does not exist, return `None`.
2931    /// - If the target node is a root node, return `Some(None)`.
2932    pub fn parent(&self, target: TreeID) -> Option<TreeParentId> {
2933        self.handler.get_node_parent(&target)
2934    }
2935
2936    /// Return whether target node exists. including deleted node.
2937    pub fn contains(&self, target: TreeID) -> bool {
2938        self.handler.contains(target)
2939    }
2940
2941    /// Return whether target node is deleted.
2942    ///
2943    /// # Errors
2944    ///
2945    /// - If the target node does not exist, return `LoroTreeError::TreeNodeNotExist`.
2946    pub fn is_node_deleted(&self, target: &TreeID) -> LoroResult<bool> {
2947        self.handler.is_node_deleted(target)
2948    }
2949
2950    /// Return all nodes, including deleted nodes
2951    pub fn nodes(&self) -> Vec<TreeID> {
2952        self.handler.nodes()
2953    }
2954
2955    /// Return all nodes, if `with_deleted` is true, the deleted nodes will be included.
2956    pub fn get_nodes(&self, with_deleted: bool) -> Vec<TreeNode> {
2957        let mut ans = self.handler.get_nodes_under(TreeParentId::Root);
2958        if with_deleted {
2959            ans.extend(self.handler.get_nodes_under(TreeParentId::Deleted));
2960        }
2961        ans.into_iter()
2962            .map(|x| TreeNode {
2963                id: x.id,
2964                parent: x.parent,
2965                fractional_index: x.fractional_index,
2966                index: x.index,
2967            })
2968            .collect()
2969    }
2970
2971    /// Return all children of the target node.
2972    ///
2973    /// If the parent node does not exist, return `None`.
2974    pub fn children<T: Into<TreeParentId>>(&self, parent: T) -> Option<Vec<TreeID>> {
2975        self.handler.children(&parent.into())
2976    }
2977
2978    /// Return the number of children of the target node.
2979    pub fn children_num<T: Into<TreeParentId>>(&self, parent: T) -> Option<usize> {
2980        let parent: TreeParentId = parent.into();
2981        self.handler.children_num(&parent)
2982    }
2983
2984    /// Return the fractional index of the target node with hex format.
2985    pub fn fractional_index(&self, target: TreeID) -> Option<String> {
2986        self.handler
2987            .get_position_by_tree_id(&target)
2988            .map(|x| x.to_string())
2989    }
2990
2991    /// Return the hierarchy array of the forest.
2992    ///
2993    /// Note: the metadata will be not resolved. So if you don't only care about hierarchy
2994    /// but also the metadata, you should use [TreeHandler::get_value_with_meta()].
2995    pub fn get_value(&self) -> LoroValue {
2996        self.handler.get_value()
2997    }
2998
2999    /// Return the hierarchy array of the forest, each node is with metadata.
3000    pub fn get_value_with_meta(&self) -> LoroValue {
3001        self.handler.get_deep_value()
3002    }
3003
3004    // This method is used for testing only.
3005    #[doc(hidden)]
3006    #[allow(non_snake_case)]
3007    pub fn __internal__next_tree_id(&self) -> TreeID {
3008        self.handler.__internal__next_tree_id()
3009    }
3010
3011    /// Whether the fractional index is enabled.
3012    pub fn is_fractional_index_enabled(&self) -> bool {
3013        self.handler.is_fractional_index_enabled()
3014    }
3015
3016    /// Enable fractional index for Tree Position.
3017    ///
3018    /// The jitter is used to avoid conflicts when multiple users are creating the node at the same position.
3019    /// value 0 is default, which means no jitter, any value larger than 0 will enable jitter.
3020    ///
3021    /// Generally speaking, jitter will affect the growth rate of document size.
3022    /// [Read more about it](https://www.loro.dev/blog/movable-tree#implementation-and-encoding-size)
3023    #[inline]
3024    pub fn enable_fractional_index(&self, jitter: u8) {
3025        self.handler.enable_fractional_index(jitter);
3026    }
3027
3028    /// Disable the fractional index generation when you don't need the Tree's siblings to be sorted.
3029    /// The fractional index will always be set to the same default value 0.
3030    ///
3031    /// After calling this, you cannot use `tree.moveTo()`, `tree.moveBefore()`, `tree.moveAfter()`,
3032    /// and `tree.createAt()`.
3033    #[inline]
3034    pub fn disable_fractional_index(&self) {
3035        self.handler.disable_fractional_index();
3036    }
3037
3038    /// Whether the tree is empty.
3039    ///
3040    #[inline]
3041    pub fn is_empty(&self) -> bool {
3042        self.handler.is_empty()
3043    }
3044
3045    /// Get the last move id of the target node.
3046    pub fn get_last_move_id(&self, target: &TreeID) -> Option<ID> {
3047        self.handler.get_last_move_id(target)
3048    }
3049}
3050
3051impl Default for LoroTree {
3052    fn default() -> Self {
3053        Self::new()
3054    }
3055}
3056
3057/// [LoroMovableList container](https://loro.dev/docs/tutorial/list)
3058///
3059/// It is used to model movable ordered lists.
3060///
3061/// Using a combination of insert and delete operations, one can simulate set and move
3062/// operations on a List. However, this approach fails in concurrent editing scenarios.
3063/// For example, if the same element is set or moved concurrently, the simulation would
3064/// result in the deletion of the original element and the insertion of two new elements,
3065/// which does not meet expectations.
3066#[derive(Clone, Debug)]
3067pub struct LoroMovableList {
3068    handler: InnerMovableListHandler,
3069}
3070
3071impl SealedTrait for LoroMovableList {}
3072impl ContainerTrait for LoroMovableList {
3073    type Handler = InnerMovableListHandler;
3074
3075    fn id(&self) -> ContainerID {
3076        self.handler.id()
3077    }
3078
3079    fn to_container(&self) -> Container {
3080        Container::MovableList(self.clone())
3081    }
3082
3083    fn to_handler(&self) -> Self::Handler {
3084        self.handler.clone()
3085    }
3086
3087    fn from_handler(handler: Self::Handler) -> Self {
3088        Self { handler }
3089    }
3090
3091    fn try_from_container(container: Container) -> Option<Self>
3092    where
3093        Self: Sized,
3094    {
3095        match container {
3096            Container::MovableList(x) => Some(x),
3097            _ => None,
3098        }
3099    }
3100
3101    fn is_attached(&self) -> bool {
3102        self.handler.is_attached()
3103    }
3104
3105    fn get_attached(&self) -> Option<Self>
3106    where
3107        Self: Sized,
3108    {
3109        self.handler.get_attached().map(Self::from_handler)
3110    }
3111
3112    fn is_deleted(&self) -> bool {
3113        self.handler.is_deleted()
3114    }
3115
3116    fn doc(&self) -> Option<LoroDoc> {
3117        self.handler.doc().map(|doc| {
3118            doc.start_auto_commit();
3119            LoroDoc::_new(doc)
3120        })
3121    }
3122}
3123
3124impl LoroMovableList {
3125    /// Create a new container that is detached from the document.
3126    ///
3127    /// The edits on a detached container will not be persisted.
3128    /// To attach the container to the document, please insert it into an attached container.
3129    pub fn new() -> LoroMovableList {
3130        Self {
3131            handler: InnerMovableListHandler::new_detached(),
3132        }
3133    }
3134
3135    /// Whether the container is attached to a document
3136    ///
3137    /// The edits on a detached container will not be persisted.
3138    /// To attach the container to the document, please insert it into an attached container.
3139    pub fn is_attached(&self) -> bool {
3140        self.handler.is_attached()
3141    }
3142
3143    /// Insert a value at the given position.
3144    pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
3145        self.handler.insert(pos, v)
3146    }
3147
3148    /// Delete the value at the given position.
3149    pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
3150        self.handler.delete(pos, len)
3151    }
3152
3153    /// Get the value at the given position.
3154    pub fn get(&self, index: usize) -> Option<ValueOrContainer> {
3155        self.handler.get_(index).map(ValueOrContainer::from)
3156    }
3157
3158    /// Get the length of the list.
3159    pub fn len(&self) -> usize {
3160        self.handler.len()
3161    }
3162
3163    /// Whether the list is empty.
3164    #[must_use]
3165    pub fn is_empty(&self) -> bool {
3166        self.len() == 0
3167    }
3168
3169    /// Get the shallow value of the list.
3170    ///
3171    /// It will not convert the state of sub-containers, but represent them as [LoroValue::Container].
3172    pub fn get_value(&self) -> LoroValue {
3173        self.handler.get_value()
3174    }
3175
3176    /// Get the deep value of the list.
3177    ///
3178    /// It will convert the state of sub-containers into a nested JSON value.
3179    pub fn get_deep_value(&self) -> LoroValue {
3180        self.handler.get_deep_value()
3181    }
3182
3183    /// Pop the last element of the list.
3184    pub fn pop(&self) -> LoroResult<Option<ValueOrContainer>> {
3185        let ans = self.handler.pop_()?.map(ValueOrContainer::from);
3186        Ok(ans)
3187    }
3188
3189    /// Push a value to the end of the list.
3190    pub fn push(&self, v: impl Into<LoroValue>) -> LoroResult<()> {
3191        self.handler.push(v.into())
3192    }
3193
3194    /// Push a container to the end of the list.
3195    pub fn push_container<C: ContainerTrait>(&self, child: C) -> LoroResult<C> {
3196        let pos = self.handler.len();
3197        Ok(C::from_handler(
3198            self.handler.insert_container(pos, child.to_handler())?,
3199        ))
3200    }
3201
3202    /// Set the value at the given position.
3203    ///
3204    /// # Example
3205    /// ```
3206    /// use loro::{LoroDoc, ToJson};
3207    /// use serde_json::json;
3208    /// let doc = LoroDoc::new();
3209    /// let ml = doc.get_movable_list("ml");
3210    /// ml.insert(0, "a").unwrap();
3211    /// ml.set(0, "b").unwrap();
3212    /// assert_eq!(ml.get_deep_value().to_json_value(), json!(["b"]));
3213    /// ```
3214    pub fn set(&self, pos: usize, value: impl Into<LoroValue>) -> LoroResult<()> {
3215        self.handler.set(pos, value.into())
3216    }
3217
3218    /// Move the value at the given position to the given position.
3219    ///
3220    /// # Example
3221    /// ```
3222    /// use loro::{LoroDoc, ToJson};
3223    /// use serde_json::json;
3224    /// let doc = LoroDoc::new();
3225    /// let ml = doc.get_movable_list("ml");
3226    /// ml.insert(0, "a").unwrap();
3227    /// ml.insert(1, "b").unwrap();
3228    /// ml.insert(2, "c").unwrap();
3229    /// ml.mov(0, 2).unwrap();
3230    /// assert_eq!(ml.get_deep_value().to_json_value(), json!(["b","c","a"]));
3231    /// ```
3232    pub fn mov(&self, from: usize, to: usize) -> LoroResult<()> {
3233        self.handler.mov(from, to)
3234    }
3235
3236    /// Insert a container at the given position.
3237    pub fn insert_container<C: ContainerTrait>(&self, pos: usize, child: C) -> LoroResult<C> {
3238        Ok(C::from_handler(
3239            self.handler.insert_container(pos, child.to_handler())?,
3240        ))
3241    }
3242
3243    /// Set the container at the given position.
3244    pub fn set_container<C: ContainerTrait>(&self, pos: usize, child: C) -> LoroResult<C> {
3245        Ok(C::from_handler(
3246            self.handler.set_container(pos, child.to_handler())?,
3247        ))
3248    }
3249
3250    /// Log the internal state of the list.
3251    pub fn log_internal_state(&self) {
3252        info!(
3253            "movable_list internal state: {}",
3254            self.handler.log_internal_state()
3255        )
3256    }
3257
3258    /// Get the cursor at the given position.
3259    ///
3260    /// Using "index" to denote cursor positions can be unstable, as positions may
3261    /// shift with document edits. To reliably represent a position or range within
3262    /// a document, it is more effective to leverage the unique ID of each item/character
3263    /// in a List CRDT or Text CRDT.
3264    ///
3265    /// Loro optimizes State metadata by not storing the IDs of deleted elements. This
3266    /// approach complicates tracking cursors since they rely on these IDs. The solution
3267    /// recalculates position by replaying relevant history to update stable positions
3268    /// accurately. To minimize the performance impact of history replay, the system
3269    /// updates cursor info to reference only the IDs of currently present elements,
3270    /// thereby reducing the need for replay.
3271    ///
3272    /// # Example
3273    ///
3274    /// ```
3275    /// use loro::LoroDoc;
3276    /// use loro_internal::cursor::Side;
3277    ///
3278    /// let doc = LoroDoc::new();
3279    /// let list = doc.get_movable_list("list");
3280    /// list.insert(0, 0).unwrap();
3281    /// let cursor = list.get_cursor(0, Side::Middle).unwrap();
3282    /// assert_eq!(doc.get_cursor_pos(&cursor).unwrap().current.pos, 0);
3283    /// list.insert(0, 0).unwrap();
3284    /// assert_eq!(doc.get_cursor_pos(&cursor).unwrap().current.pos, 1);
3285    /// list.insert(0, 0).unwrap();
3286    /// list.insert(0, 0).unwrap();
3287    /// assert_eq!(doc.get_cursor_pos(&cursor).unwrap().current.pos, 3);
3288    /// list.insert(4, 0).unwrap();
3289    /// assert_eq!(doc.get_cursor_pos(&cursor).unwrap().current.pos, 3);
3290    /// ```
3291    pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
3292        self.handler.get_cursor(pos, side)
3293    }
3294
3295    /// Get the elements of the list as a vector of LoroValues.
3296    ///
3297    /// This method returns a vector containing all the elements in the list as LoroValues.
3298    /// It provides a convenient way to access the entire contents of the LoroMovableList
3299    /// as a standard Rust vector.
3300    ///
3301    /// # Returns
3302    ///
3303    /// A `Vec<LoroValue>` containing all elements of the list.
3304    ///
3305    /// # Example
3306    ///
3307    /// ```
3308    /// use loro::LoroDoc;
3309    ///
3310    /// let doc = LoroDoc::new();
3311    /// let list = doc.get_movable_list("mylist");
3312    /// list.insert(0, 1).unwrap();
3313    /// list.insert(1, "hello").unwrap();
3314    /// list.insert(2, true).unwrap();
3315    ///
3316    /// let vec = list.to_vec();
3317    /// assert_eq!(vec.len(), 3);
3318    /// assert_eq!(vec[0], 1.into());
3319    /// assert_eq!(vec[1], "hello".into());
3320    /// assert_eq!(vec[2], true.into());
3321    /// ```
3322    pub fn to_vec(&self) -> Vec<LoroValue> {
3323        self.get_value().into_list().unwrap().unwrap()
3324    }
3325
3326    /// Delete all elements in the list.
3327    pub fn clear(&self) -> LoroResult<()> {
3328        self.handler.clear()
3329    }
3330
3331    /// Iterate over the elements of the list.
3332    pub fn for_each<I>(&self, mut f: I)
3333    where
3334        I: FnMut(ValueOrContainer),
3335    {
3336        self.handler.for_each(&mut |v| {
3337            f(ValueOrContainer::from(v));
3338        })
3339    }
3340
3341    /// Get the creator of the list item at the given position.
3342    pub fn get_creator_at(&self, pos: usize) -> Option<PeerID> {
3343        self.handler.get_creator_at(pos)
3344    }
3345
3346    /// Get the last mover of the list item at the given position.
3347    pub fn get_last_mover_at(&self, pos: usize) -> Option<PeerID> {
3348        self.handler.get_last_mover_at(pos)
3349    }
3350
3351    /// Get the last editor of the list item at the given position.
3352    pub fn get_last_editor_at(&self, pos: usize) -> Option<PeerID> {
3353        self.handler.get_last_editor_at(pos)
3354    }
3355}
3356
3357impl Default for LoroMovableList {
3358    fn default() -> Self {
3359        Self::new()
3360    }
3361}
3362
3363/// Unknown container.
3364#[derive(Clone, Debug)]
3365pub struct LoroUnknown {
3366    handler: InnerUnknownHandler,
3367}
3368
3369impl SealedTrait for LoroUnknown {}
3370impl ContainerTrait for LoroUnknown {
3371    type Handler = InnerUnknownHandler;
3372
3373    fn id(&self) -> ContainerID {
3374        self.handler.id()
3375    }
3376
3377    fn to_container(&self) -> Container {
3378        Container::Unknown(self.clone())
3379    }
3380
3381    fn to_handler(&self) -> Self::Handler {
3382        self.handler.clone()
3383    }
3384
3385    fn from_handler(handler: Self::Handler) -> Self {
3386        Self { handler }
3387    }
3388
3389    fn try_from_container(container: Container) -> Option<Self>
3390    where
3391        Self: Sized,
3392    {
3393        match container {
3394            Container::Unknown(x) => Some(x),
3395            _ => None,
3396        }
3397    }
3398
3399    fn is_attached(&self) -> bool {
3400        self.handler.is_attached()
3401    }
3402
3403    fn get_attached(&self) -> Option<Self>
3404    where
3405        Self: Sized,
3406    {
3407        self.handler.get_attached().map(Self::from_handler)
3408    }
3409
3410    fn is_deleted(&self) -> bool {
3411        self.handler.is_deleted()
3412    }
3413
3414    fn doc(&self) -> Option<LoroDoc> {
3415        self.handler.doc().map(|doc| {
3416            doc.start_auto_commit();
3417            LoroDoc::_new(doc)
3418        })
3419    }
3420}
3421
3422use enum_as_inner::EnumAsInner;
3423#[cfg(feature = "jsonpath")]
3424use loro_internal::jsonpath::jsonpath_impl::JsonPathError;
3425
3426/// All the CRDT containers supported by Loro.
3427#[derive(Clone, Debug, EnumAsInner)]
3428pub enum Container {
3429    /// [LoroList container](https://loro.dev/docs/tutorial/list)
3430    List(LoroList),
3431    /// [LoroMap container](https://loro.dev/docs/tutorial/map)
3432    Map(LoroMap),
3433    /// [LoroText container](https://loro.dev/docs/tutorial/text)
3434    Text(LoroText),
3435    /// [LoroTree container]
3436    Tree(LoroTree),
3437    /// [LoroMovableList container](https://loro.dev/docs/tutorial/list)
3438    MovableList(LoroMovableList),
3439    #[cfg(feature = "counter")]
3440    /// [LoroCounter container]
3441    Counter(counter::LoroCounter),
3442    /// Unknown container
3443    Unknown(LoroUnknown),
3444}
3445
3446impl SealedTrait for Container {}
3447impl ContainerTrait for Container {
3448    type Handler = loro_internal::handler::Handler;
3449
3450    fn id(&self) -> ContainerID {
3451        match self {
3452            Container::List(x) => x.id(),
3453            Container::Map(x) => x.id(),
3454            Container::Text(x) => x.id(),
3455            Container::Tree(x) => x.id(),
3456            Container::MovableList(x) => x.id(),
3457            #[cfg(feature = "counter")]
3458            Container::Counter(x) => x.id(),
3459            Container::Unknown(x) => x.id(),
3460        }
3461    }
3462
3463    fn to_container(&self) -> Container {
3464        self.clone()
3465    }
3466
3467    fn to_handler(&self) -> Self::Handler {
3468        match self {
3469            Container::List(x) => Self::Handler::List(x.to_handler()),
3470            Container::Map(x) => Self::Handler::Map(x.to_handler()),
3471            Container::Text(x) => Self::Handler::Text(x.to_handler()),
3472            Container::Tree(x) => Self::Handler::Tree(x.to_handler()),
3473            Container::MovableList(x) => Self::Handler::MovableList(x.to_handler()),
3474            #[cfg(feature = "counter")]
3475            Container::Counter(x) => Self::Handler::Counter(x.to_handler()),
3476            Container::Unknown(x) => Self::Handler::Unknown(x.to_handler()),
3477        }
3478    }
3479
3480    fn from_handler(handler: Self::Handler) -> Self {
3481        match handler {
3482            InnerHandler::Text(x) => Container::Text(LoroText { handler: x }),
3483            InnerHandler::Map(x) => Container::Map(LoroMap { handler: x }),
3484            InnerHandler::List(x) => Container::List(LoroList { handler: x }),
3485            InnerHandler::MovableList(x) => Container::MovableList(LoroMovableList { handler: x }),
3486            InnerHandler::Tree(x) => Container::Tree(LoroTree { handler: x }),
3487            #[cfg(feature = "counter")]
3488            InnerHandler::Counter(x) => Container::Counter(counter::LoroCounter { handler: x }),
3489            InnerHandler::Unknown(x) => Container::Unknown(LoroUnknown { handler: x }),
3490        }
3491    }
3492
3493    fn is_attached(&self) -> bool {
3494        match self {
3495            Container::List(x) => x.is_attached(),
3496            Container::Map(x) => x.is_attached(),
3497            Container::Text(x) => x.is_attached(),
3498            Container::Tree(x) => x.is_attached(),
3499            Container::MovableList(x) => x.is_attached(),
3500            #[cfg(feature = "counter")]
3501            Container::Counter(x) => x.is_attached(),
3502            Container::Unknown(x) => x.is_attached(),
3503        }
3504    }
3505
3506    fn get_attached(&self) -> Option<Self> {
3507        match self {
3508            Container::List(x) => x.get_attached().map(Container::List),
3509            Container::MovableList(x) => x.get_attached().map(Container::MovableList),
3510            Container::Map(x) => x.get_attached().map(Container::Map),
3511            Container::Text(x) => x.get_attached().map(Container::Text),
3512            Container::Tree(x) => x.get_attached().map(Container::Tree),
3513            #[cfg(feature = "counter")]
3514            Container::Counter(x) => x.get_attached().map(Container::Counter),
3515            Container::Unknown(x) => x.get_attached().map(Container::Unknown),
3516        }
3517    }
3518
3519    fn try_from_container(container: Container) -> Option<Self>
3520    where
3521        Self: Sized,
3522    {
3523        Some(container)
3524    }
3525
3526    fn is_deleted(&self) -> bool {
3527        match self {
3528            Container::List(x) => x.is_deleted(),
3529            Container::Map(x) => x.is_deleted(),
3530            Container::Text(x) => x.is_deleted(),
3531            Container::Tree(x) => x.is_deleted(),
3532            Container::MovableList(x) => x.is_deleted(),
3533            #[cfg(feature = "counter")]
3534            Container::Counter(x) => x.is_deleted(),
3535            Container::Unknown(x) => x.is_deleted(),
3536        }
3537    }
3538    fn doc(&self) -> Option<LoroDoc> {
3539        match self {
3540            Container::List(x) => x.doc(),
3541            Container::Map(x) => x.doc(),
3542            Container::Text(x) => x.doc(),
3543            Container::Tree(x) => x.doc(),
3544            Container::MovableList(x) => x.doc(),
3545            #[cfg(feature = "counter")]
3546            Container::Counter(x) => x.doc(),
3547            Container::Unknown(x) => x.doc(),
3548        }
3549    }
3550}
3551
3552impl Container {
3553    /// Create a detached container of the given type.
3554    ///
3555    /// A detached container is a container that is not attached to a document.
3556    /// The edits on a detached container will not be persisted.
3557    /// To attach the container to the document, please insert it into an attached container.
3558    pub fn new(kind: ContainerType) -> Self {
3559        match kind {
3560            ContainerType::List => Container::List(LoroList::new()),
3561            ContainerType::MovableList => Container::MovableList(LoroMovableList::new()),
3562            ContainerType::Map => Container::Map(LoroMap::new()),
3563            ContainerType::Text => Container::Text(LoroText::new()),
3564            ContainerType::Tree => Container::Tree(LoroTree::new()),
3565            #[cfg(feature = "counter")]
3566            ContainerType::Counter => Container::Counter(counter::LoroCounter::new()),
3567            ContainerType::Unknown(_) => unreachable!(),
3568        }
3569    }
3570
3571    /// Get the type of the container.
3572    pub fn get_type(&self) -> ContainerType {
3573        match self {
3574            Container::List(_) => ContainerType::List,
3575            Container::MovableList(_) => ContainerType::MovableList,
3576            Container::Map(_) => ContainerType::Map,
3577            Container::Text(_) => ContainerType::Text,
3578            Container::Tree(_) => ContainerType::Tree,
3579            #[cfg(feature = "counter")]
3580            Container::Counter(_) => ContainerType::Counter,
3581            Container::Unknown(x) => x.handler.id().container_type(),
3582        }
3583    }
3584}
3585
3586impl From<InnerHandler> for Container {
3587    fn from(value: InnerHandler) -> Self {
3588        match value {
3589            InnerHandler::Text(x) => Container::Text(LoroText { handler: x }),
3590            InnerHandler::Map(x) => Container::Map(LoroMap { handler: x }),
3591            InnerHandler::List(x) => Container::List(LoroList { handler: x }),
3592            InnerHandler::Tree(x) => Container::Tree(LoroTree { handler: x }),
3593            InnerHandler::MovableList(x) => Container::MovableList(LoroMovableList { handler: x }),
3594            #[cfg(feature = "counter")]
3595            InnerHandler::Counter(x) => Container::Counter(counter::LoroCounter { handler: x }),
3596            InnerHandler::Unknown(x) => Container::Unknown(LoroUnknown { handler: x }),
3597        }
3598    }
3599}
3600
3601/// It's a type that can be either a value or a container.
3602#[derive(Debug, Clone, EnumAsInner)]
3603pub enum ValueOrContainer {
3604    /// A value.
3605    Value(LoroValue),
3606    /// A container.
3607    Container(Container),
3608}
3609
3610impl ValueOrContainer {
3611    /// Get the deep value of the value or container.
3612    pub fn get_deep_value(&self) -> LoroValue {
3613        match self {
3614            ValueOrContainer::Value(v) => v.clone(),
3615            ValueOrContainer::Container(c) => match c {
3616                Container::List(c) => c.get_deep_value(),
3617                Container::Map(c) => c.get_deep_value(),
3618                Container::Text(c) => c.to_string().into(),
3619                Container::Tree(c) => c.get_value(),
3620                Container::MovableList(c) => c.get_deep_value(),
3621                #[cfg(feature = "counter")]
3622                Container::Counter(c) => c.get_value().into(),
3623                Container::Unknown(_) => LoroValue::Null,
3624            },
3625        }
3626    }
3627
3628    pub(crate) fn into_value_or_handler(self) -> ValueOrHandler {
3629        match self {
3630            ValueOrContainer::Value(v) => ValueOrHandler::Value(v),
3631            ValueOrContainer::Container(c) => ValueOrHandler::Handler(c.to_handler()),
3632        }
3633    }
3634}
3635
3636/// UndoManager can be used to undo and redo the changes made to the document with a certain peer.
3637///
3638/// Notes & pitfalls:
3639/// - Local-only: undo/redo affects only local operations from the bound peer; it does not revert
3640///   remote edits. For global rollback, use time travel (`checkout`/`revert_to`).
3641/// - Peer identity: keep the `peer_id` stable while an `UndoManager` is in use. Changing peer IDs
3642///   can disrupt undo grouping/semantics.
3643/// - Grouping: you may want to tune the merge interval and exclude origins to group related edits.
3644#[derive(Debug)]
3645#[repr(transparent)]
3646pub struct UndoManager(InnerUndoManager);
3647
3648impl UndoManager {
3649    /// Create a new UndoManager.
3650    pub fn new(doc: &LoroDoc) -> Self {
3651        let inner = InnerUndoManager::new(&doc.doc);
3652        inner.set_max_undo_steps(100);
3653        Self(inner)
3654    }
3655
3656    /// Undo the last change made by the peer.
3657    pub fn undo(&mut self) -> LoroResult<bool> {
3658        self.0.undo()
3659    }
3660
3661    /// Redo the last change made by the peer.
3662    pub fn redo(&mut self) -> LoroResult<bool> {
3663        self.0.redo()
3664    }
3665
3666    /// Record a new checkpoint.
3667    pub fn record_new_checkpoint(&mut self) -> LoroResult<()> {
3668        self.0.record_new_checkpoint()
3669    }
3670
3671    /// Whether the undo manager can undo.
3672    pub fn can_undo(&self) -> bool {
3673        self.0.can_undo()
3674    }
3675
3676    /// Whether the undo manager can redo.
3677    pub fn can_redo(&self) -> bool {
3678        self.0.can_redo()
3679    }
3680
3681    /// How many times the undo manager can undo.
3682    pub fn undo_count(&self) -> usize {
3683        self.0.undo_count()
3684    }
3685
3686    /// How many times the undo manager can redo.
3687    pub fn redo_count(&self) -> usize {
3688        self.0.redo_count()
3689    }
3690
3691    /// If a local event's origin matches the given prefix, it will not be recorded in the
3692    /// undo stack.
3693    pub fn add_exclude_origin_prefix(&mut self, prefix: &str) {
3694        self.0.add_exclude_origin_prefix(prefix)
3695    }
3696
3697    /// Set the maximum number of undo steps. The default value is 100.
3698    pub fn set_max_undo_steps(&mut self, size: usize) {
3699        self.0.set_max_undo_steps(size)
3700    }
3701
3702    /// Set the merge interval in ms. The default value is 0, which means no merge.
3703    pub fn set_merge_interval(&mut self, interval: i64) {
3704        self.0.set_merge_interval(interval)
3705    }
3706
3707    /// Set the listener for push events.
3708    /// The listener will be called when a new undo/redo item is pushed into the stack.
3709    pub fn set_on_push(&mut self, on_push: Option<OnPush>) {
3710        if let Some(on_push) = on_push {
3711            self.0.set_on_push(Some(Box::new(move |u, c, e| {
3712                on_push(u, c, e.map(|x| x.into()))
3713            })));
3714        } else {
3715            self.0.set_on_push(None);
3716        }
3717    }
3718
3719    /// Set the listener for pop events.
3720    /// The listener will be called when an undo/redo item is popped from the stack.
3721    pub fn set_on_pop(&mut self, on_pop: Option<OnPop>) {
3722        self.0.set_on_pop(on_pop);
3723    }
3724
3725    /// Clear the undo stack and the redo stack
3726    pub fn clear(&self) {
3727        self.0.clear();
3728    }
3729
3730    /// Will start a new group of changes, all subsequent changes will be merged
3731    /// into a new item on the undo stack. If we receive remote changes, we determine
3732    /// wether or not they are conflicting. If the remote changes are conflicting
3733    /// we split the undo item and close the group. If there are no conflict
3734    /// in changed container ids we continue the group merge.
3735    pub fn group_start(&mut self) -> LoroResult<()> {
3736        self.0.group_start()
3737    }
3738
3739    /// Ends the current group, calling UndoManager::undo() after this will
3740    /// undo all changes that occurred during the group.
3741    pub fn group_end(&mut self) {
3742        self.0.group_end();
3743    }
3744
3745    /// Get the peer id of the undo manager.
3746    pub fn peer(&self) -> PeerID {
3747        self.0.peer()
3748    }
3749
3750    /// Get the metadata of the top undo stack item, if any.
3751    pub fn top_undo_meta(&self) -> Option<UndoItemMeta> {
3752        self.0.top_undo_meta()
3753    }
3754
3755    /// Get the metadata of the top redo stack item, if any.
3756    pub fn top_redo_meta(&self) -> Option<UndoItemMeta> {
3757        self.0.top_redo_meta()
3758    }
3759
3760    /// Get the value associated with the top undo stack item, if any.
3761    pub fn top_undo_value(&self) -> Option<LoroValue> {
3762        self.0.top_undo_value()
3763    }
3764
3765    /// Get the value associated with the top redo stack item, if any.
3766    pub fn top_redo_value(&self) -> Option<LoroValue> {
3767        self.0.top_redo_value()
3768    }
3769}
3770/// When a undo/redo item is pushed, the undo manager will call the on_push callback to get the meta data of the undo item.
3771/// The returned cursors will be recorded for a new pushed undo item.
3772pub type OnPush =
3773    Box<dyn for<'a> Fn(UndoOrRedo, CounterSpan, Option<DiffEvent>) -> UndoItemMeta + Send + Sync>;