1use super::{state::DocState, txn::Transaction};
2use crate::sync::Mutex;
3use crate::{
4 container::{
5 idx::ContainerIdx,
6 list::list_op::{DeleteSpan, DeleteSpanWithId, ListOp},
7 richtext::{richtext_state::PosType, RichtextState, StyleKey, StyleOp, TextStyleInfoFlag},
8 },
9 cursor::{Cursor, Side},
10 delta::{DeltaItem, Meta, StyleMeta, TreeExternalDiff},
11 diff::{diff, diff_impl::UpdateTimeoutError, OperateProxy},
12 event::{Diff, TextDiff, TextDiffItem, TextMeta},
13 op::ListSlice,
14 state::{IndexType, State, TreeParentId},
15 txn::EventHint,
16 utils::{string_slice::StringSlice, utf16::count_utf16_len},
17 LoroDoc, LoroDocInner,
18};
19use append_only_bytes::BytesSlice;
20use enum_as_inner::EnumAsInner;
21use generic_btree::rle::HasLength;
22use loro_common::{
23 ContainerID, ContainerType, IdFull, InternalString, LoroError, LoroResult, LoroValue, PeerID,
24 TreeID, ID,
25};
26use rustc_hash::FxHashMap;
27use serde::{Deserialize, Serialize};
28use std::{borrow::Cow, cmp::Reverse, collections::BinaryHeap, fmt::Debug, ops::Deref, sync::Arc};
29use tracing::{error, instrument};
30
31pub use crate::diff::diff_impl::UpdateOptions;
32pub use tree::TreeHandler;
33mod movable_list_apply_delta;
34mod tree;
35
36const REGULAR_CONTAINER_VALUE_ARG_ERROR: &str =
37 "Cannot use a LoroValue::Container as a regular value. To create a child container, use insert_container/set_container, or ensure_mergeable_* on maps for mergeable children";
38
39mod text_update;
40
41fn ensure_no_regular_container_value(value: &LoroValue) -> LoroResult<()> {
42 if !matches!(
48 value,
49 LoroValue::Container(_) | LoroValue::List(_) | LoroValue::Map(_)
50 ) {
51 return Ok(());
52 }
53
54 let mut stack = vec![value];
55 while let Some(value) = stack.pop() {
56 match value {
57 LoroValue::Container(_) => {
58 return Err(LoroError::ArgErr(
59 REGULAR_CONTAINER_VALUE_ARG_ERROR
60 .to_string()
61 .into_boxed_str(),
62 ));
63 }
64 LoroValue::List(list) => {
65 stack.extend(list.iter());
66 }
67 LoroValue::Map(map) => {
68 stack.extend(map.values());
69 }
70 LoroValue::Null
71 | LoroValue::Bool(_)
72 | LoroValue::Double(_)
73 | LoroValue::I64(_)
74 | LoroValue::Binary(_)
75 | LoroValue::String(_) => {}
76 }
77 }
78
79 Ok(())
80}
81
82fn checked_range_end(
83 pos: usize,
84 len: usize,
85 container_len: usize,
86 info: impl Fn() -> Box<str>,
89) -> LoroResult<usize> {
90 let end = pos.checked_add(len).ok_or_else(|| LoroError::OutOfBound {
91 pos: usize::MAX,
92 len: container_len,
93 info: info(),
94 })?;
95 if end > container_len {
96 return Err(LoroError::OutOfBound {
97 pos: end,
98 len: container_len,
99 info: info(),
100 });
101 }
102
103 Ok(end)
104}
105
106fn checked_delta_index_end(pos: usize, len: usize, container_len: usize) -> LoroResult<usize> {
107 pos.checked_add(len).ok_or_else(|| LoroError::OutOfBound {
108 pos: usize::MAX,
109 len: container_len,
110 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
111 })
112}
113
114pub trait HandlerTrait: Clone + Sized {
115 fn is_attached(&self) -> bool;
116 fn attached_handler(&self) -> Option<&BasicHandler>;
117 fn get_value(&self) -> LoroValue;
118 fn get_deep_value(&self) -> LoroValue;
119 fn kind(&self) -> ContainerType;
120 fn to_handler(&self) -> Handler;
121 fn from_handler(h: Handler) -> Option<Self>;
122 fn doc(&self) -> Option<LoroDoc>;
123 fn attach(
125 &self,
126 txn: &mut Transaction,
127 parent: &BasicHandler,
128 self_id: ContainerID,
129 ) -> LoroResult<Self>;
130 fn get_attached(&self) -> Option<Self>;
132
133 fn parent(&self) -> Option<Handler> {
134 self.attached_handler().and_then(|x| x.parent())
135 }
136
137 fn idx(&self) -> ContainerIdx {
138 self.attached_handler()
139 .map(|x| x.container_idx)
140 .unwrap_or_else(|| {
141 ContainerIdx::from_index_and_type(ContainerIdx::INDEX_MASK, self.kind())
142 })
143 }
144
145 fn id(&self) -> ContainerID {
146 self.attached_handler()
147 .map(|x| x.id.clone())
148 .unwrap_or_else(|| ContainerID::new_normal(ID::NONE_ID, self.kind()))
149 }
150
151 fn with_state<R>(&self, f: impl FnOnce(&mut State) -> LoroResult<R>) -> LoroResult<R> {
152 let inner = self
153 .attached_handler()
154 .ok_or(LoroError::MisuseDetachedContainer {
155 method: "with_state",
156 })?;
157 let state = inner.doc.state.clone();
158 let mut guard = state.lock();
159 guard.with_state_mut(inner.container_idx, f)
160 }
161}
162
163fn create_handler(inner: &BasicHandler, id: ContainerID) -> Handler {
164 Handler::new_attached(id, inner.doc.clone())
165}
166
167fn value_to_value_or_handler(inner: &BasicHandler, value: LoroValue) -> ValueOrHandler {
168 match value {
169 LoroValue::Container(container_id) => {
170 ValueOrHandler::Handler(create_handler(inner, container_id))
171 }
172 value => ValueOrHandler::Value(value),
173 }
174}
175
176#[derive(Clone, Debug)]
178pub struct BasicHandler {
179 id: ContainerID,
180 container_idx: ContainerIdx,
181 doc: LoroDoc,
182}
183
184struct DetachedInner<T> {
185 value: T,
186 attached: Option<BasicHandler>,
188}
189
190impl<T> DetachedInner<T> {
191 fn new(v: T) -> Self {
192 Self {
193 value: v,
194 attached: None,
195 }
196 }
197}
198
199enum MaybeDetached<T> {
200 Detached(Arc<Mutex<DetachedInner<T>>>),
201 Attached(BasicHandler),
202}
203
204impl<T> Clone for MaybeDetached<T> {
205 fn clone(&self) -> Self {
206 match self {
207 MaybeDetached::Detached(a) => MaybeDetached::Detached(Arc::clone(a)),
208 MaybeDetached::Attached(a) => MaybeDetached::Attached(a.clone()),
209 }
210 }
211}
212
213impl<T> MaybeDetached<T> {
214 fn new_detached(v: T) -> Self {
215 MaybeDetached::Detached(Arc::new(Mutex::new(DetachedInner::new(v))))
216 }
217
218 fn is_attached(&self) -> bool {
219 match self {
220 MaybeDetached::Detached(_) => false,
221 MaybeDetached::Attached(_) => true,
222 }
223 }
224
225 fn attached_handler(&self) -> Option<&BasicHandler> {
226 match self {
227 MaybeDetached::Detached(_) => None,
228 MaybeDetached::Attached(a) => Some(a),
229 }
230 }
231
232 fn try_attached_state(&self) -> LoroResult<&BasicHandler> {
233 match self {
234 MaybeDetached::Detached(_) => Err(LoroError::MisuseDetachedContainer {
235 method: "inner_state",
236 }),
237 MaybeDetached::Attached(a) => Ok(a),
238 }
239 }
240}
241
242impl<T> From<BasicHandler> for MaybeDetached<T> {
243 fn from(a: BasicHandler) -> Self {
244 MaybeDetached::Attached(a)
245 }
246}
247
248impl BasicHandler {
249 pub(crate) fn doc(&self) -> LoroDoc {
250 self.doc.clone()
251 }
252
253 #[inline]
254 fn with_doc_state<R>(&self, f: impl FnOnce(&mut DocState) -> R) -> R {
255 let state = self.doc.state.clone();
256 let mut guard = state.lock();
257 f(&mut guard)
258 }
259
260 fn with_txn<R>(
261 &self,
262 f: impl FnOnce(&mut Transaction) -> Result<R, LoroError>,
263 ) -> Result<R, LoroError> {
264 with_txn(&self.doc, f)
265 }
266
267 fn get_parent(&self) -> Option<Handler> {
268 let parent_idx = self.doc.arena.get_parent(self.container_idx)?;
269 let parent_id = self.doc.arena.get_container_id(parent_idx).unwrap();
270 {
271 let kind = parent_id.container_type();
272 let handler = BasicHandler {
273 container_idx: parent_idx,
274 id: parent_id,
275 doc: self.doc.clone(),
276 };
277
278 Some(match kind {
279 ContainerType::Map => Handler::Map(MapHandler {
280 inner: handler.into(),
281 }),
282 ContainerType::List => Handler::List(ListHandler {
283 inner: handler.into(),
284 }),
285 ContainerType::Tree => Handler::Tree(TreeHandler {
286 inner: handler.into(),
287 }),
288 ContainerType::Text => Handler::Text(TextHandler {
289 inner: handler.into(),
290 }),
291 ContainerType::MovableList => Handler::MovableList(MovableListHandler {
292 inner: handler.into(),
293 }),
294 #[cfg(feature = "counter")]
295 ContainerType::Counter => Handler::Counter(counter::CounterHandler {
296 inner: handler.into(),
297 }),
298 ContainerType::Unknown(_) => unreachable!(),
299 })
300 }
301 }
302
303 pub fn get_value(&self) -> LoroValue {
304 self.doc.state.lock().get_value_by_idx(self.container_idx)
305 }
306
307 pub fn get_deep_value(&self) -> LoroValue {
308 self.doc
309 .state
310 .lock()
311 .get_container_deep_value(self.container_idx)
312 }
313
314 fn with_state<R>(&self, f: impl FnOnce(&mut State) -> R) -> R {
315 let mut guard = self.doc.state.lock();
316 guard.with_state_mut(self.container_idx, f)
317 }
318
319 pub fn parent(&self) -> Option<Handler> {
320 self.get_parent()
321 }
322
323 fn is_deleted(&self) -> bool {
324 self.doc.state.lock().is_deleted(self.container_idx)
325 }
326
327 fn has_decoded_state(&self) -> bool {
328 self.with_doc_state(|state| state.has_decoded_container_state(self.container_idx))
329 }
330}
331
332#[derive(Clone)]
334pub struct TextHandler {
335 inner: MaybeDetached<RichtextState>,
336}
337
338impl HandlerTrait for TextHandler {
339 fn to_handler(&self) -> Handler {
340 Handler::Text(self.clone())
341 }
342
343 fn attach(
344 &self,
345 txn: &mut Transaction,
346 parent: &BasicHandler,
347 self_id: ContainerID,
348 ) -> LoroResult<Self> {
349 match &self.inner {
350 MaybeDetached::Detached(t) => {
351 let mut t = t.lock();
352 let inner = create_handler(parent, self_id);
353 let text = inner.into_text().unwrap();
354 let mut delta: Vec<TextDelta> = Vec::new();
355 for span in t.value.iter() {
356 delta.push(TextDelta::Insert {
357 insert: span.text.to_string(),
358 attributes: span.attributes.to_option_map(),
359 });
360 }
361
362 text.apply_delta_with_txn(txn, &delta)?;
363 t.attached = text.attached_handler().cloned();
364 Ok(text)
365 }
366 MaybeDetached::Attached(a) => {
367 let new_inner = create_handler(a, self_id);
368 let ans = new_inner.into_text().unwrap();
369
370 let delta = self.get_delta();
371 ans.apply_delta_with_txn(txn, &delta)?;
372 Ok(ans)
373 }
374 }
375 }
376
377 fn attached_handler(&self) -> Option<&BasicHandler> {
378 self.inner.attached_handler()
379 }
380
381 fn get_value(&self) -> LoroValue {
382 match &self.inner {
383 MaybeDetached::Detached(t) => {
384 let t = t.lock();
385 LoroValue::String((t.value.to_string()).into())
386 }
387 MaybeDetached::Attached(a) => a.get_value(),
388 }
389 }
390
391 fn get_deep_value(&self) -> LoroValue {
392 self.get_value()
393 }
394
395 fn is_attached(&self) -> bool {
396 matches!(&self.inner, MaybeDetached::Attached(..))
397 }
398
399 fn kind(&self) -> ContainerType {
400 ContainerType::Text
401 }
402
403 fn get_attached(&self) -> Option<Self> {
404 match &self.inner {
405 MaybeDetached::Detached(d) => d.lock().attached.clone().map(|x| Self {
406 inner: MaybeDetached::Attached(x),
407 }),
408 MaybeDetached::Attached(_a) => Some(self.clone()),
409 }
410 }
411
412 fn from_handler(h: Handler) -> Option<Self> {
413 match h {
414 Handler::Text(x) => Some(x),
415 _ => None,
416 }
417 }
418
419 fn doc(&self) -> Option<LoroDoc> {
420 match &self.inner {
421 MaybeDetached::Detached(_) => None,
422 MaybeDetached::Attached(a) => Some(a.doc()),
423 }
424 }
425}
426
427impl std::fmt::Debug for TextHandler {
428 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
429 match &self.inner {
430 MaybeDetached::Detached(_) => {
431 write!(f, "TextHandler(Unattached)")
432 }
433 MaybeDetached::Attached(a) => {
434 write!(f, "TextHandler({:?})", &a.id)
435 }
436 }
437 }
438}
439
440#[derive(Debug, Clone, EnumAsInner, Deserialize, Serialize, PartialEq)]
441#[serde(untagged)]
442pub enum TextDelta {
443 Retain {
444 retain: usize,
445 attributes: Option<FxHashMap<String, LoroValue>>,
446 },
447 Insert {
448 insert: String,
449 attributes: Option<FxHashMap<String, LoroValue>>,
450 },
451 Delete {
452 delete: usize,
453 },
454}
455
456impl TextDelta {
457 pub fn from_text_diff<'a>(diff: impl Iterator<Item = &'a TextDiffItem>) -> Vec<TextDelta> {
458 let mut ans = Vec::with_capacity(diff.size_hint().0);
459 for iter in diff {
460 match iter {
461 loro_delta::DeltaItem::Retain { len, attr } => {
462 ans.push(TextDelta::Retain {
463 retain: *len,
464 attributes: if attr.0.is_empty() {
465 None
466 } else {
467 Some(attr.0.clone())
468 },
469 });
470 }
471 loro_delta::DeltaItem::Replace {
472 value,
473 attr,
474 delete,
475 } => {
476 if value.rle_len() > 0 {
477 ans.push(TextDelta::Insert {
478 insert: value.to_string(),
479 attributes: if attr.0.is_empty() {
480 None
481 } else {
482 Some(attr.0.clone())
483 },
484 });
485 }
486 if *delete > 0 {
487 ans.push(TextDelta::Delete { delete: *delete });
488 }
489 }
490 }
491 }
492
493 ans
494 }
495
496 pub fn into_text_diff(vec: impl Iterator<Item = Self>) -> TextDiff {
497 let mut delta = TextDiff::new();
498 for item in vec {
499 match item {
500 TextDelta::Retain { retain, attributes } => {
501 delta.push_retain(retain, TextMeta(attributes.unwrap_or_default().clone()));
502 }
503 TextDelta::Insert { insert, attributes } => {
504 delta.push_insert(
505 StringSlice::from(insert.as_str()),
506 TextMeta(attributes.unwrap_or_default()),
507 );
508 }
509 TextDelta::Delete { delete } => {
510 delta.push_delete(delete);
511 }
512 }
513 }
514
515 delta
516 }
517}
518
519impl From<&DeltaItem<StringSlice, StyleMeta>> for TextDelta {
520 fn from(value: &DeltaItem<StringSlice, StyleMeta>) -> Self {
521 match value {
522 crate::delta::DeltaItem::Retain { retain, attributes } => TextDelta::Retain {
523 retain: *retain,
524 attributes: attributes.to_option_map(),
525 },
526 crate::delta::DeltaItem::Insert { insert, attributes } => TextDelta::Insert {
527 insert: insert.to_string(),
528 attributes: attributes.to_option_map(),
529 },
530 crate::delta::DeltaItem::Delete {
531 delete,
532 attributes: _,
533 } => TextDelta::Delete { delete: *delete },
534 }
535 }
536}
537
538#[derive(Clone)]
539pub struct MapHandler {
540 inner: MaybeDetached<FxHashMap<String, ValueOrHandler>>,
541}
542
543impl HandlerTrait for MapHandler {
544 fn is_attached(&self) -> bool {
545 matches!(&self.inner, MaybeDetached::Attached(..))
546 }
547
548 fn attached_handler(&self) -> Option<&BasicHandler> {
549 match &self.inner {
550 MaybeDetached::Detached(_) => None,
551 MaybeDetached::Attached(a) => Some(a),
552 }
553 }
554
555 fn get_value(&self) -> LoroValue {
556 match &self.inner {
557 MaybeDetached::Detached(m) => {
558 let m = m.lock();
559 let mut map = FxHashMap::default();
560 for (k, v) in m.value.iter() {
561 map.insert(k.to_string(), v.to_value());
562 }
563 LoroValue::Map(map.into())
564 }
565 MaybeDetached::Attached(a) => a.get_value(),
566 }
567 }
568
569 fn get_deep_value(&self) -> LoroValue {
570 match &self.inner {
571 MaybeDetached::Detached(m) => {
572 let m = m.lock();
573 let mut map = FxHashMap::default();
574 for (k, v) in m.value.iter() {
575 map.insert(k.to_string(), v.to_deep_value());
576 }
577 LoroValue::Map(map.into())
578 }
579 MaybeDetached::Attached(a) => a.get_deep_value(),
580 }
581 }
582
583 fn kind(&self) -> ContainerType {
584 ContainerType::Map
585 }
586
587 fn to_handler(&self) -> Handler {
588 Handler::Map(self.clone())
589 }
590
591 fn attach(
592 &self,
593 txn: &mut Transaction,
594 parent: &BasicHandler,
595 self_id: ContainerID,
596 ) -> LoroResult<Self> {
597 match &self.inner {
598 MaybeDetached::Detached(m) => {
599 let mut m = m.lock();
600 let inner = create_handler(parent, self_id);
601 let map = inner.into_map().unwrap();
602 for (k, v) in m.value.iter() {
603 match v {
604 ValueOrHandler::Value(v) => {
605 map.insert_with_txn(txn, k, v.clone())?;
606 }
607 ValueOrHandler::Handler(h) => {
608 map.insert_container_with_txn(txn, k, h.clone())?;
609 }
610 }
611 }
612 m.attached = map.attached_handler().cloned();
613 Ok(map)
614 }
615 MaybeDetached::Attached(a) => {
616 let new_inner = create_handler(a, self_id);
617 let ans = new_inner.into_map().unwrap();
618
619 for (k, v) in self.get_value().into_map().unwrap().iter() {
620 if let LoroValue::Container(id) = v {
621 ans.insert_container_with_txn(txn, k, create_handler(a, id.clone()))?;
622 } else {
623 ans.insert_with_txn(txn, k, v.clone())?;
624 }
625 }
626
627 Ok(ans)
628 }
629 }
630 }
631
632 fn get_attached(&self) -> Option<Self> {
633 match &self.inner {
634 MaybeDetached::Detached(d) => d.lock().attached.clone().map(|x| Self {
635 inner: MaybeDetached::Attached(x),
636 }),
637 MaybeDetached::Attached(_a) => Some(self.clone()),
638 }
639 }
640
641 fn from_handler(h: Handler) -> Option<Self> {
642 match h {
643 Handler::Map(x) => Some(x),
644 _ => None,
645 }
646 }
647
648 fn doc(&self) -> Option<LoroDoc> {
649 match &self.inner {
650 MaybeDetached::Detached(_) => None,
651 MaybeDetached::Attached(a) => Some(a.doc()),
652 }
653 }
654}
655
656impl std::fmt::Debug for MapHandler {
657 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
658 match &self.inner {
659 MaybeDetached::Detached(_) => write!(f, "MapHandler Detached"),
660 MaybeDetached::Attached(a) => write!(f, "MapHandler {}", a.id),
661 }
662 }
663}
664
665#[derive(Clone)]
666pub struct ListHandler {
667 inner: MaybeDetached<Vec<ValueOrHandler>>,
668}
669
670#[derive(Clone)]
671pub struct MovableListHandler {
672 inner: MaybeDetached<Vec<ValueOrHandler>>,
673}
674
675impl HandlerTrait for MovableListHandler {
676 fn is_attached(&self) -> bool {
677 matches!(&self.inner, MaybeDetached::Attached(..))
678 }
679
680 fn attached_handler(&self) -> Option<&BasicHandler> {
681 match &self.inner {
682 MaybeDetached::Detached(_) => None,
683 MaybeDetached::Attached(a) => Some(a),
684 }
685 }
686
687 fn get_value(&self) -> LoroValue {
688 match &self.inner {
689 MaybeDetached::Detached(a) => {
690 let a = a.lock();
691 LoroValue::List(a.value.iter().map(|v| v.to_value()).collect())
692 }
693 MaybeDetached::Attached(a) => a.get_value(),
694 }
695 }
696
697 fn get_deep_value(&self) -> LoroValue {
698 match &self.inner {
699 MaybeDetached::Detached(a) => {
700 let a = a.lock();
701 LoroValue::List(a.value.iter().map(|v| v.to_deep_value()).collect())
702 }
703 MaybeDetached::Attached(a) => a.get_deep_value(),
704 }
705 }
706
707 fn kind(&self) -> ContainerType {
708 ContainerType::MovableList
709 }
710
711 fn to_handler(&self) -> Handler {
712 Handler::MovableList(self.clone())
713 }
714
715 fn from_handler(h: Handler) -> Option<Self> {
716 match h {
717 Handler::MovableList(x) => Some(x),
718 _ => None,
719 }
720 }
721
722 fn attach(
723 &self,
724 txn: &mut Transaction,
725 parent: &BasicHandler,
726 self_id: ContainerID,
727 ) -> LoroResult<Self> {
728 match &self.inner {
729 MaybeDetached::Detached(l) => {
730 let mut l = l.lock();
731 let inner = create_handler(parent, self_id);
732 let list = inner.into_movable_list().unwrap();
733 for (index, v) in l.value.iter().enumerate() {
734 match v {
735 ValueOrHandler::Value(v) => {
736 list.insert_with_txn(txn, index, v.clone())?;
737 }
738 ValueOrHandler::Handler(h) => {
739 list.insert_container_with_txn(txn, index, h.clone())?;
740 }
741 }
742 }
743 l.attached = list.attached_handler().cloned();
744 Ok(list)
745 }
746 MaybeDetached::Attached(a) => {
747 let new_inner = create_handler(a, self_id);
748 let ans = new_inner.into_movable_list().unwrap();
749
750 for (i, v) in self.get_value().into_list().unwrap().iter().enumerate() {
751 if let LoroValue::Container(id) = v {
752 ans.insert_container_with_txn(txn, i, create_handler(a, id.clone()))?;
753 } else {
754 ans.insert_with_txn(txn, i, v.clone())?;
755 }
756 }
757
758 Ok(ans)
759 }
760 }
761 }
762
763 fn get_attached(&self) -> Option<Self> {
764 match &self.inner {
765 MaybeDetached::Detached(d) => d.lock().attached.clone().map(|x| Self {
766 inner: MaybeDetached::Attached(x),
767 }),
768 MaybeDetached::Attached(_a) => Some(self.clone()),
769 }
770 }
771
772 fn doc(&self) -> Option<LoroDoc> {
773 match &self.inner {
774 MaybeDetached::Detached(_) => None,
775 MaybeDetached::Attached(a) => Some(a.doc()),
776 }
777 }
778}
779
780impl std::fmt::Debug for MovableListHandler {
781 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
782 write!(f, "MovableListHandler {}", self.id())
783 }
784}
785
786impl std::fmt::Debug for ListHandler {
787 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
788 match &self.inner {
789 MaybeDetached::Detached(_) => write!(f, "ListHandler Detached"),
790 MaybeDetached::Attached(a) => write!(f, "ListHandler {}", a.id),
791 }
792 }
793}
794
795impl HandlerTrait for ListHandler {
796 fn is_attached(&self) -> bool {
797 self.inner.is_attached()
798 }
799
800 fn attached_handler(&self) -> Option<&BasicHandler> {
801 self.inner.attached_handler()
802 }
803
804 fn get_value(&self) -> LoroValue {
805 match &self.inner {
806 MaybeDetached::Detached(a) => {
807 let a = a.lock();
808 LoroValue::List(a.value.iter().map(|v| v.to_value()).collect())
809 }
810 MaybeDetached::Attached(a) => a.get_value(),
811 }
812 }
813
814 fn get_deep_value(&self) -> LoroValue {
815 match &self.inner {
816 MaybeDetached::Detached(a) => {
817 let a = a.lock();
818 LoroValue::List(a.value.iter().map(|v| v.to_deep_value()).collect())
819 }
820 MaybeDetached::Attached(a) => a.get_deep_value(),
821 }
822 }
823
824 fn kind(&self) -> ContainerType {
825 ContainerType::List
826 }
827
828 fn to_handler(&self) -> Handler {
829 Handler::List(self.clone())
830 }
831
832 fn attach(
833 &self,
834 txn: &mut Transaction,
835 parent: &BasicHandler,
836 self_id: ContainerID,
837 ) -> LoroResult<Self> {
838 match &self.inner {
839 MaybeDetached::Detached(l) => {
840 let mut l = l.lock();
841 let inner = create_handler(parent, self_id);
842 let list = inner.into_list().unwrap();
843 for (index, v) in l.value.iter().enumerate() {
844 match v {
845 ValueOrHandler::Value(v) => {
846 list.insert_with_txn(txn, index, v.clone())?;
847 }
848 ValueOrHandler::Handler(h) => {
849 list.insert_container_with_txn(txn, index, h.clone())?;
850 }
851 }
852 }
853 l.attached = list.attached_handler().cloned();
854 Ok(list)
855 }
856 MaybeDetached::Attached(a) => {
857 let new_inner = create_handler(a, self_id);
858 let ans = new_inner.into_list().unwrap();
859
860 for (i, v) in self.get_value().into_list().unwrap().iter().enumerate() {
861 if let LoroValue::Container(id) = v {
862 ans.insert_container_with_txn(txn, i, create_handler(a, id.clone()))?;
863 } else {
864 ans.insert_with_txn(txn, i, v.clone())?;
865 }
866 }
867
868 Ok(ans)
869 }
870 }
871 }
872
873 fn get_attached(&self) -> Option<Self> {
874 match &self.inner {
875 MaybeDetached::Detached(d) => d.lock().attached.clone().map(|x| Self {
876 inner: MaybeDetached::Attached(x),
877 }),
878 MaybeDetached::Attached(_a) => Some(self.clone()),
879 }
880 }
881
882 fn from_handler(h: Handler) -> Option<Self> {
883 match h {
884 Handler::List(x) => Some(x),
885 _ => None,
886 }
887 }
888
889 fn doc(&self) -> Option<LoroDoc> {
890 match &self.inner {
891 MaybeDetached::Detached(_) => None,
892 MaybeDetached::Attached(a) => Some(a.doc()),
893 }
894 }
895}
896
897#[derive(Clone)]
898pub struct UnknownHandler {
899 inner: BasicHandler,
900}
901
902impl Debug for UnknownHandler {
903 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
904 write!(f, "UnknownHandler")
905 }
906}
907
908impl UnknownHandler {
909 pub fn is_deleted(&self) -> bool {
910 self.inner.is_deleted()
911 }
912}
913
914impl HandlerTrait for UnknownHandler {
915 fn is_attached(&self) -> bool {
916 true
917 }
918
919 fn attached_handler(&self) -> Option<&BasicHandler> {
920 Some(&self.inner)
921 }
922
923 fn get_value(&self) -> LoroValue {
924 LoroValue::Null
928 }
929
930 fn get_deep_value(&self) -> LoroValue {
931 LoroValue::Null
932 }
933
934 fn kind(&self) -> ContainerType {
935 self.inner.id.container_type()
936 }
937
938 fn to_handler(&self) -> Handler {
939 Handler::Unknown(self.clone())
940 }
941
942 fn from_handler(h: Handler) -> Option<Self> {
943 match h {
944 Handler::Unknown(x) => Some(x),
945 _ => None,
946 }
947 }
948
949 fn attach(
950 &self,
951 _txn: &mut Transaction,
952 _parent: &BasicHandler,
953 self_id: ContainerID,
954 ) -> LoroResult<Self> {
955 let new_inner = create_handler(&self.inner, self_id);
956 let ans = new_inner.into_unknown().unwrap();
957 Ok(ans)
958 }
959
960 fn get_attached(&self) -> Option<Self> {
961 Some(self.clone())
962 }
963
964 fn doc(&self) -> Option<LoroDoc> {
965 Some(self.inner.doc())
966 }
967}
968
969#[derive(Clone, EnumAsInner, Debug)]
970pub enum Handler {
971 Text(TextHandler),
972 Map(MapHandler),
973 List(ListHandler),
974 MovableList(MovableListHandler),
975 Tree(TreeHandler),
976 #[cfg(feature = "counter")]
977 Counter(counter::CounterHandler),
978 Unknown(UnknownHandler),
979}
980
981impl HandlerTrait for Handler {
982 fn is_attached(&self) -> bool {
983 match self {
984 Self::Text(x) => x.is_attached(),
985 Self::Map(x) => x.is_attached(),
986 Self::List(x) => x.is_attached(),
987 Self::Tree(x) => x.is_attached(),
988 Self::MovableList(x) => x.is_attached(),
989 #[cfg(feature = "counter")]
990 Self::Counter(x) => x.is_attached(),
991 Self::Unknown(x) => x.is_attached(),
992 }
993 }
994
995 fn attached_handler(&self) -> Option<&BasicHandler> {
996 match self {
997 Self::Text(x) => x.attached_handler(),
998 Self::Map(x) => x.attached_handler(),
999 Self::List(x) => x.attached_handler(),
1000 Self::MovableList(x) => x.attached_handler(),
1001 Self::Tree(x) => x.attached_handler(),
1002 #[cfg(feature = "counter")]
1003 Self::Counter(x) => x.attached_handler(),
1004 Self::Unknown(x) => x.attached_handler(),
1005 }
1006 }
1007
1008 fn get_value(&self) -> LoroValue {
1009 match self {
1010 Self::Text(x) => x.get_value(),
1011 Self::Map(x) => x.get_value(),
1012 Self::List(x) => x.get_value(),
1013 Self::MovableList(x) => x.get_value(),
1014 Self::Tree(x) => x.get_value(),
1015 #[cfg(feature = "counter")]
1016 Self::Counter(x) => x.get_value(),
1017 Self::Unknown(x) => x.get_value(),
1018 }
1019 }
1020
1021 fn get_deep_value(&self) -> LoroValue {
1022 match self {
1023 Self::Text(x) => x.get_deep_value(),
1024 Self::Map(x) => x.get_deep_value(),
1025 Self::List(x) => x.get_deep_value(),
1026 Self::MovableList(x) => x.get_deep_value(),
1027 Self::Tree(x) => x.get_deep_value(),
1028 #[cfg(feature = "counter")]
1029 Self::Counter(x) => x.get_deep_value(),
1030 Self::Unknown(x) => x.get_deep_value(),
1031 }
1032 }
1033
1034 fn kind(&self) -> ContainerType {
1035 match self {
1036 Self::Text(x) => x.kind(),
1037 Self::Map(x) => x.kind(),
1038 Self::List(x) => x.kind(),
1039 Self::MovableList(x) => x.kind(),
1040 Self::Tree(x) => x.kind(),
1041 #[cfg(feature = "counter")]
1042 Self::Counter(x) => x.kind(),
1043 Self::Unknown(x) => x.kind(),
1044 }
1045 }
1046
1047 fn to_handler(&self) -> Handler {
1048 match self {
1049 Self::Text(x) => x.to_handler(),
1050 Self::Map(x) => x.to_handler(),
1051 Self::List(x) => x.to_handler(),
1052 Self::MovableList(x) => x.to_handler(),
1053 Self::Tree(x) => x.to_handler(),
1054 #[cfg(feature = "counter")]
1055 Self::Counter(x) => x.to_handler(),
1056 Self::Unknown(x) => x.to_handler(),
1057 }
1058 }
1059
1060 fn attach(
1061 &self,
1062 txn: &mut Transaction,
1063 parent: &BasicHandler,
1064 self_id: ContainerID,
1065 ) -> LoroResult<Self> {
1066 match self {
1067 Self::Text(x) => Ok(Handler::Text(x.attach(txn, parent, self_id)?)),
1068 Self::Map(x) => Ok(Handler::Map(x.attach(txn, parent, self_id)?)),
1069 Self::List(x) => Ok(Handler::List(x.attach(txn, parent, self_id)?)),
1070 Self::MovableList(x) => Ok(Handler::MovableList(x.attach(txn, parent, self_id)?)),
1071 Self::Tree(x) => Ok(Handler::Tree(x.attach(txn, parent, self_id)?)),
1072 #[cfg(feature = "counter")]
1073 Self::Counter(x) => Ok(Handler::Counter(x.attach(txn, parent, self_id)?)),
1074 Self::Unknown(x) => Ok(Handler::Unknown(x.attach(txn, parent, self_id)?)),
1075 }
1076 }
1077
1078 fn get_attached(&self) -> Option<Self> {
1079 match self {
1080 Self::Text(x) => x.get_attached().map(Handler::Text),
1081 Self::Map(x) => x.get_attached().map(Handler::Map),
1082 Self::List(x) => x.get_attached().map(Handler::List),
1083 Self::MovableList(x) => x.get_attached().map(Handler::MovableList),
1084 Self::Tree(x) => x.get_attached().map(Handler::Tree),
1085 #[cfg(feature = "counter")]
1086 Self::Counter(x) => x.get_attached().map(Handler::Counter),
1087 Self::Unknown(x) => x.get_attached().map(Handler::Unknown),
1088 }
1089 }
1090
1091 fn from_handler(h: Handler) -> Option<Self> {
1092 Some(h)
1093 }
1094
1095 fn doc(&self) -> Option<LoroDoc> {
1096 match self {
1097 Self::Text(x) => x.doc(),
1098 Self::Map(x) => x.doc(),
1099 Self::List(x) => x.doc(),
1100 Self::MovableList(x) => x.doc(),
1101 Self::Tree(x) => x.doc(),
1102 #[cfg(feature = "counter")]
1103 Self::Counter(x) => x.doc(),
1104 Self::Unknown(x) => x.doc(),
1105 }
1106 }
1107}
1108
1109impl Handler {
1110 fn apply_map_container_diff_value(
1111 map: &MapHandler,
1112 key: &str,
1113 old_id: ContainerID,
1114 on_container_remap: &mut dyn FnMut(ContainerID, ContainerID),
1115 ) -> LoroResult<()> {
1116 if old_id.is_mergeable() {
1117 let parent_id = map.id();
1118 let kind = old_id.container_type();
1119 let new_id = ContainerID::new_mergeable(&parent_id, key, kind);
1120 let marker = loro_common::mergeable_marker(&parent_id, key, kind);
1121 map.insert_without_skipping(key, marker)?;
1122 on_container_remap(old_id, new_id);
1123 return Ok(());
1124 }
1125
1126 let new_h = map.insert_container(key, Handler::new_unattached(old_id.container_type()))?;
1127 let new_id = new_h.id();
1128 on_container_remap(old_id, new_id);
1129 Ok(())
1130 }
1131
1132 pub(crate) fn new_attached(id: ContainerID, doc: LoroDoc) -> Self {
1133 let kind = id.container_type();
1134 let handler = BasicHandler {
1135 container_idx: doc.arena.register_container(&id),
1136 id,
1137 doc,
1138 };
1139
1140 match kind {
1141 ContainerType::Map => Self::Map(MapHandler {
1142 inner: handler.into(),
1143 }),
1144 ContainerType::List => Self::List(ListHandler {
1145 inner: handler.into(),
1146 }),
1147 ContainerType::Tree => Self::Tree(TreeHandler {
1148 inner: handler.into(),
1149 }),
1150 ContainerType::Text => Self::Text(TextHandler {
1151 inner: handler.into(),
1152 }),
1153 ContainerType::MovableList => Self::MovableList(MovableListHandler {
1154 inner: handler.into(),
1155 }),
1156 #[cfg(feature = "counter")]
1157 ContainerType::Counter => Self::Counter(counter::CounterHandler {
1158 inner: handler.into(),
1159 }),
1160 ContainerType::Unknown(_) => Self::Unknown(UnknownHandler { inner: handler }),
1161 }
1162 }
1163
1164 #[allow(unused)]
1165 pub(crate) fn new_unattached(kind: ContainerType) -> Self {
1166 match kind {
1167 ContainerType::Text => Self::Text(TextHandler::new_detached()),
1168 ContainerType::Map => Self::Map(MapHandler::new_detached()),
1169 ContainerType::List => Self::List(ListHandler::new_detached()),
1170 ContainerType::Tree => Self::Tree(TreeHandler::new_detached()),
1171 ContainerType::MovableList => Self::MovableList(MovableListHandler::new_detached()),
1172 #[cfg(feature = "counter")]
1173 ContainerType::Counter => Self::Counter(counter::CounterHandler::new_detached()),
1174 ContainerType::Unknown(_) => unreachable!(),
1175 }
1176 }
1177
1178 pub fn id(&self) -> ContainerID {
1179 match self {
1180 Self::Map(x) => x.id(),
1181 Self::List(x) => x.id(),
1182 Self::Text(x) => x.id(),
1183 Self::Tree(x) => x.id(),
1184 Self::MovableList(x) => x.id(),
1185 #[cfg(feature = "counter")]
1186 Self::Counter(x) => x.id(),
1187 Self::Unknown(x) => x.id(),
1188 }
1189 }
1190
1191 pub(crate) fn container_idx(&self) -> ContainerIdx {
1192 match self {
1193 Self::Map(x) => x.idx(),
1194 Self::List(x) => x.idx(),
1195 Self::Text(x) => x.idx(),
1196 Self::Tree(x) => x.idx(),
1197 Self::MovableList(x) => x.idx(),
1198 #[cfg(feature = "counter")]
1199 Self::Counter(x) => x.idx(),
1200 Self::Unknown(x) => x.idx(),
1201 }
1202 }
1203
1204 pub fn c_type(&self) -> ContainerType {
1205 match self {
1206 Self::Map(_) => ContainerType::Map,
1207 Self::List(_) => ContainerType::List,
1208 Self::Text(_) => ContainerType::Text,
1209 Self::Tree(_) => ContainerType::Tree,
1210 Self::MovableList(_) => ContainerType::MovableList,
1211 #[cfg(feature = "counter")]
1212 Self::Counter(_) => ContainerType::Counter,
1213 Self::Unknown(x) => x.id().container_type(),
1214 }
1215 }
1216
1217 fn get_deep_value(&self) -> LoroValue {
1218 match self {
1219 Self::Map(x) => x.get_deep_value(),
1220 Self::List(x) => x.get_deep_value(),
1221 Self::MovableList(x) => x.get_deep_value(),
1222 Self::Text(x) => x.get_deep_value(),
1223 Self::Tree(x) => x.get_deep_value(),
1224 #[cfg(feature = "counter")]
1225 Self::Counter(x) => x.get_deep_value(),
1226 Self::Unknown(x) => x.get_deep_value(),
1227 }
1228 }
1229
1230 pub(crate) fn apply_diff(
1231 &self,
1232 diff: Diff,
1233 container_remap: &mut FxHashMap<ContainerID, ContainerID>,
1234 ) -> LoroResult<()> {
1235 let on_container_remap = &mut |old_id, new_id| {
1238 if old_id != new_id {
1239 container_remap.insert(old_id, new_id);
1240 }
1241 };
1242 match self {
1243 Self::Map(x) => {
1244 let diff = match diff {
1245 crate::event::Diff::Map(d) => d,
1246 _ => {
1247 return Err(LoroError::DecodeError(
1248 "Invalid diff type for map container".into(),
1249 ));
1250 }
1251 };
1252 for (key, value) in diff.updated.into_iter() {
1253 match value.value {
1254 Some(ValueOrHandler::Handler(h)) => {
1255 Self::apply_map_container_diff_value(
1256 x,
1257 &key,
1258 h.id(),
1259 on_container_remap,
1260 )?;
1261 }
1262 Some(ValueOrHandler::Value(LoroValue::Container(old_id))) => {
1263 Self::apply_map_container_diff_value(
1264 x,
1265 &key,
1266 old_id,
1267 on_container_remap,
1268 )?;
1269 }
1270 Some(ValueOrHandler::Value(v)) => {
1271 x.insert_without_skipping(&key, v)?;
1272 }
1273 None => {
1274 x.delete(&key)?;
1275 }
1276 }
1277 }
1278 }
1279 Self::Text(x) => {
1280 let delta = match diff {
1281 crate::event::Diff::Text(d) => d,
1282 _ => {
1283 return Err(LoroError::DecodeError(
1284 "Invalid diff type for text container".into(),
1285 ));
1286 }
1287 };
1288 x.apply_delta(&TextDelta::from_text_diff(delta.iter()))?;
1289 }
1290 Self::List(x) => {
1291 let delta = match diff {
1292 crate::event::Diff::List(d) => d,
1293 _ => {
1294 return Err(LoroError::DecodeError(
1295 "Invalid diff type for list container".into(),
1296 ));
1297 }
1298 };
1299 x.apply_delta(delta, on_container_remap)?;
1300 }
1301 Self::MovableList(x) => {
1302 let delta = match diff {
1303 crate::event::Diff::List(d) => d,
1304 _ => {
1305 return Err(LoroError::DecodeError(
1306 "Invalid diff type for movable list container".into(),
1307 ));
1308 }
1309 };
1310 x.apply_delta(delta, container_remap)?;
1311 }
1312 Self::Tree(x) => {
1313 fn remap_tree_id(
1314 id: &mut TreeID,
1315 container_remap: &FxHashMap<ContainerID, ContainerID>,
1316 ) {
1317 let mut remapped = false;
1318 let mut map_id = id.associated_meta_container();
1319 while let Some(rid) = container_remap.get(&map_id) {
1320 remapped = true;
1321 map_id = rid.clone();
1322 }
1323 if remapped {
1324 *id = TreeID::new(
1325 *map_id.as_normal().unwrap().0,
1326 *map_id.as_normal().unwrap().1,
1327 )
1328 }
1329 }
1330 let tree_diff = match diff {
1331 crate::event::Diff::Tree(d) => d,
1332 _ => {
1333 return Err(LoroError::DecodeError(
1334 "Invalid diff type for tree container".into(),
1335 ));
1336 }
1337 };
1338 for diff in tree_diff.diff {
1339 let mut target = diff.target;
1340 match diff.action {
1341 TreeExternalDiff::Create {
1342 mut parent,
1343 index: _,
1344 position,
1345 } => {
1346 if let TreeParentId::Node(p) = &mut parent {
1347 remap_tree_id(p, container_remap)
1348 }
1349 remap_tree_id(&mut target, container_remap);
1350 if !x.is_node_unexist(&target) && !x.is_node_deleted(&target)? {
1351 x.move_at_with_target_for_apply_diff(parent, position, target)?;
1364 } else {
1365 let new_target = x.__internal__next_tree_id();
1366 if x.create_at_with_target_for_apply_diff(
1367 parent, position, new_target,
1368 )? {
1369 container_remap.insert(
1370 target.associated_meta_container(),
1371 new_target.associated_meta_container(),
1372 );
1373 }
1374 }
1375 }
1376 TreeExternalDiff::Move {
1377 mut parent,
1378 index: _,
1379 position,
1380 old_parent: _,
1381 old_index: _,
1382 } => {
1383 if let TreeParentId::Node(p) = &mut parent {
1384 remap_tree_id(p, container_remap)
1385 }
1386 remap_tree_id(&mut target, container_remap);
1387 if x.is_node_unexist(&target) || x.is_node_deleted(&target)? {
1389 let new_target = x.__internal__next_tree_id();
1391 if x.create_at_with_target_for_apply_diff(
1392 parent, position, new_target,
1393 )? {
1394 container_remap.insert(
1395 target.associated_meta_container(),
1396 new_target.associated_meta_container(),
1397 );
1398 }
1399 } else {
1400 x.move_at_with_target_for_apply_diff(parent, position, target)?;
1401 }
1402 }
1403 TreeExternalDiff::Delete { .. } => {
1404 remap_tree_id(&mut target, container_remap);
1405 if !x.is_node_deleted(&target)? {
1406 x.delete(target)?;
1407 }
1408 }
1409 }
1410 }
1411 }
1412 #[cfg(feature = "counter")]
1413 Self::Counter(x) => {
1414 let delta = match diff {
1415 crate::event::Diff::Counter(d) => d,
1416 _ => {
1417 return Err(LoroError::DecodeError(
1418 "Invalid diff type for counter container".into(),
1419 ));
1420 }
1421 };
1422 x.increment(delta)?;
1423 }
1424 Self::Unknown(_) => {
1425 }
1427 }
1428
1429 Ok(())
1430 }
1431
1432 pub fn clear(&self) -> LoroResult<()> {
1433 match self {
1434 Handler::Text(text_handler) => text_handler.clear(),
1435 Handler::Map(map_handler) => map_handler.clear(),
1436 Handler::List(list_handler) => list_handler.clear(),
1437 Handler::MovableList(movable_list_handler) => movable_list_handler.clear(),
1438 Handler::Tree(tree_handler) => tree_handler.clear(),
1439 #[cfg(feature = "counter")]
1440 Handler::Counter(counter_handler) => counter_handler.clear(),
1441 Handler::Unknown(_unknown_handler) => Ok(()),
1442 }
1443 }
1444}
1445
1446#[derive(Clone, EnumAsInner, Debug)]
1447pub enum ValueOrHandler {
1448 Value(LoroValue),
1449 Handler(Handler),
1450}
1451
1452impl ValueOrHandler {
1453 pub(crate) fn from_value(value: LoroValue, doc: &Arc<LoroDocInner>) -> Self {
1454 if let LoroValue::Container(c) = value {
1455 ValueOrHandler::Handler(Handler::new_attached(c, LoroDoc::from_inner(doc.clone())))
1456 } else {
1457 ValueOrHandler::Value(value)
1458 }
1459 }
1460
1461 pub(crate) fn to_value(&self) -> LoroValue {
1462 match self {
1463 Self::Value(v) => v.clone(),
1464 Self::Handler(h) => LoroValue::Container(h.id().clone()),
1465 }
1466 }
1467
1468 pub(crate) fn to_deep_value(&self) -> LoroValue {
1469 match self {
1470 Self::Value(v) => v.clone(),
1471 Self::Handler(h) => h.get_deep_value(),
1472 }
1473 }
1474}
1475
1476impl From<LoroValue> for ValueOrHandler {
1477 fn from(value: LoroValue) -> Self {
1478 ValueOrHandler::Value(value)
1479 }
1480}
1481
1482impl TextHandler {
1483 pub fn new_detached() -> Self {
1488 Self {
1489 inner: MaybeDetached::new_detached(RichtextState::default()),
1490 }
1491 }
1492
1493 pub fn version_id(&self) -> Option<usize> {
1497 match &self.inner {
1498 MaybeDetached::Detached(_) => None,
1499 MaybeDetached::Attached(a) => {
1500 Some(a.with_state(|state| state.as_richtext_state_mut().unwrap().get_version_id()))
1501 }
1502 }
1503 }
1504
1505 pub fn get_richtext_value(&self) -> LoroValue {
1506 match &self.inner {
1507 MaybeDetached::Detached(t) => {
1508 let t = t.lock();
1509 t.value.get_richtext_value()
1510 }
1511 MaybeDetached::Attached(a) => {
1512 a.with_state(|state| state.as_richtext_state_mut().unwrap().get_richtext_value())
1513 }
1514 }
1515 }
1516
1517 pub fn is_empty(&self) -> bool {
1518 match &self.inner {
1519 MaybeDetached::Detached(t) => t.lock().value.is_empty(),
1520 MaybeDetached::Attached(a) if a.has_decoded_state() => {
1521 a.with_state(|state| state.as_richtext_state_mut().unwrap().is_empty())
1522 }
1523 MaybeDetached::Attached(a) => a.get_value().as_string().unwrap().is_empty(),
1524 }
1525 }
1526
1527 pub fn len_utf8(&self) -> usize {
1528 match &self.inner {
1529 MaybeDetached::Detached(t) => {
1530 let t = t.lock();
1531 t.value.len_utf8()
1532 }
1533 MaybeDetached::Attached(a) => {
1534 a.with_doc_state(|state| state.get_text_len(a.container_idx, PosType::Bytes))
1535 }
1536 }
1537 }
1538
1539 pub fn len_utf16(&self) -> usize {
1540 match &self.inner {
1541 MaybeDetached::Detached(t) => {
1542 let t = t.lock();
1543 t.value.len_utf16()
1544 }
1545 MaybeDetached::Attached(a) => {
1546 a.with_doc_state(|state| state.get_text_len(a.container_idx, PosType::Utf16))
1547 }
1548 }
1549 }
1550
1551 pub fn len_unicode(&self) -> usize {
1552 match &self.inner {
1553 MaybeDetached::Detached(t) => {
1554 let t = t.lock();
1555 t.value.len_unicode()
1556 }
1557 MaybeDetached::Attached(a) => {
1558 a.with_doc_state(|state| state.get_text_len(a.container_idx, PosType::Unicode))
1559 }
1560 }
1561 }
1562
1563 pub fn len_event(&self) -> usize {
1566 if cfg!(feature = "wasm") {
1567 self.len_utf16()
1568 } else {
1569 self.len_unicode()
1570 }
1571 }
1572
1573 fn len(&self, pos_type: PosType) -> usize {
1574 match &self.inner {
1575 MaybeDetached::Detached(t) => t.lock().value.len(pos_type),
1576 MaybeDetached::Attached(a) => {
1577 a.with_doc_state(|state| state.get_text_len(a.container_idx, pos_type))
1578 }
1579 }
1580 }
1581
1582 fn validate_text_boundary(&self, pos: usize, pos_type: PosType) -> LoroResult<()> {
1583 let err = match pos_type {
1584 PosType::Bytes => Some(LoroError::UTF8InUnicodeCodePoint { pos }),
1585 PosType::Utf16 => Some(LoroError::UTF16InUnicodeCodePoint { pos }),
1586 PosType::Event if cfg!(feature = "wasm") => {
1587 Some(LoroError::UTF16InUnicodeCodePoint { pos })
1588 }
1589 _ => None,
1590 };
1591
1592 let Some(err) = err else {
1593 return Ok(());
1594 };
1595
1596 let len = self.len(pos_type);
1597 if pos > len {
1598 return Ok(());
1599 }
1600
1601 if self.all_text_positions_are_boundaries(pos_type, len) {
1602 return Ok(());
1603 }
1604
1605 let Some(unicode_pos) = self.convert_pos(pos, pos_type, PosType::Unicode) else {
1606 return Err(err);
1607 };
1608 if self.convert_pos(unicode_pos, PosType::Unicode, pos_type) != Some(pos) {
1609 return Err(err);
1610 }
1611
1612 Ok(())
1613 }
1614
1615 fn all_text_positions_are_boundaries(&self, pos_type: PosType, len: usize) -> bool {
1616 match pos_type {
1617 PosType::Bytes => len == self.len_unicode(),
1618 PosType::Utf16 => len == self.len_unicode(),
1619 PosType::Event if cfg!(feature = "wasm") => len == self.len_unicode(),
1620 _ => false,
1621 }
1622 }
1623
1624 pub fn diagnose(&self) {
1625 match &self.inner {
1626 MaybeDetached::Detached(t) => {
1627 let t = t.lock();
1628 t.value.diagnose();
1629 }
1630 MaybeDetached::Attached(a) => {
1631 a.with_state(|state| state.as_richtext_state_mut().unwrap().diagnose());
1632 }
1633 }
1634 }
1635
1636 pub fn iter(&self, mut callback: impl FnMut(&str) -> bool) {
1637 let spans: Vec<String> = match &self.inner {
1639 MaybeDetached::Detached(t) => {
1640 let t = t.lock();
1641 t.value
1642 .iter()
1643 .map(|span| span.text.as_str().to_owned())
1644 .collect()
1645 }
1646 MaybeDetached::Attached(a) => a.with_state(|state| {
1647 let mut spans = Vec::new();
1648 state.as_richtext_state_mut().unwrap().iter(|span| {
1649 spans.push(span.to_owned());
1650 true
1651 });
1652 spans
1653 }),
1654 };
1655
1656 for span in spans {
1657 if !callback(span.as_str()) {
1658 return;
1659 }
1660 }
1661 }
1662
1663 pub fn char_at(&self, pos: usize, pos_type: PosType) -> LoroResult<char> {
1665 let len = self.len(pos_type);
1666 if pos >= len {
1667 return Err(LoroError::OutOfBound {
1668 pos,
1669 len,
1670 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1671 });
1672 }
1673 if let Ok(c) = match &self.inner {
1674 MaybeDetached::Detached(t) => {
1675 let t = t.lock();
1676 let event_pos = match pos_type {
1677 PosType::Event => pos,
1678 _ => t.value.index_to_event_index(pos, pos_type),
1679 };
1680 t.value.get_char_by_event_index(event_pos)
1681 }
1682 MaybeDetached::Attached(a) if a.has_decoded_state() || pos_type == PosType::Entity => a
1683 .with_state(|state| {
1684 let state = state.as_richtext_state_mut().unwrap();
1685 let event_pos = match pos_type {
1686 PosType::Event => pos,
1687 _ => state.index_to_event_index(pos, pos_type),
1688 };
1689 state.get_char_by_event_index(event_pos)
1690 }),
1691 MaybeDetached::Attached(a) => {
1692 return text_char_at(a.get_value().as_string().unwrap(), pos, pos_type);
1693 }
1694 } {
1695 Ok(c)
1696 } else {
1697 Err(LoroError::OutOfBound {
1698 pos,
1699 len,
1700 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1701 })
1702 }
1703 }
1704
1705 pub fn slice(
1711 &self,
1712 start_index: usize,
1713 end_index: usize,
1714 pos_type: PosType,
1715 ) -> LoroResult<String> {
1716 self.slice_with_pos_type(start_index, end_index, pos_type)
1717 }
1718
1719 pub fn slice_utf16(&self, start_index: usize, end_index: usize) -> LoroResult<String> {
1720 self.slice(start_index, end_index, PosType::Utf16)
1721 }
1722
1723 fn slice_with_pos_type(
1724 &self,
1725 start_index: usize,
1726 end_index: usize,
1727 pos_type: PosType,
1728 ) -> LoroResult<String> {
1729 if end_index < start_index {
1730 return Err(LoroError::EndIndexLessThanStartIndex {
1731 start: start_index,
1732 end: end_index,
1733 });
1734 }
1735 if start_index == end_index {
1736 return Ok(String::new());
1737 }
1738
1739 let info = || format!("Position: {}:{}", file!(), line!()).into_boxed_str();
1740 match &self.inner {
1741 MaybeDetached::Detached(t) => {
1742 let t = t.lock();
1743 let len = t.value.len(pos_type);
1744 if end_index > len {
1745 return Err(LoroError::OutOfBound {
1746 pos: end_index,
1747 len,
1748 info: info(),
1749 });
1750 }
1751 let (start, end) = match pos_type {
1752 PosType::Event => (start_index, end_index),
1753 _ => (
1754 t.value.index_to_event_index(start_index, pos_type),
1755 t.value.index_to_event_index(end_index, pos_type),
1756 ),
1757 };
1758 t.value.get_text_slice_by_event_index(start, end - start)
1759 }
1760 MaybeDetached::Attached(a) if a.has_decoded_state() || pos_type == PosType::Entity => a
1761 .with_state(|state| {
1762 let state = state.as_richtext_state_mut().unwrap();
1763 let len = state.len(pos_type);
1764 if end_index > len {
1765 return Err(LoroError::OutOfBound {
1766 pos: end_index,
1767 len,
1768 info: info(),
1769 });
1770 }
1771 let (start, end) = match pos_type {
1772 PosType::Event => (start_index, end_index),
1773 _ => (
1774 state.index_to_event_index(start_index, pos_type),
1775 state.index_to_event_index(end_index, pos_type),
1776 ),
1777 };
1778 state.get_text_slice_by_event_index(start, end - start)
1779 }),
1780 MaybeDetached::Attached(a) => text_slice(
1781 a.get_value().as_string().unwrap(),
1782 start_index,
1783 end_index,
1784 pos_type,
1785 )
1786 .map_err(|err| match err {
1787 LoroError::OutOfBound { pos, len, .. } => LoroError::OutOfBound {
1788 pos,
1789 len,
1790 info: info(),
1791 },
1792 err => err,
1793 }),
1794 }
1795 }
1796
1797 pub fn slice_delta(
1798 &self,
1799 start_index: usize,
1800 end_index: usize,
1801 pos_type: PosType,
1802 ) -> LoroResult<Vec<TextDelta>> {
1803 if end_index < start_index {
1804 return Err(LoroError::EndIndexLessThanStartIndex {
1805 start: start_index,
1806 end: end_index,
1807 });
1808 }
1809 if start_index == end_index {
1810 return Ok(Vec::new());
1811 }
1812
1813 let len = self.len(pos_type);
1814 if end_index > len {
1815 return Err(LoroError::OutOfBound {
1816 pos: end_index,
1817 len,
1818 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1819 });
1820 }
1821 self.validate_text_boundary(start_index, pos_type)?;
1822 self.validate_text_boundary(end_index, pos_type)?;
1823
1824 match &self.inner {
1825 MaybeDetached::Detached(t) => {
1826 let t = t.lock();
1827 let ans = t.value.slice_delta(start_index, end_index, pos_type)?;
1828 Ok(ans
1829 .into_iter()
1830 .map(|(s, a)| TextDelta::Insert {
1831 insert: s,
1832 attributes: a.to_option_map_without_null_value(),
1833 })
1834 .collect())
1835 }
1836 MaybeDetached::Attached(a) => a.with_state(|state| {
1837 let ans = state.as_richtext_state_mut().unwrap().slice_delta(
1838 start_index,
1839 end_index,
1840 pos_type,
1841 )?;
1842 Ok(ans
1843 .into_iter()
1844 .map(|(s, a)| TextDelta::Insert {
1845 insert: s,
1846 attributes: a.to_option_map_without_null_value(),
1847 })
1848 .collect())
1849 }),
1850 }
1851 }
1852
1853 pub fn splice(&self, pos: usize, len: usize, s: &str, pos_type: PosType) -> LoroResult<String> {
1860 let end = checked_range_end(pos, len, self.len(pos_type), || {
1861 format!("Position: {}:{}", file!(), line!()).into_boxed_str()
1862 })?;
1863 let x = self.slice(pos, end, pos_type)?;
1864 self.delete(pos, len, pos_type)?;
1865 self.insert(pos, s, pos_type)?;
1866 Ok(x)
1867 }
1868
1869 pub fn splice_utf8(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1870 self.delete_utf8(pos, len)?;
1872 self.insert_utf8(pos, s)?;
1873 Ok(())
1874 }
1875
1876 pub fn splice_utf16(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1877 self.delete(pos, len, PosType::Utf16)?;
1878 self.insert(pos, s, PosType::Utf16)?;
1879 Ok(())
1880 }
1881
1882 pub fn insert(&self, pos: usize, s: &str, pos_type: PosType) -> LoroResult<()> {
1886 match &self.inner {
1887 MaybeDetached::Detached(t) => {
1888 let len = self.len(pos_type);
1889 if pos > len {
1890 return Err(LoroError::OutOfBound {
1891 pos,
1892 len,
1893 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1894 });
1895 }
1896 self.validate_text_boundary(pos, pos_type)?;
1897
1898 let mut t = t.lock();
1899 let (index, _) = t
1900 .value
1901 .get_entity_index_for_text_insert(pos, pos_type)
1902 .unwrap();
1903 t.value.insert_at_entity_index(
1904 index,
1905 BytesSlice::from_bytes(s.as_bytes()),
1906 IdFull::NONE_ID,
1907 );
1908 Ok(())
1909 }
1910 MaybeDetached::Attached(a) => {
1911 if s.is_empty() {
1912 let len = self.len(pos_type);
1913 if pos > len {
1914 return Err(LoroError::OutOfBound {
1915 pos,
1916 len,
1917 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1918 });
1919 }
1920 self.validate_text_boundary(pos, pos_type)?;
1921 return Ok(());
1922 }
1923
1924 a.with_txn(|txn| self.insert_with_txn(txn, pos, s, pos_type))
1925 }
1926 }
1927 }
1928
1929 pub fn insert_utf8(&self, pos: usize, s: &str) -> LoroResult<()> {
1930 self.insert(pos, s, PosType::Bytes)
1931 }
1932
1933 pub fn insert_utf16(&self, pos: usize, s: &str) -> LoroResult<()> {
1934 self.insert(pos, s, PosType::Utf16)
1935 }
1936
1937 pub fn insert_unicode(&self, pos: usize, s: &str) -> LoroResult<()> {
1938 self.insert(pos, s, PosType::Unicode)
1939 }
1940
1941 pub fn insert_with_txn(
1943 &self,
1944 txn: &mut Transaction,
1945 pos: usize,
1946 s: &str,
1947 pos_type: PosType,
1948 ) -> LoroResult<()> {
1949 self.insert_with_txn_and_attr(txn, pos, s, None, pos_type)?;
1950 Ok(())
1951 }
1952
1953 pub fn insert_with_txn_utf8(
1954 &self,
1955 txn: &mut Transaction,
1956 pos: usize,
1957 s: &str,
1958 ) -> LoroResult<()> {
1959 self.insert_with_txn(txn, pos, s, PosType::Bytes)
1960 }
1961
1962 pub fn delete(&self, pos: usize, len: usize, pos_type: PosType) -> LoroResult<()> {
1966 if len == 0 {
1967 return Ok(());
1968 }
1969
1970 let text_len = self.len(pos_type);
1971 let end = checked_range_end(pos, len, text_len, || {
1972 format!("Position: {}:{}", file!(), line!()).into_boxed_str()
1973 })?;
1974 self.validate_text_boundary(pos, pos_type)?;
1975 self.validate_text_boundary(end, pos_type)?;
1976
1977 match &self.inner {
1978 MaybeDetached::Detached(t) => {
1979 let mut t = t.lock();
1980 let ranges = t.value.get_text_entity_ranges(pos, len, pos_type)?;
1981 for range in ranges.iter().rev() {
1982 t.value
1983 .drain_by_entity_index(range.entity_start, range.entity_len(), None);
1984 }
1985 Ok(())
1986 }
1987 MaybeDetached::Attached(a) => {
1988 a.with_txn(|txn| self.delete_with_txn(txn, pos, len, pos_type))
1989 }
1990 }
1991 }
1992
1993 pub fn delete_utf8(&self, pos: usize, len: usize) -> LoroResult<()> {
1994 self.delete(pos, len, PosType::Bytes)
1995 }
1996
1997 pub fn delete_utf16(&self, pos: usize, len: usize) -> LoroResult<()> {
1998 self.delete(pos, len, PosType::Utf16)
1999 }
2000
2001 pub fn delete_unicode(&self, pos: usize, len: usize) -> LoroResult<()> {
2002 self.delete(pos, len, PosType::Unicode)
2003 }
2004
2005 fn insert_with_txn_and_attr(
2008 &self,
2009 txn: &mut Transaction,
2010 pos: usize,
2011 s: &str,
2012 attr: Option<&FxHashMap<String, LoroValue>>,
2013 pos_type: PosType,
2014 ) -> Result<Vec<(InternalString, LoroValue)>, LoroError> {
2015 if s.is_empty() {
2016 return Ok(Vec::new());
2017 }
2018
2019 #[cfg(not(feature = "wasm"))]
2026 if attr.is_none() && pos_type == PosType::Unicode {
2027 let inner = self.inner.try_attached_state()?;
2028 let fast = inner.with_state(|state| {
2029 let rt = state.as_richtext_state_mut().unwrap();
2030 if rt.has_styles() {
2031 return Ok(false);
2032 }
2033 let len = rt.len_unicode();
2034 if pos > len {
2035 return Err(LoroError::OutOfBound {
2036 pos,
2037 len,
2038 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2039 });
2040 }
2041 Ok(true)
2042 })?;
2043 if fast {
2044 let unicode_len = s.chars().count();
2045 txn.apply_local_op(
2046 inner.container_idx,
2047 crate::op::RawOpContent::List(
2048 crate::container::list::list_op::ListOp::Insert {
2049 slice: ListSlice::RawStr {
2050 str: Cow::Borrowed(s),
2051 unicode_len,
2052 },
2053 pos,
2055 },
2056 ),
2057 EventHint::InsertText {
2058 pos: pos as u32,
2060 styles: StyleMeta::empty(),
2061 unicode_len: unicode_len as u32,
2062 event_len: unicode_len as u32,
2063 },
2064 &inner.doc,
2065 )?;
2066 return Ok(Vec::new());
2067 }
2068 }
2069
2070 match pos_type {
2071 PosType::Event => {
2072 if pos > self.len_event() {
2073 return Err(LoroError::OutOfBound {
2074 pos,
2075 len: self.len_event(),
2076 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2077 });
2078 }
2079 }
2080 PosType::Bytes => {
2081 if pos > self.len_utf8() {
2082 return Err(LoroError::OutOfBound {
2083 pos,
2084 len: self.len_utf8(),
2085 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2086 });
2087 }
2088 }
2089 PosType::Unicode => {
2090 if pos > self.len_unicode() {
2091 return Err(LoroError::OutOfBound {
2092 pos,
2093 len: self.len_unicode(),
2094 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2095 });
2096 }
2097 }
2098 PosType::Entity => {}
2099 PosType::Utf16 => {
2100 if pos > self.len_utf16() {
2101 return Err(LoroError::OutOfBound {
2102 pos,
2103 len: self.len_utf16(),
2104 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2105 });
2106 }
2107 }
2108 }
2109 self.validate_text_boundary(pos, pos_type)?;
2110
2111 let inner = self.inner.try_attached_state()?;
2112 let (entity_index, event_index, styles) = inner.with_state(|state| {
2113 let richtext_state = state.as_richtext_state_mut().unwrap();
2114 let ret = richtext_state.get_entity_index_for_text_insert(pos, pos_type);
2115 let (entity_index, cursor) = match ret {
2116 Err(_) => match pos_type {
2117 PosType::Bytes => {
2118 return (
2119 Err(LoroError::UTF8InUnicodeCodePoint { pos }),
2120 0,
2121 StyleMeta::empty(),
2122 );
2123 }
2124 PosType::Utf16 | PosType::Event => {
2125 return (
2126 Err(LoroError::UTF16InUnicodeCodePoint { pos }),
2127 0,
2128 StyleMeta::empty(),
2129 );
2130 }
2131 _ => unreachable!(),
2132 },
2133 Ok(x) => x,
2134 };
2135 let event_index = if let Some(cursor) = cursor {
2136 if pos_type == PosType::Event {
2137 debug_assert_eq!(
2138 richtext_state.get_event_index_by_cursor(cursor),
2139 pos,
2140 "pos={} cursor={:?} state={:#?}",
2141 pos,
2142 cursor,
2143 &richtext_state
2144 );
2145 pos
2146 } else {
2147 richtext_state.get_event_index_by_cursor(cursor)
2148 }
2149 } else {
2150 assert_eq!(entity_index, 0);
2151 0
2152 };
2153 let styles = richtext_state.get_styles_at_entity_index(entity_index);
2154 (Ok(entity_index), event_index, styles)
2155 });
2156
2157 let entity_index = match entity_index {
2158 Err(x) => return Err(x),
2159 _ => entity_index.unwrap(),
2160 };
2161
2162 let mut override_styles = Vec::new();
2163 if let Some(attr) = attr {
2164 let map: FxHashMap<_, _> = styles.iter().map(|x| (x.0.clone(), x.1.data)).collect();
2166 for (key, style) in map.iter() {
2167 match attr.get(key.deref()) {
2168 Some(v) if v == style => {}
2169 new_style_value => {
2170 let new_style_value = new_style_value.cloned().unwrap_or(LoroValue::Null);
2172 override_styles.push((key.clone(), new_style_value));
2173 }
2174 }
2175 }
2176
2177 for (key, style) in attr.iter() {
2178 let key = key.as_str().into();
2179 if !map.contains_key(&key) {
2180 override_styles.push((key, style.clone()));
2181 }
2182 }
2183 }
2184
2185 let unicode_len = s.chars().count();
2186 let event_len = if cfg!(feature = "wasm") {
2187 count_utf16_len(s.as_bytes())
2188 } else {
2189 unicode_len
2190 };
2191
2192 txn.apply_local_op(
2193 inner.container_idx,
2194 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
2195 slice: ListSlice::RawStr {
2196 str: Cow::Borrowed(s),
2197 unicode_len,
2198 },
2199 pos: entity_index,
2200 }),
2201 EventHint::InsertText {
2202 pos: event_index as u32,
2203 styles,
2204 unicode_len: unicode_len as u32,
2205 event_len: event_len as u32,
2206 },
2207 &inner.doc,
2208 )?;
2209
2210 Ok(override_styles)
2211 }
2212
2213 pub fn delete_with_txn(
2215 &self,
2216 txn: &mut Transaction,
2217 pos: usize,
2218 len: usize,
2219 pos_type: PosType,
2220 ) -> LoroResult<()> {
2221 self.delete_with_txn_inline(txn, pos, len, pos_type)
2222 }
2223
2224 fn delete_with_txn_inline(
2225 &self,
2226 txn: &mut Transaction,
2227 pos: usize,
2228 len: usize,
2229 pos_type: PosType,
2230 ) -> LoroResult<()> {
2231 if len == 0 {
2232 return Ok(());
2233 }
2234
2235 let text_len = self.len(pos_type);
2236 let end = checked_range_end(pos, len, text_len, || {
2237 format!("Position: {}:{}", file!(), line!()).into_boxed_str()
2238 })
2239 .inspect_err(|_| error!("pos={} len={} len={}", pos, len, text_len))?;
2240 self.validate_text_boundary(pos, pos_type)?;
2241 self.validate_text_boundary(end, pos_type)?;
2242
2243 let inner = self.inner.try_attached_state()?;
2244 let s = tracing::span!(tracing::Level::INFO, "delete", "pos={} len={}", pos, len);
2245 let _e = s.enter();
2246 let mut event_pos = 0;
2247 let mut event_len = 0;
2248 let ranges = inner.with_state(|state| {
2249 let richtext_state = state.as_richtext_state_mut().unwrap();
2250 let fast = cfg!(not(feature = "wasm"))
2254 && pos_type == PosType::Unicode
2255 && !richtext_state.has_styles();
2256 if fast {
2257 event_pos = pos;
2258 event_len = len;
2259 } else {
2260 event_pos = richtext_state.index_to_event_index(pos, pos_type);
2261 let event_end = richtext_state.index_to_event_index(end, pos_type);
2262 event_len = event_end - event_pos;
2263 }
2264
2265 richtext_state.get_text_entity_ranges_in_event_index_range(event_pos, event_len)
2266 })?;
2267
2268 let pos = event_pos as isize;
2270 let len = event_len as isize;
2271 let mut event_end = pos + len;
2272 for range in ranges.iter().rev() {
2273 let event_start = event_end - range.event_len as isize;
2274 txn.apply_local_op(
2275 inner.container_idx,
2276 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
2277 range.id_start,
2278 range.entity_start as isize,
2279 range.entity_len() as isize,
2280 ))),
2281 EventHint::DeleteText {
2282 span: DeleteSpan {
2283 pos: event_start,
2284 signed_len: range.event_len as isize,
2285 },
2286 unicode_len: range.entity_len(),
2287 },
2288 &inner.doc,
2289 )?;
2290 event_end = event_start;
2291 }
2292
2293 Ok(())
2294 }
2295
2296 pub fn mark(
2300 &self,
2301 start: usize,
2302 end: usize,
2303 key: impl Into<InternalString>,
2304 value: LoroValue,
2305 pos_type: PosType,
2306 ) -> LoroResult<()> {
2307 match &self.inner {
2308 MaybeDetached::Detached(t) => {
2309 let mut g = t.lock();
2310 self.mark_for_detached(&mut g.value, key, &value, start, end, pos_type)
2311 }
2312 MaybeDetached::Attached(a) => {
2313 a.with_txn(|txn| self.mark_with_txn(txn, start, end, key, value, pos_type))
2314 }
2315 }
2316 }
2317
2318 fn mark_for_detached(
2319 &self,
2320 state: &mut RichtextState,
2321 key: impl Into<InternalString>,
2322 value: &LoroValue,
2323 start: usize,
2324 end: usize,
2325 pos_type: PosType,
2326 ) -> Result<(), LoroError> {
2327 let key: InternalString = key.into();
2328 let is_delete = matches!(value, &LoroValue::Null);
2329 if start >= end {
2330 return Err(loro_common::LoroError::ArgErr(
2331 "Start must be less than end".to_string().into_boxed_str(),
2332 ));
2333 }
2334 ensure_no_regular_container_value(value)?;
2335
2336 let len = state.len(pos_type);
2337 if end > len {
2338 return Err(LoroError::OutOfBound {
2339 pos: end,
2340 len,
2341 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2342 });
2343 }
2344 let (entity_range, styles) =
2345 state.get_entity_range_and_text_styles_at_range(start..end, pos_type);
2346 let already_applied = styles.map(|styles| styles.has_key_value(&key, value));
2350 let already_applied = match already_applied {
2351 Some(applied) => applied,
2352 None => state.range_has_style_key_value(entity_range.clone(), &key, value),
2353 };
2354 if already_applied {
2355 return Ok(());
2356 }
2357
2358 let has_target_style =
2359 state.range_has_style_key(entity_range.clone(), &StyleKey::Key(key.clone()));
2360 let missing_style_key = is_delete && !has_target_style;
2361
2362 if missing_style_key {
2363 return Ok(());
2364 }
2365
2366 let style_op = Arc::new(StyleOp {
2367 lamport: 0,
2368 peer: 0,
2369 cnt: 0,
2370 key,
2371 value: value.clone(),
2372 info: if is_delete {
2374 TextStyleInfoFlag::BOLD.to_delete()
2375 } else {
2376 TextStyleInfoFlag::BOLD
2377 },
2378 });
2379 state.mark_with_entity_index(entity_range, style_op);
2380 Ok(())
2381 }
2382
2383 pub fn unmark(
2385 &self,
2386 start: usize,
2387 end: usize,
2388 key: impl Into<InternalString>,
2389 pos_type: PosType,
2390 ) -> LoroResult<()> {
2391 match &self.inner {
2392 MaybeDetached::Detached(t) => self.mark_for_detached(
2393 &mut t.lock().value,
2394 key,
2395 &LoroValue::Null,
2396 start,
2397 end,
2398 pos_type,
2399 ),
2400 MaybeDetached::Attached(a) => a.with_txn(|txn| {
2401 self.mark_with_txn(txn, start, end, key, LoroValue::Null, pos_type)
2402 }),
2403 }
2404 }
2405
2406 pub fn mark_with_txn(
2408 &self,
2409 txn: &mut Transaction,
2410 start: usize,
2411 end: usize,
2412 key: impl Into<InternalString>,
2413 value: LoroValue,
2414 pos_type: PosType,
2415 ) -> LoroResult<()> {
2416 if start >= end {
2417 return Err(loro_common::LoroError::ArgErr(
2418 "Start must be less than end".to_string().into_boxed_str(),
2419 ));
2420 }
2421 ensure_no_regular_container_value(&value)?;
2422
2423 let inner = self.inner.try_attached_state()?;
2424 let key: InternalString = key.into();
2425 let is_delete = matches!(&value, &LoroValue::Null);
2426
2427 let mut doc_state = inner.doc.state.lock();
2428 let len = doc_state.with_state_mut(inner.container_idx, |state| {
2429 state.as_richtext_state_mut().unwrap().len(pos_type)
2430 });
2431
2432 if end > len {
2433 return Err(LoroError::OutOfBound {
2434 pos: end,
2435 len,
2436 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2437 });
2438 }
2439
2440 let (entity_range, skip, missing_style_key, event_start, event_end) = doc_state
2441 .with_state_mut(inner.container_idx, |state| {
2442 let state = state.as_richtext_state_mut().unwrap();
2443 let event_start = state.index_to_event_index(start, pos_type);
2444 let event_end = state.index_to_event_index(end, pos_type);
2445 let (entity_range, styles) =
2446 state.get_entity_range_and_styles_at_range(start..end, pos_type);
2447
2448 let skip = match styles
2452 .as_ref()
2453 .map(|styles| styles.has_key_value(&key, &value))
2454 {
2455 Some(skip) => skip,
2456 None => state.has_style_key_value_in_entity_range(
2457 entity_range.clone(),
2458 &key,
2459 &value,
2460 ),
2461 };
2462 let has_target_style = state.has_style_key_in_entity_range(
2463 entity_range.clone(),
2464 &StyleKey::Key(key.clone()),
2465 );
2466 let missing_style_key = is_delete && !has_target_style;
2467
2468 (
2469 entity_range,
2470 skip,
2471 missing_style_key,
2472 event_start,
2473 event_end,
2474 )
2475 });
2476
2477 if skip || missing_style_key {
2478 return Ok(());
2479 }
2480
2481 let entity_start = entity_range.start;
2482 let entity_end = entity_range.end;
2483 let style_config = doc_state.config.text_style_config.read();
2484 let flag = if is_delete {
2485 style_config
2486 .get_style_flag_for_unmark(&key)
2487 .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2488 } else {
2489 style_config
2490 .get_style_flag(&key)
2491 .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2492 };
2493
2494 drop(style_config);
2495 drop(doc_state);
2496 txn.apply_local_op(
2497 inner.container_idx,
2498 crate::op::RawOpContent::List(ListOp::StyleStart {
2499 start: entity_start as u32,
2500 end: entity_end as u32,
2501 key: key.clone(),
2502 value: value.clone(),
2503 info: flag,
2504 }),
2505 EventHint::Mark {
2506 start: event_start as u32,
2507 end: event_end as u32,
2508 style: crate::container::richtext::Style { key, data: value },
2509 },
2510 &inner.doc,
2511 )?;
2512
2513 txn.apply_local_op(
2514 inner.container_idx,
2515 crate::op::RawOpContent::List(ListOp::StyleEnd),
2516 EventHint::MarkEnd,
2517 &inner.doc,
2518 )?;
2519
2520 Ok(())
2521 }
2522
2523 pub fn check(&self) {
2524 match &self.inner {
2525 MaybeDetached::Detached(t) => {
2526 let t = t.lock();
2527 t.value.check_consistency_between_content_and_style_ranges();
2528 }
2529 MaybeDetached::Attached(a) => a.with_state(|state| {
2530 state
2531 .as_richtext_state_mut()
2532 .unwrap()
2533 .check_consistency_between_content_and_style_ranges();
2534 }),
2535 }
2536 }
2537
2538 pub fn apply_delta(&self, delta: &[TextDelta]) -> LoroResult<()> {
2539 match &self.inner {
2540 MaybeDetached::Detached(t) => {
2541 let _t = t.lock();
2542 Err(LoroError::NotImplemented(
2544 "`apply_delta` on a detached text container",
2545 ))
2546 }
2547 MaybeDetached::Attached(a) => a.with_txn(|txn| self.apply_delta_with_txn(txn, delta)),
2548 }
2549 }
2550
2551 pub fn apply_delta_with_txn(
2552 &self,
2553 txn: &mut Transaction,
2554 delta: &[TextDelta],
2555 ) -> LoroResult<()> {
2556 let mut index = 0;
2557 struct PendingMark {
2558 start: usize,
2559 end: usize,
2560 attributes: FxHashMap<InternalString, LoroValue>,
2561 }
2562 let mut marks: Vec<PendingMark> = Vec::new();
2563 for d in delta {
2564 match d {
2565 TextDelta::Insert { insert, attributes } => {
2566 let insert_len = event_len(insert.as_str());
2567 if insert_len == 0 {
2568 continue;
2569 }
2570
2571 let mut empty_attr = None;
2572 let attr_ref = attributes.as_ref().unwrap_or_else(|| {
2573 empty_attr = Some(FxHashMap::default());
2574 empty_attr.as_ref().unwrap()
2575 });
2576
2577 let end = checked_delta_index_end(index, insert_len, self.len_event())?;
2578 let override_styles = self.insert_with_txn_and_attr(
2579 txn,
2580 index,
2581 insert.as_str(),
2582 Some(attr_ref),
2583 PosType::Event,
2584 )?;
2585
2586 let mut pending_mark = PendingMark {
2587 start: index,
2588 end,
2589 attributes: FxHashMap::default(),
2590 };
2591 for (key, value) in override_styles {
2592 pending_mark.attributes.insert(key, value);
2593 }
2594 marks.push(pending_mark);
2595 index = end;
2596 }
2597 TextDelta::Delete { delete } => {
2598 self.delete_with_txn(txn, index, *delete, PosType::Event)?;
2599 }
2600 TextDelta::Retain { attributes, retain } => {
2601 let end = checked_delta_index_end(index, *retain, self.len_event())?;
2602 match attributes {
2603 Some(attr) if !attr.is_empty() => {
2604 let mut pending_mark = PendingMark {
2605 start: index,
2606 end,
2607 attributes: FxHashMap::default(),
2608 };
2609 for (key, value) in attr {
2610 pending_mark
2611 .attributes
2612 .insert(key.deref().into(), value.clone());
2613 }
2614 marks.push(pending_mark);
2615 }
2616 _ => {}
2617 }
2618 index = end;
2619 }
2620 }
2621 }
2622
2623 let mut len = match &self.inner {
2624 MaybeDetached::Detached(_) => self.len_event(),
2625 MaybeDetached::Attached(a) => {
2626 a.with_state(|state| state.as_richtext_state_mut().unwrap().len(PosType::Event))
2627 }
2628 };
2629 for pending_mark in marks {
2630 if pending_mark.start >= len {
2631 self.insert_with_txn(
2632 txn,
2633 len,
2634 &"\n".repeat(pending_mark.start - len + 1),
2635 PosType::Event,
2636 )?;
2637 len = pending_mark.start;
2638 }
2639
2640 for (key, value) in pending_mark.attributes {
2641 self.mark_with_txn(
2642 txn,
2643 pending_mark.start,
2644 pending_mark.end,
2645 key.deref(),
2646 value,
2647 PosType::Event,
2648 )?;
2649 }
2650 }
2651
2652 Ok(())
2653 }
2654
2655 pub fn update(&self, text: &str, options: UpdateOptions) -> Result<(), UpdateTimeoutError> {
2656 let old_str = self.to_string();
2657 let new = text.chars().map(|x| x as u32).collect::<Vec<u32>>();
2658 let old = old_str.chars().map(|x| x as u32).collect::<Vec<u32>>();
2659 diff(
2660 &mut OperateProxy::new(text_update::DiffHook::new(self, &new)),
2661 options,
2662 &old,
2663 &new,
2664 )?;
2665 Ok(())
2666 }
2667
2668 pub fn update_by_line(
2669 &self,
2670 text: &str,
2671 options: UpdateOptions,
2672 ) -> Result<(), UpdateTimeoutError> {
2673 let hook = text_update::DiffHookForLine::new(self, text);
2674 let old_lines = hook.get_old_arr().to_vec();
2675 let new_lines = hook.get_new_arr().to_vec();
2676 diff(
2677 &mut OperateProxy::new(hook),
2678 options,
2679 &old_lines,
2680 &new_lines,
2681 )
2682 }
2683
2684 #[allow(clippy::inherent_to_string)]
2685 pub fn to_string(&self) -> String {
2686 match &self.inner {
2687 MaybeDetached::Detached(t) => t.lock().value.to_string(),
2688 MaybeDetached::Attached(a) => a.get_value().into_string().unwrap().unwrap(),
2689 }
2690 }
2691
2692 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
2695 let inner = self.inner.try_attached_state()?;
2696 Ok(inner.with_doc_state(|state| {
2697 state.get_container_deep_value_with_id(inner.container_idx, None)
2698 }))
2699 }
2700
2701 pub fn get_cursor(&self, event_index: usize, side: Side) -> Option<Cursor> {
2702 self.get_cursor_internal(event_index, side, true)
2703 }
2704
2705 pub(crate) fn get_cursor_internal(
2707 &self,
2708 index: usize,
2709 side: Side,
2710 get_by_event_index: bool,
2711 ) -> Option<Cursor> {
2712 match &self.inner {
2713 MaybeDetached::Detached(_) => None,
2714 MaybeDetached::Attached(a) => {
2715 let (id, len, origin_pos) = a.with_state(|s| {
2716 let s = s.as_richtext_state_mut().unwrap();
2717 (
2718 s.get_stable_position(index, get_by_event_index),
2719 if get_by_event_index {
2720 s.len_event()
2721 } else {
2722 s.len_unicode()
2723 },
2724 if get_by_event_index {
2725 s.event_index_to_unicode_index(index)
2726 } else {
2727 index
2728 },
2729 )
2730 });
2731
2732 if len == 0 {
2733 return Some(Cursor {
2734 id: None,
2735 container: self.id(),
2736 side: if side == Side::Middle {
2737 Side::Left
2738 } else {
2739 side
2740 },
2741 origin_pos: 0,
2742 });
2743 }
2744
2745 if len <= index {
2746 return Some(Cursor {
2747 id: None,
2748 container: self.id(),
2749 side: Side::Right,
2750 origin_pos: len,
2751 });
2752 }
2753
2754 let id = id?;
2755 Some(Cursor {
2756 id: Some(id),
2757 container: self.id(),
2758 side,
2759 origin_pos,
2760 })
2761 }
2762 }
2763 }
2764
2765 pub(crate) fn convert_entity_index_to_event_index(&self, entity_index: usize) -> usize {
2766 match &self.inner {
2767 MaybeDetached::Detached(s) => s.lock().value.entity_index_to_event_index(entity_index),
2768 MaybeDetached::Attached(a) => {
2769 let mut pos = 0;
2770 a.with_state(|s| {
2771 let s = s.as_richtext_state_mut().unwrap();
2772 pos = s.entity_index_to_event_index(entity_index);
2773 });
2774 pos
2775 }
2776 }
2777 }
2778
2779 pub fn get_delta(&self) -> Vec<TextDelta> {
2780 match &self.inner {
2781 MaybeDetached::Detached(s) => {
2782 let mut delta = Vec::new();
2783 for span in s.lock().value.iter() {
2784 if span.text.as_str().is_empty() {
2785 continue;
2786 }
2787
2788 let next_attr = span.attributes.to_option_map();
2789 match delta.last_mut() {
2790 Some(TextDelta::Insert { insert, attributes })
2791 if &next_attr == attributes =>
2792 {
2793 insert.push_str(span.text.as_str());
2794 continue;
2795 }
2796 _ => {}
2797 }
2798
2799 delta.push(TextDelta::Insert {
2800 insert: span.text.as_str().to_string(),
2801 attributes: next_attr,
2802 })
2803 }
2804 delta
2805 }
2806 MaybeDetached::Attached(_a) => self
2807 .with_state(|state| {
2808 let state = state.as_richtext_state_mut().unwrap();
2809 Ok(state.get_delta())
2810 })
2811 .unwrap(),
2812 }
2813 }
2814
2815 pub fn is_deleted(&self) -> bool {
2816 match &self.inner {
2817 MaybeDetached::Detached(_) => false,
2818 MaybeDetached::Attached(a) => a.is_deleted(),
2819 }
2820 }
2821
2822 pub fn push_str(&self, s: &str) -> LoroResult<()> {
2823 self.insert_utf8(self.len_utf8(), s)
2824 }
2825
2826 pub fn clear(&self) -> LoroResult<()> {
2827 match &self.inner {
2828 MaybeDetached::Detached(mutex) => {
2829 let mut t = mutex.lock();
2830 let len = t.value.len_unicode();
2831 let ranges = t.value.get_text_entity_ranges(0, len, PosType::Unicode)?;
2832 for range in ranges.iter().rev() {
2833 t.value
2834 .drain_by_entity_index(range.entity_start, range.entity_len(), None);
2835 }
2836 Ok(())
2837 }
2838 MaybeDetached::Attached(a) => a.with_txn(|txn| {
2839 let len = a.with_state(|s| s.as_richtext_state_mut().unwrap().len_unicode());
2840 self.delete_with_txn_inline(txn, 0, len, PosType::Unicode)
2841 }),
2842 }
2843 }
2844
2845 pub fn convert_pos(&self, index: usize, from: PosType, to: PosType) -> Option<usize> {
2850 if from == to {
2851 return Some(index);
2852 }
2853
2854 if matches!(from, PosType::Entity) || matches!(to, PosType::Entity) {
2855 return None;
2856 }
2857
2858 let (event_index, unicode_index) = match &self.inner {
2860 MaybeDetached::Detached(t) => {
2861 let t = t.lock();
2862 if index > t.value.len(from) {
2863 return None;
2864 }
2865 let event_index = if from == PosType::Event {
2866 index
2867 } else {
2868 t.value.index_to_event_index(index, from)
2869 };
2870 let unicode_index = if from == PosType::Unicode {
2871 index
2872 } else {
2873 t.value.event_index_to_unicode_index(event_index)
2874 };
2875 (event_index, unicode_index)
2876 }
2877 MaybeDetached::Attached(a) if a.has_decoded_state() => {
2878 let res: Option<(usize, usize)> = a.with_state(|state| {
2879 let state = state.as_richtext_state_mut().unwrap();
2880 if index > state.len(from) {
2881 return None;
2882 }
2883
2884 let event_index = if from == PosType::Event {
2885 index
2886 } else {
2887 state.index_to_event_index(index, from)
2888 };
2889 let unicode_index = if from == PosType::Unicode {
2890 index
2891 } else {
2892 state.event_index_to_unicode_index(event_index)
2893 };
2894 Some((event_index, unicode_index))
2895 });
2896
2897 res?
2898 }
2899 MaybeDetached::Attached(a) => {
2900 let value = a.get_value();
2901 let s = value.as_string().unwrap();
2902 let unicode_index = text_pos_to_unicode(s, index, from)?;
2903 let event_index = unicode_to_text_pos(s, unicode_index, PosType::Event)?;
2904 (event_index, unicode_index)
2905 }
2906 };
2907
2908 let result = match to {
2909 PosType::Unicode => Some(unicode_index),
2910 PosType::Event => Some(event_index),
2911 PosType::Bytes | PosType::Utf16 => {
2912 match &self.inner {
2916 MaybeDetached::Detached(t) => {
2917 let t = t.lock();
2918 if event_index > t.value.len_event() {
2919 return None;
2920 }
2921 Some(t.value.event_index_to_index(event_index, to))
2922 }
2923 MaybeDetached::Attached(a) if a.has_decoded_state() => a.with_state(|state| {
2924 let state = state.as_richtext_state_mut().unwrap();
2925 if event_index > state.len_event() {
2926 return None;
2927 }
2928 Some(state.event_index_to_index(event_index, to))
2929 }),
2930 MaybeDetached::Attached(a) => {
2931 let value = a.get_value();
2932 let s = value.as_string().unwrap();
2933 unicode_to_text_pos(s, unicode_index, to)
2934 }
2935 }
2936 }
2937 PosType::Entity => None,
2938 };
2939 result
2940 }
2941}
2942
2943fn event_len(s: &str) -> usize {
2944 if cfg!(feature = "wasm") {
2945 count_utf16_len(s.as_bytes())
2946 } else {
2947 s.chars().count()
2948 }
2949}
2950
2951fn text_len(s: &str, pos_type: PosType) -> Option<usize> {
2952 Some(match pos_type {
2953 PosType::Bytes => s.len(),
2954 PosType::Unicode => s.chars().count(),
2955 PosType::Utf16 => count_utf16_len(s.as_bytes()),
2956 PosType::Event => event_len(s),
2957 PosType::Entity => return None,
2958 })
2959}
2960
2961fn text_pos_to_unicode(s: &str, index: usize, pos_type: PosType) -> Option<usize> {
2962 match pos_type {
2963 PosType::Unicode => (index <= s.chars().count()).then_some(index),
2964 PosType::Bytes => {
2965 if index > s.len() {
2966 None
2967 } else {
2968 Some(
2969 s.char_indices()
2970 .take_while(|(pos, c)| *pos + c.len_utf8() <= index)
2971 .count(),
2972 )
2973 }
2974 }
2975 PosType::Utf16 => utf16_to_unicode_pos(s, index),
2976 PosType::Event if cfg!(feature = "wasm") => utf16_to_unicode_pos(s, index),
2977 PosType::Event => (index <= s.chars().count()).then_some(index),
2978 PosType::Entity => None,
2979 }
2980}
2981
2982fn unicode_to_text_pos(s: &str, index: usize, pos_type: PosType) -> Option<usize> {
2983 match pos_type {
2984 PosType::Unicode => (index <= s.chars().count()).then_some(index),
2985 PosType::Bytes => unicode_to_byte_pos(s, index),
2986 PosType::Utf16 => unicode_to_utf16_pos(s, index),
2987 PosType::Event if cfg!(feature = "wasm") => unicode_to_utf16_pos(s, index),
2988 PosType::Event => (index <= s.chars().count()).then_some(index),
2989 PosType::Entity => None,
2990 }
2991}
2992
2993fn unicode_to_byte_pos(s: &str, index: usize) -> Option<usize> {
2994 if index == 0 {
2995 return Some(0);
2996 }
2997
2998 let mut unicode_pos = 0;
2999 for (byte_pos, _) in s.char_indices() {
3000 if unicode_pos == index {
3001 return Some(byte_pos);
3002 }
3003 unicode_pos += 1;
3004 }
3005
3006 (unicode_pos == index).then_some(s.len())
3007}
3008
3009fn unicode_to_utf16_pos(s: &str, index: usize) -> Option<usize> {
3010 let mut unicode_pos = 0;
3011 let mut utf16_pos = 0;
3012 if index == 0 {
3013 return Some(0);
3014 }
3015
3016 for c in s.chars() {
3017 unicode_pos += 1;
3018 utf16_pos += c.len_utf16();
3019 if unicode_pos == index {
3020 return Some(utf16_pos);
3021 }
3022 }
3023
3024 (unicode_pos == index).then_some(utf16_pos)
3025}
3026
3027fn utf16_to_unicode_pos(s: &str, index: usize) -> Option<usize> {
3028 let mut unicode_pos = 0;
3029 let mut utf16_pos = 0;
3030 if index == 0 {
3031 return Some(0);
3032 }
3033
3034 for c in s.chars() {
3035 let next_utf16_pos = utf16_pos + c.len_utf16();
3036 if index < next_utf16_pos {
3037 return Some(unicode_pos);
3038 }
3039 if index == next_utf16_pos {
3040 return Some(unicode_pos + 1);
3041 }
3042 utf16_pos = next_utf16_pos;
3043 unicode_pos += 1;
3044 }
3045
3046 (index == utf16_pos).then_some(unicode_pos)
3047}
3048
3049fn text_boundary_error(pos: usize, pos_type: PosType) -> LoroError {
3050 match pos_type {
3051 PosType::Bytes => LoroError::UTF8InUnicodeCodePoint { pos },
3052 PosType::Utf16 => LoroError::UTF16InUnicodeCodePoint { pos },
3053 PosType::Event if cfg!(feature = "wasm") => LoroError::UTF16InUnicodeCodePoint { pos },
3054 _ => LoroError::OutOfBound {
3055 pos,
3056 len: 0,
3057 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3058 },
3059 }
3060}
3061
3062fn text_char_at(s: &str, pos: usize, pos_type: PosType) -> LoroResult<char> {
3063 let len = text_len(s, pos_type).unwrap_or(0);
3064 if pos >= len {
3065 return Err(LoroError::OutOfBound {
3066 pos,
3067 len,
3068 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3069 });
3070 }
3071
3072 let unicode_pos =
3073 text_pos_to_unicode(s, pos, pos_type).ok_or_else(|| text_boundary_error(pos, pos_type))?;
3074 s.chars().nth(unicode_pos).ok_or(LoroError::OutOfBound {
3075 pos,
3076 len,
3077 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3078 })
3079}
3080
3081fn text_slice(s: &str, start: usize, end: usize, pos_type: PosType) -> LoroResult<String> {
3082 if end < start {
3083 return Err(LoroError::EndIndexLessThanStartIndex { start, end });
3084 }
3085 if start == end {
3086 return Ok(String::new());
3087 }
3088
3089 let len = text_len(s, pos_type).unwrap_or(0);
3090 if end > len {
3091 return Err(LoroError::OutOfBound {
3092 pos: end,
3093 len,
3094 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3095 });
3096 }
3097
3098 let start = text_pos_to_unicode(s, start, pos_type)
3099 .ok_or_else(|| text_boundary_error(start, pos_type))?;
3100 let end =
3101 text_pos_to_unicode(s, end, pos_type).ok_or_else(|| text_boundary_error(end, pos_type))?;
3102 let start = unicode_to_byte_pos(s, start).expect("unicode index must map to a byte boundary");
3103 let end = unicode_to_byte_pos(s, end).expect("unicode index must map to a byte boundary");
3104 Ok(s[start..end].to_string())
3105}
3106
3107impl ListHandler {
3108 pub fn new_detached() -> Self {
3112 Self {
3113 inner: MaybeDetached::new_detached(Vec::new()),
3114 }
3115 }
3116
3117 pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
3118 match &self.inner {
3119 MaybeDetached::Detached(l) => {
3120 let mut list = l.lock();
3121 let len = list.value.len();
3122 if pos > len {
3123 return Err(LoroError::OutOfBound {
3124 pos,
3125 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3126 len,
3127 });
3128 }
3129 let value = v.into();
3130 ensure_no_regular_container_value(&value)?;
3131 list.value.insert(pos, ValueOrHandler::Value(value));
3132 Ok(())
3133 }
3134 MaybeDetached::Attached(a) => {
3135 a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
3136 }
3137 }
3138 }
3139
3140 pub fn insert_with_txn(
3141 &self,
3142 txn: &mut Transaction,
3143 pos: usize,
3144 v: LoroValue,
3145 ) -> LoroResult<()> {
3146 if pos > self.len() {
3147 return Err(LoroError::OutOfBound {
3148 pos,
3149 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3150 len: self.len(),
3151 });
3152 }
3153
3154 let inner = self.inner.try_attached_state()?;
3155 ensure_no_regular_container_value(&v)?;
3156
3157 txn.apply_local_op(
3158 inner.container_idx,
3159 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3160 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3161 pos,
3162 }),
3163 EventHint::InsertList { len: 1, pos },
3164 &inner.doc,
3165 )
3166 }
3167
3168 pub fn push(&self, v: impl Into<LoroValue>) -> LoroResult<()> {
3169 match &self.inner {
3170 MaybeDetached::Detached(l) => {
3171 let mut list = l.lock();
3172 let value = v.into();
3173 ensure_no_regular_container_value(&value)?;
3174 list.value.push(ValueOrHandler::Value(value));
3175 Ok(())
3176 }
3177 MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v.into())),
3178 }
3179 }
3180
3181 pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
3182 let pos = self.len();
3183 self.insert_with_txn(txn, pos, v)
3184 }
3185
3186 pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
3187 match &self.inner {
3188 MaybeDetached::Detached(l) => {
3189 let mut list = l.lock();
3190 Ok(list.value.pop().map(|v| v.to_value()))
3191 }
3192 MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
3193 }
3194 }
3195
3196 pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
3197 let len = self.len();
3198 if len == 0 {
3199 return Ok(None);
3200 }
3201
3202 let v = self.get(len - 1);
3203 self.delete_with_txn(txn, len - 1, 1)?;
3204 Ok(v)
3205 }
3206
3207 pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3208 match &self.inner {
3209 MaybeDetached::Detached(l) => {
3210 let mut list = l.lock();
3211 if pos > list.value.len() {
3212 return Err(LoroError::OutOfBound {
3213 pos,
3214 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3215 len: list.value.len(),
3216 });
3217 }
3218 list.value
3219 .insert(pos, ValueOrHandler::Handler(child.to_handler()));
3220 Ok(child)
3221 }
3222 MaybeDetached::Attached(a) => {
3223 a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
3224 }
3225 }
3226 }
3227
3228 pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
3229 self.insert_container(self.len(), child)
3230 }
3231
3232 pub fn insert_container_with_txn<H: HandlerTrait>(
3233 &self,
3234 txn: &mut Transaction,
3235 pos: usize,
3236 child: H,
3237 ) -> LoroResult<H> {
3238 if pos > self.len() {
3239 return Err(LoroError::OutOfBound {
3240 pos,
3241 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3242 len: self.len(),
3243 });
3244 }
3245
3246 let inner = self.inner.try_attached_state()?;
3247 let id = txn.next_id();
3248 let container_id = ContainerID::new_normal(id, child.kind());
3249 let v = LoroValue::Container(container_id.clone());
3250 txn.apply_local_op(
3251 inner.container_idx,
3252 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3253 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3254 pos,
3255 }),
3256 EventHint::InsertList { len: 1, pos },
3257 &inner.doc,
3258 )?;
3259 let ans = child.attach(txn, inner, container_id)?;
3260 Ok(ans)
3261 }
3262
3263 pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
3264 match &self.inner {
3265 MaybeDetached::Detached(l) => {
3266 let mut list = l.lock();
3267 let end = checked_range_end(pos, len, list.value.len(), || {
3268 format!("Position: {}:{}", file!(), line!()).into_boxed_str()
3269 })?;
3270 list.value.drain(pos..end);
3271 Ok(())
3272 }
3273 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
3274 }
3275 }
3276
3277 pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
3278 if len == 0 {
3279 return Ok(());
3280 }
3281
3282 let list_len = self.len();
3283 let end = checked_range_end(pos, len, list_len, || {
3284 format!("Position: {}:{}", file!(), line!()).into_boxed_str()
3285 })?;
3286
3287 let inner = self.inner.try_attached_state()?;
3288 let ids: Vec<_> = inner.with_state(|state| {
3289 let list = state.as_list_state().unwrap();
3290 (pos..end).map(|i| list.get_id_at(i).unwrap()).collect()
3291 });
3292
3293 for id in ids.into_iter() {
3294 txn.apply_local_op(
3295 inner.container_idx,
3296 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
3297 id.id(),
3298 pos as isize,
3299 1,
3300 ))),
3301 EventHint::DeleteList(DeleteSpan::new(pos as isize, 1)),
3302 &inner.doc,
3303 )?;
3304 }
3305
3306 Ok(())
3307 }
3308
3309 pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
3310 match &self.inner {
3311 MaybeDetached::Detached(l) => {
3312 let list = l.lock();
3313 let value = list.value.get(index).ok_or(LoroError::OutOfBound {
3314 pos: index,
3315 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3316 len: list.value.len(),
3317 })?;
3318 match value {
3319 ValueOrHandler::Handler(h) => Ok(h.clone()),
3320 _ => Err(LoroError::ArgErr(
3321 format!(
3322 "Expected container at index {}, but found {:?}",
3323 index, value
3324 )
3325 .into_boxed_str(),
3326 )),
3327 }
3328 }
3329 MaybeDetached::Attached(_) => {
3330 let Some(value) = self.get_(index) else {
3331 return Err(LoroError::OutOfBound {
3332 pos: index,
3333 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3334 len: self.len(),
3335 });
3336 };
3337 match value {
3338 ValueOrHandler::Handler(handler) => Ok(handler),
3339 ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
3340 format!(
3341 "Expected container at index {}, but found {:?}",
3342 index, value
3343 )
3344 .into_boxed_str(),
3345 )),
3346 }
3347 }
3348 }
3349 }
3350
3351 pub fn len(&self) -> usize {
3352 match &self.inner {
3353 MaybeDetached::Detached(l) => l.lock().value.len(),
3354 MaybeDetached::Attached(a) => {
3355 a.with_doc_state(|state| state.get_list_len(a.container_idx))
3356 }
3357 }
3358 }
3359
3360 pub fn is_empty(&self) -> bool {
3361 self.len() == 0
3362 }
3363
3364 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
3365 let inner = self.inner.try_attached_state()?;
3366 Ok(inner.with_doc_state(|state| {
3367 state.get_container_deep_value_with_id(inner.container_idx, None)
3368 }))
3369 }
3370
3371 pub fn get_slice_deep_value_with_id(&self, start: usize, end: usize) -> LoroResult<LoroValue> {
3377 let inner = self.inner.try_attached_state()?;
3378 Ok(inner.with_doc_state(|state| {
3379 state.get_list_range_deep_value(inner.container_idx, start, end, true)
3380 }))
3381 }
3382
3383 pub fn get_slice_deep_value(&self, start: usize, end: usize) -> LoroResult<LoroValue> {
3389 let inner = self.inner.try_attached_state()?;
3390 Ok(inner.with_doc_state(|state| {
3391 state.get_list_range_deep_value(inner.container_idx, start, end, false)
3392 }))
3393 }
3394
3395 pub fn get(&self, index: usize) -> Option<LoroValue> {
3396 match &self.inner {
3397 MaybeDetached::Detached(l) => l.lock().value.get(index).map(|x| x.to_value()),
3398 MaybeDetached::Attached(a) => {
3399 a.with_doc_state(|state| state.get_list_value_at(a.container_idx, index))
3400 }
3401 }
3402 }
3403
3404 pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
3406 match &self.inner {
3407 MaybeDetached::Detached(l) => {
3408 let l = l.lock();
3409 l.value.get(index).cloned()
3410 }
3411 MaybeDetached::Attached(inner) => {
3412 let value = inner
3413 .with_doc_state(|state| state.get_list_value_at(inner.container_idx, index));
3414 value.map(|value| value_to_value_or_handler(inner, value))
3415 }
3416 }
3417 }
3418
3419 pub fn for_each<I>(&self, mut f: I)
3420 where
3421 I: FnMut(ValueOrHandler),
3422 {
3423 match &self.inner {
3424 MaybeDetached::Detached(l) => {
3425 let l = l.lock();
3426 for v in l.value.iter() {
3427 f(v.clone())
3428 }
3429 }
3430 MaybeDetached::Attached(inner) => {
3431 let temp = inner.with_doc_state(|state| {
3432 state
3433 .get_list_values(inner.container_idx)
3434 .into_iter()
3435 .map(|value| value_to_value_or_handler(inner, value))
3436 .collect::<Vec<_>>()
3437 });
3438 for v in temp.into_iter() {
3439 f(v);
3440 }
3441 }
3442 }
3443 }
3444
3445 pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
3446 match &self.inner {
3447 MaybeDetached::Detached(_) => None,
3448 MaybeDetached::Attached(a) => {
3449 let (id, len) = a.with_state(|s| {
3450 let l = s.as_list_state().unwrap();
3451 (l.get_id_at(pos), l.len())
3452 });
3453
3454 if len == 0 {
3455 return Some(Cursor {
3456 id: None,
3457 container: self.id(),
3458 side: if side == Side::Middle {
3459 Side::Left
3460 } else {
3461 side
3462 },
3463 origin_pos: 0,
3464 });
3465 }
3466
3467 if len <= pos {
3468 return Some(Cursor {
3469 id: None,
3470 container: self.id(),
3471 side: Side::Right,
3472 origin_pos: len,
3473 });
3474 }
3475
3476 let id = id?;
3477 Some(Cursor {
3478 id: Some(id.id()),
3479 container: self.id(),
3480 side,
3481 origin_pos: pos,
3482 })
3483 }
3484 }
3485 }
3486
3487 fn apply_delta(
3488 &self,
3489 delta: loro_delta::DeltaRope<
3490 loro_delta::array_vec::ArrayVec<ValueOrHandler, 8>,
3491 crate::event::ListDeltaMeta,
3492 >,
3493 on_container_remap: &mut dyn FnMut(ContainerID, ContainerID),
3494 ) -> LoroResult<()> {
3495 match &self.inner {
3496 MaybeDetached::Detached(_) => unimplemented!(),
3497 MaybeDetached::Attached(_) => {
3498 let mut index = 0;
3499 for item in delta.iter() {
3500 match item {
3501 loro_delta::DeltaItem::Retain { len, .. } => {
3502 index += len;
3503 }
3504 loro_delta::DeltaItem::Replace { value, delete, .. } => {
3505 if *delete > 0 {
3506 self.delete(index, *delete)?;
3507 }
3508
3509 for v in value.iter() {
3510 match v {
3511 ValueOrHandler::Value(LoroValue::Container(old_id)) => {
3512 let new_h = self.insert_container(
3513 index,
3514 Handler::new_unattached(old_id.container_type()),
3515 )?;
3516 let new_id = new_h.id();
3517 on_container_remap(old_id.clone(), new_id);
3518 }
3519 ValueOrHandler::Handler(h) => {
3520 let old_id = h.id();
3521 let new_h = self.insert_container(
3522 index,
3523 Handler::new_unattached(old_id.container_type()),
3524 )?;
3525 let new_id = new_h.id();
3526 on_container_remap(old_id, new_id);
3527 }
3528 ValueOrHandler::Value(v) => {
3529 self.insert(index, v.clone())?;
3530 }
3531 }
3532
3533 index += 1;
3534 }
3535 }
3536 }
3537 }
3538 }
3539 }
3540
3541 Ok(())
3542 }
3543
3544 pub fn is_deleted(&self) -> bool {
3545 match &self.inner {
3546 MaybeDetached::Detached(_) => false,
3547 MaybeDetached::Attached(a) => a.is_deleted(),
3548 }
3549 }
3550
3551 pub fn clear(&self) -> LoroResult<()> {
3552 match &self.inner {
3553 MaybeDetached::Detached(l) => {
3554 let mut l = l.lock();
3555 l.value.clear();
3556 Ok(())
3557 }
3558 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
3559 }
3560 }
3561
3562 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
3563 self.delete_with_txn(txn, 0, self.len())
3564 }
3565
3566 pub fn get_id_at(&self, pos: usize) -> Option<ID> {
3567 match &self.inner {
3568 MaybeDetached::Detached(_) => None,
3569 MaybeDetached::Attached(a) => a.with_state(|state| {
3570 state
3571 .as_list_state()
3572 .unwrap()
3573 .get_id_at(pos)
3574 .map(|x| x.id())
3575 }),
3576 }
3577 }
3578}
3579
3580impl MovableListHandler {
3581 pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
3582 match &self.inner {
3583 MaybeDetached::Detached(d) => {
3584 let mut d = d.lock();
3585 if pos > d.value.len() {
3586 return Err(LoroError::OutOfBound {
3587 pos,
3588 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3589 len: d.value.len(),
3590 });
3591 }
3592 let value = v.into();
3593 ensure_no_regular_container_value(&value)?;
3594 d.value.insert(pos, ValueOrHandler::Value(value));
3595 Ok(())
3596 }
3597 MaybeDetached::Attached(a) => {
3598 a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
3599 }
3600 }
3601 }
3602
3603 #[instrument(skip_all)]
3604 pub fn insert_with_txn(
3605 &self,
3606 txn: &mut Transaction,
3607 pos: usize,
3608 v: LoroValue,
3609 ) -> LoroResult<()> {
3610 if pos > self.len() {
3611 return Err(LoroError::OutOfBound {
3612 pos,
3613 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3614 len: self.len(),
3615 });
3616 }
3617
3618 ensure_no_regular_container_value(&v)?;
3619
3620 let op_index = self.with_state(|state| {
3621 let list = state.as_movable_list_state().unwrap();
3622 Ok(list
3623 .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3624 .unwrap())
3625 })?;
3626
3627 let inner = self.inner.try_attached_state()?;
3628 txn.apply_local_op(
3629 inner.container_idx,
3630 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3631 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3632 pos: op_index,
3633 }),
3634 EventHint::InsertList { len: 1, pos },
3635 &inner.doc,
3636 )
3637 }
3638
3639 #[inline]
3640 pub fn mov(&self, from: usize, to: usize) -> LoroResult<()> {
3641 match &self.inner {
3642 MaybeDetached::Detached(d) => {
3643 let mut d = d.lock();
3644 if from >= d.value.len() {
3645 return Err(LoroError::OutOfBound {
3646 pos: from,
3647 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3648 len: d.value.len(),
3649 });
3650 }
3651 if to >= d.value.len() {
3652 return Err(LoroError::OutOfBound {
3653 pos: to,
3654 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3655 len: d.value.len(),
3656 });
3657 }
3658 let v = d.value.remove(from);
3659 d.value.insert(to, v);
3660 Ok(())
3661 }
3662 MaybeDetached::Attached(a) => a.with_txn(|txn| self.move_with_txn(txn, from, to)),
3663 }
3664 }
3665
3666 #[instrument(skip_all)]
3668 pub fn move_with_txn(&self, txn: &mut Transaction, from: usize, to: usize) -> LoroResult<()> {
3669 if from == to {
3670 return Ok(());
3671 }
3672
3673 if from >= self.len() {
3674 return Err(LoroError::OutOfBound {
3675 pos: from,
3676 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3677 len: self.len(),
3678 });
3679 }
3680
3681 if to >= self.len() {
3682 return Err(LoroError::OutOfBound {
3683 pos: to,
3684 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3685 len: self.len(),
3686 });
3687 }
3688
3689 let (op_from, op_to, elem_id, value) = self.with_state(|state| {
3690 let list = state.as_movable_list_state().unwrap();
3691 let (elem_id, elem) = list
3692 .get_elem_at_given_pos(from, IndexType::ForUser)
3693 .unwrap();
3694 Ok((
3695 list.convert_index(from, IndexType::ForUser, IndexType::ForOp)
3696 .unwrap(),
3697 list.convert_index(to, IndexType::ForUser, IndexType::ForOp)
3698 .unwrap(),
3699 elem_id,
3700 elem.value().clone(),
3701 ))
3702 })?;
3703
3704 let inner = self.inner.try_attached_state()?;
3705 txn.apply_local_op(
3706 inner.container_idx,
3707 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Move {
3708 from: op_from as u32,
3709 to: op_to as u32,
3710 elem_id: elem_id.to_id(),
3711 }),
3712 EventHint::Move {
3713 value,
3714 from: from as u32,
3715 to: to as u32,
3716 },
3717 &inner.doc,
3718 )
3719 }
3720
3721 pub fn push(&self, v: LoroValue) -> LoroResult<()> {
3722 match &self.inner {
3723 MaybeDetached::Detached(d) => {
3724 let mut d = d.lock();
3725 d.value.push(v.into());
3726 Ok(())
3727 }
3728 MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v)),
3729 }
3730 }
3731
3732 pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
3733 let pos = self.len();
3734 self.insert_with_txn(txn, pos, v)
3735 }
3736
3737 pub fn pop_(&self) -> LoroResult<Option<ValueOrHandler>> {
3738 match &self.inner {
3739 MaybeDetached::Detached(d) => {
3740 let mut d = d.lock();
3741 Ok(d.value.pop())
3742 }
3743 MaybeDetached::Attached(a) => {
3744 if self.is_empty() {
3745 return Ok(None);
3746 }
3747 let last = self.len() - 1;
3748 let ans = self.get_(last);
3749 a.with_txn(|txn| self.pop_with_txn(txn))?;
3750 Ok(ans)
3751 }
3752 }
3753 }
3754
3755 pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
3756 match &self.inner {
3757 MaybeDetached::Detached(a) => {
3758 let mut a = a.lock();
3759 Ok(a.value.pop().map(|x| x.to_value()))
3760 }
3761 MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
3762 }
3763 }
3764
3765 pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
3766 let len = self.len();
3767 if len == 0 {
3768 return Ok(None);
3769 }
3770
3771 let v = self.get(len - 1);
3772 self.delete_with_txn(txn, len - 1, 1)?;
3773 Ok(v)
3774 }
3775
3776 pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3777 match &self.inner {
3778 MaybeDetached::Detached(d) => {
3779 let mut d = d.lock();
3780 if pos > d.value.len() {
3781 return Err(LoroError::OutOfBound {
3782 pos,
3783 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3784 len: d.value.len(),
3785 });
3786 }
3787 d.value
3788 .insert(pos, ValueOrHandler::Handler(child.to_handler()));
3789 Ok(child)
3790 }
3791 MaybeDetached::Attached(a) => {
3792 a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
3793 }
3794 }
3795 }
3796
3797 pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
3798 self.insert_container(self.len(), child)
3799 }
3800
3801 pub fn insert_container_with_txn<H: HandlerTrait>(
3802 &self,
3803 txn: &mut Transaction,
3804 pos: usize,
3805 child: H,
3806 ) -> LoroResult<H> {
3807 if pos > self.len() {
3808 return Err(LoroError::OutOfBound {
3809 pos,
3810 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3811 len: self.len(),
3812 });
3813 }
3814
3815 let op_index = self.with_state(|state| {
3816 let list = state.as_movable_list_state().unwrap();
3817 Ok(list
3818 .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3819 .unwrap())
3820 })?;
3821
3822 let id = txn.next_id();
3823 let container_id = ContainerID::new_normal(id, child.kind());
3824 let v = LoroValue::Container(container_id.clone());
3825 let inner = self.inner.try_attached_state()?;
3826 txn.apply_local_op(
3827 inner.container_idx,
3828 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3829 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3830 pos: op_index,
3831 }),
3832 EventHint::InsertList { len: 1, pos },
3833 &inner.doc,
3834 )?;
3835 child.attach(txn, inner, container_id)
3836 }
3837
3838 pub fn set(&self, index: usize, value: impl Into<LoroValue>) -> LoroResult<()> {
3839 match &self.inner {
3840 MaybeDetached::Detached(d) => {
3841 let mut d = d.lock();
3842 if index >= d.value.len() {
3843 return Err(LoroError::OutOfBound {
3844 pos: index,
3845 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3846 len: d.value.len(),
3847 });
3848 }
3849 let value = value.into();
3850 ensure_no_regular_container_value(&value)?;
3851 d.value[index] = ValueOrHandler::Value(value);
3852 Ok(())
3853 }
3854 MaybeDetached::Attached(a) => {
3855 a.with_txn(|txn| self.set_with_txn(txn, index, value.into()))
3856 }
3857 }
3858 }
3859
3860 pub fn set_with_txn(
3861 &self,
3862 txn: &mut Transaction,
3863 index: usize,
3864 value: LoroValue,
3865 ) -> LoroResult<()> {
3866 if index >= self.len() {
3867 return Err(LoroError::OutOfBound {
3868 pos: index,
3869 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3870 len: self.len(),
3871 });
3872 }
3873
3874 let inner = self.inner.try_attached_state()?;
3875 let Some(elem_id) = self.with_state(|state| {
3876 let list = state.as_movable_list_state().unwrap();
3877 Ok(list.get_elem_id_at(index, IndexType::ForUser))
3878 })?
3879 else {
3880 unreachable!()
3881 };
3882 ensure_no_regular_container_value(&value)?;
3883
3884 let op = crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3885 elem_id: elem_id.to_id(),
3886 value: value.clone(),
3887 });
3888
3889 let hint = EventHint::SetList { index, value };
3890 txn.apply_local_op(inner.container_idx, op, hint, &inner.doc)
3891 }
3892
3893 pub fn set_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3894 match &self.inner {
3895 MaybeDetached::Detached(d) => {
3896 let mut d = d.lock();
3897 if pos >= d.value.len() {
3898 return Err(LoroError::OutOfBound {
3899 pos,
3900 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3901 len: d.value.len(),
3902 });
3903 }
3904 d.value[pos] = ValueOrHandler::Handler(child.to_handler());
3905 Ok(child)
3906 }
3907 MaybeDetached::Attached(a) => {
3908 a.with_txn(|txn| self.set_container_with_txn(txn, pos, child))
3909 }
3910 }
3911 }
3912
3913 pub fn set_container_with_txn<H: HandlerTrait>(
3914 &self,
3915 txn: &mut Transaction,
3916 pos: usize,
3917 child: H,
3918 ) -> LoroResult<H> {
3919 let id = txn.next_id();
3920 let container_id = ContainerID::new_normal(id, child.kind());
3921 let v = LoroValue::Container(container_id.clone());
3922 let Some(elem_id) = self.with_state(|state| {
3923 let list = state.as_movable_list_state().unwrap();
3924 Ok(list.get_elem_id_at(pos, IndexType::ForUser))
3925 })?
3926 else {
3927 let len = self.len();
3928 if pos >= len {
3929 return Err(LoroError::OutOfBound {
3930 pos,
3931 len,
3932 info: "".into(),
3933 });
3934 } else {
3935 unreachable!()
3936 }
3937 };
3938 let inner = self.inner.try_attached_state()?;
3939 txn.apply_local_op(
3940 inner.container_idx,
3941 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3942 elem_id: elem_id.to_id(),
3943 value: v.clone(),
3944 }),
3945 EventHint::SetList {
3946 index: pos,
3947 value: v,
3948 },
3949 &inner.doc,
3950 )?;
3951
3952 child.attach(txn, inner, container_id)
3953 }
3954
3955 pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
3956 match &self.inner {
3957 MaybeDetached::Detached(d) => {
3958 let mut d = d.lock();
3959 let end = checked_range_end(pos, len, d.value.len(), || {
3960 format!("Position: {}:{}", file!(), line!()).into_boxed_str()
3961 })?;
3962 d.value.drain(pos..end);
3963 Ok(())
3964 }
3965 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
3966 }
3967 }
3968
3969 #[instrument(skip_all)]
3970 pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
3971 if len == 0 {
3972 return Ok(());
3973 }
3974
3975 let list_len = self.len();
3976 let end = checked_range_end(pos, len, list_len, || {
3977 format!("Position: {}:{}", file!(), line!()).into_boxed_str()
3978 })?;
3979
3980 let (ids, new_poses) = self.with_state(|state| {
3981 let list = state.as_movable_list_state().unwrap();
3982 let ids: Vec<_> = (pos..end)
3983 .map(|i| list.get_list_id_at(i, IndexType::ForUser).unwrap())
3984 .collect();
3985 let poses: Vec<_> = (pos..end)
3986 .map(|user_index| {
3988 let op_index = list
3989 .convert_index(user_index, IndexType::ForUser, IndexType::ForOp)
3990 .unwrap();
3991 assert!(op_index >= user_index);
3992 op_index - (user_index - pos)
3993 })
3994 .collect();
3995 Ok((ids, poses))
3996 })?;
3997
3998 loro_common::info!(?pos, ?len, ?ids, ?new_poses, "delete_with_txn");
3999 let user_pos = pos;
4000 let inner = self.inner.try_attached_state()?;
4001 for (id, op_pos) in ids.into_iter().zip(new_poses.into_iter()) {
4002 txn.apply_local_op(
4003 inner.container_idx,
4004 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
4005 id,
4006 op_pos as isize,
4007 1,
4008 ))),
4009 EventHint::DeleteList(DeleteSpan::new(user_pos as isize, 1)),
4010 &inner.doc,
4011 )?;
4012 }
4013
4014 Ok(())
4015 }
4016
4017 pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
4018 match &self.inner {
4019 MaybeDetached::Detached(l) => {
4020 let list = l.lock();
4021 let value = list.value.get(index).ok_or(LoroError::OutOfBound {
4022 pos: index,
4023 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
4024 len: list.value.len(),
4025 })?;
4026 match value {
4027 ValueOrHandler::Handler(h) => Ok(h.clone()),
4028 _ => Err(LoroError::ArgErr(
4029 format!(
4030 "Expected container at index {}, but found {:?}",
4031 index, value
4032 )
4033 .into_boxed_str(),
4034 )),
4035 }
4036 }
4037 MaybeDetached::Attached(_) => {
4038 let Some(value) = self.get_(index) else {
4039 return Err(LoroError::OutOfBound {
4040 pos: index,
4041 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
4042 len: self.len(),
4043 });
4044 };
4045 match value {
4046 ValueOrHandler::Handler(handler) => Ok(handler),
4047 ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
4048 format!(
4049 "Expected container at index {}, but found {:?}",
4050 index, value
4051 )
4052 .into_boxed_str(),
4053 )),
4054 }
4055 }
4056 }
4057 }
4058
4059 pub fn len(&self) -> usize {
4060 match &self.inner {
4061 MaybeDetached::Detached(d) => {
4062 let d = d.lock();
4063 d.value.len()
4064 }
4065 MaybeDetached::Attached(a) => {
4066 a.with_doc_state(|state| state.get_list_len(a.container_idx))
4067 }
4068 }
4069 }
4070
4071 pub fn is_empty(&self) -> bool {
4072 self.len() == 0
4073 }
4074
4075 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
4076 let inner = self.inner.try_attached_state()?;
4077 Ok(inner.with_doc_state(|state| {
4078 state.get_container_deep_value_with_id(inner.container_idx, None)
4079 }))
4080 }
4081
4082 pub fn get_slice_deep_value_with_id(&self, start: usize, end: usize) -> LoroResult<LoroValue> {
4088 let inner = self.inner.try_attached_state()?;
4089 Ok(inner.with_doc_state(|state| {
4090 state.get_list_range_deep_value(inner.container_idx, start, end, true)
4091 }))
4092 }
4093
4094 pub fn get_slice_deep_value(&self, start: usize, end: usize) -> LoroResult<LoroValue> {
4100 let inner = self.inner.try_attached_state()?;
4101 Ok(inner.with_doc_state(|state| {
4102 state.get_list_range_deep_value(inner.container_idx, start, end, false)
4103 }))
4104 }
4105
4106 pub fn get(&self, index: usize) -> Option<LoroValue> {
4107 match &self.inner {
4108 MaybeDetached::Detached(d) => {
4109 let d = d.lock();
4110 d.value.get(index).map(|v| v.to_value())
4111 }
4112 MaybeDetached::Attached(a) => {
4113 a.with_doc_state(|state| state.get_list_value_at(a.container_idx, index))
4114 }
4115 }
4116 }
4117
4118 pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
4120 match &self.inner {
4121 MaybeDetached::Detached(d) => {
4122 let d = d.lock();
4123 d.value.get(index).cloned()
4124 }
4125 MaybeDetached::Attached(m) => {
4126 let value =
4127 m.with_doc_state(|state| state.get_list_value_at(m.container_idx, index));
4128 value.map(|value| value_to_value_or_handler(m, value))
4129 }
4130 }
4131 }
4132
4133 pub fn for_each<I>(&self, mut f: I)
4134 where
4135 I: FnMut(ValueOrHandler),
4136 {
4137 match &self.inner {
4138 MaybeDetached::Detached(d) => {
4139 let d = d.lock();
4140 for v in d.value.iter() {
4141 f(v.clone());
4142 }
4143 }
4144 MaybeDetached::Attached(m) => {
4145 let temp = m.with_doc_state(|state| {
4146 state
4147 .get_list_values(m.container_idx)
4148 .into_iter()
4149 .map(|value| value_to_value_or_handler(m, value))
4150 .collect::<Vec<_>>()
4151 });
4152
4153 for v in temp.into_iter() {
4154 f(v);
4155 }
4156 }
4157 }
4158 }
4159
4160 pub fn log_internal_state(&self) -> String {
4161 match &self.inner {
4162 MaybeDetached::Detached(d) => {
4163 let d = d.lock();
4164 format!("{:#?}", &d.value)
4165 }
4166 MaybeDetached::Attached(a) => a.with_state(|state| {
4167 let a = state.as_movable_list_state().unwrap();
4168 format!("{a:#?}")
4169 }),
4170 }
4171 }
4172
4173 pub fn new_detached() -> MovableListHandler {
4174 MovableListHandler {
4175 inner: MaybeDetached::new_detached(Default::default()),
4176 }
4177 }
4178
4179 pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
4180 match &self.inner {
4181 MaybeDetached::Detached(_) => None,
4182 MaybeDetached::Attached(inner) => {
4183 let (id, len) = inner.with_state(|s| {
4184 let l = s.as_movable_list_state().unwrap();
4185 (l.get_list_item_id_at(pos), l.len())
4186 });
4187
4188 if len == 0 {
4189 return Some(Cursor {
4190 id: None,
4191 container: self.id(),
4192 side: if side == Side::Middle {
4193 Side::Left
4194 } else {
4195 side
4196 },
4197 origin_pos: 0,
4198 });
4199 }
4200
4201 if len <= pos {
4202 return Some(Cursor {
4203 id: None,
4204 container: self.id(),
4205 side: Side::Right,
4206 origin_pos: len,
4207 });
4208 }
4209
4210 let id = id?;
4211 Some(Cursor {
4212 id: Some(id.id()),
4213 container: self.id(),
4214 side,
4215 origin_pos: pos,
4216 })
4217 }
4218 }
4219 }
4220
4221 pub(crate) fn op_pos_to_user_pos(&self, new_pos: usize) -> usize {
4222 match &self.inner {
4223 MaybeDetached::Detached(_) => new_pos,
4224 MaybeDetached::Attached(inner) => {
4225 let mut pos = new_pos;
4226 inner.with_state(|s| {
4227 let l = s.as_movable_list_state().unwrap();
4228 pos = l
4229 .convert_index(new_pos, IndexType::ForOp, IndexType::ForUser)
4230 .unwrap_or(l.len());
4231 });
4232 pos
4233 }
4234 }
4235 }
4236
4237 pub fn is_deleted(&self) -> bool {
4238 match &self.inner {
4239 MaybeDetached::Detached(_) => false,
4240 MaybeDetached::Attached(a) => a.is_deleted(),
4241 }
4242 }
4243
4244 pub fn clear(&self) -> LoroResult<()> {
4245 match &self.inner {
4246 MaybeDetached::Detached(d) => {
4247 let mut d = d.lock();
4248 d.value.clear();
4249 Ok(())
4250 }
4251 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
4252 }
4253 }
4254
4255 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
4256 self.delete_with_txn(txn, 0, self.len())
4257 }
4258
4259 pub fn get_creator_at(&self, pos: usize) -> Option<PeerID> {
4260 match &self.inner {
4261 MaybeDetached::Detached(_) => None,
4262 MaybeDetached::Attached(a) => {
4263 a.with_state(|state| state.as_movable_list_state().unwrap().get_creator_at(pos))
4264 }
4265 }
4266 }
4267
4268 pub fn get_last_mover_at(&self, pos: usize) -> Option<PeerID> {
4269 match &self.inner {
4270 MaybeDetached::Detached(_) => None,
4271 MaybeDetached::Attached(a) => a.with_state(|state| {
4272 state
4273 .as_movable_list_state()
4274 .unwrap()
4275 .get_last_mover_at(pos)
4276 }),
4277 }
4278 }
4279
4280 pub fn get_last_editor_at(&self, pos: usize) -> Option<PeerID> {
4281 match &self.inner {
4282 MaybeDetached::Detached(_) => None,
4283 MaybeDetached::Attached(a) => a.with_state(|state| {
4284 state
4285 .as_movable_list_state()
4286 .unwrap()
4287 .get_last_editor_at(pos)
4288 }),
4289 }
4290 }
4291}
4292
4293impl MapHandler {
4294 pub fn new_detached() -> Self {
4298 Self {
4299 inner: MaybeDetached::new_detached(Default::default()),
4300 }
4301 }
4302
4303 pub fn insert(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
4304 match &self.inner {
4305 MaybeDetached::Detached(m) => {
4306 let mut m = m.lock();
4307 let value = value.into();
4308 ensure_no_regular_container_value(&value)?;
4309 m.value.insert(key.into(), ValueOrHandler::Value(value));
4310 Ok(())
4311 }
4312 MaybeDetached::Attached(a) => {
4313 a.with_txn(|txn| self.insert_with_txn(txn, key, value.into()))
4314 }
4315 }
4316 }
4317
4318 fn insert_without_skipping(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
4320 match &self.inner {
4321 MaybeDetached::Detached(m) => {
4322 let mut m = m.lock();
4323 let value = value.into();
4324 ensure_no_regular_container_value(&value)?;
4325 m.value.insert(key.into(), ValueOrHandler::Value(value));
4326 Ok(())
4327 }
4328 MaybeDetached::Attached(a) => a.with_txn(|txn| {
4329 let this = &self;
4330 let value = value.into();
4331 ensure_no_regular_container_value(&value)?;
4332
4333 let inner = this.inner.try_attached_state()?;
4334 txn.apply_local_op(
4335 inner.container_idx,
4336 crate::op::RawOpContent::Map(crate::container::map::MapSet {
4337 key: key.into(),
4338 value: Some(value.clone()),
4339 }),
4340 EventHint::Map {
4341 key: key.into(),
4342 value: Some(value.clone()),
4343 },
4344 &inner.doc,
4345 )
4346 }),
4347 }
4348 }
4349
4350 pub fn insert_with_txn(
4351 &self,
4352 txn: &mut Transaction,
4353 key: &str,
4354 value: LoroValue,
4355 ) -> LoroResult<()> {
4356 ensure_no_regular_container_value(&value)?;
4357
4358 if self.get(key).map(|x| x == value).unwrap_or(false) {
4359 return Ok(());
4361 }
4362
4363 let inner = self.inner.try_attached_state()?;
4364 txn.apply_local_op(
4365 inner.container_idx,
4366 crate::op::RawOpContent::Map(crate::container::map::MapSet {
4367 key: key.into(),
4368 value: Some(value.clone()),
4369 }),
4370 EventHint::Map {
4371 key: key.into(),
4372 value: Some(value.clone()),
4373 },
4374 &inner.doc,
4375 )
4376 }
4377
4378 pub fn insert_container<T: HandlerTrait>(&self, key: &str, handler: T) -> LoroResult<T> {
4379 match &self.inner {
4380 MaybeDetached::Detached(m) => {
4381 let mut m = m.lock();
4382 let to_insert = handler.to_handler();
4383 m.value
4384 .insert(key.into(), ValueOrHandler::Handler(to_insert.clone()));
4385 Ok(handler)
4386 }
4387 MaybeDetached::Attached(a) => {
4388 a.with_txn(|txn| self.insert_container_with_txn(txn, key, handler))
4389 }
4390 }
4391 }
4392
4393 pub fn insert_container_with_txn<H: HandlerTrait>(
4394 &self,
4395 txn: &mut Transaction,
4396 key: &str,
4397 child: H,
4398 ) -> LoroResult<H> {
4399 let inner = self.inner.try_attached_state()?;
4400 let id = txn.next_id();
4401 let container_id = ContainerID::new_normal(id, child.kind());
4402 txn.apply_local_op(
4403 inner.container_idx,
4404 crate::op::RawOpContent::Map(crate::container::map::MapSet {
4405 key: key.into(),
4406 value: Some(LoroValue::Container(container_id.clone())),
4407 }),
4408 EventHint::Map {
4409 key: key.into(),
4410 value: Some(LoroValue::Container(container_id.clone())),
4411 },
4412 &inner.doc,
4413 )?;
4414
4415 child.attach(txn, inner, container_id)
4416 }
4417
4418 pub fn delete(&self, key: &str) -> LoroResult<()> {
4419 match &self.inner {
4420 MaybeDetached::Detached(m) => {
4421 let mut m = m.lock();
4422 m.value.remove(key);
4423 Ok(())
4424 }
4425 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, key)),
4426 }
4427 }
4428
4429 pub fn delete_with_txn(&self, txn: &mut Transaction, key: &str) -> LoroResult<()> {
4430 let inner = self.inner.try_attached_state()?;
4431 txn.apply_local_op(
4432 inner.container_idx,
4433 crate::op::RawOpContent::Map(crate::container::map::MapSet {
4434 key: key.into(),
4435 value: None,
4436 }),
4437 EventHint::Map {
4438 key: key.into(),
4439 value: None,
4440 },
4441 &inner.doc,
4442 )
4443 }
4444
4445 pub fn for_each<I>(&self, mut f: I)
4446 where
4447 I: FnMut(&str, ValueOrHandler),
4448 {
4449 match &self.inner {
4450 MaybeDetached::Detached(m) => {
4451 let m = m.lock();
4452 for (k, v) in m.value.iter() {
4453 f(k, v.clone());
4454 }
4455 }
4456 MaybeDetached::Attached(inner) => {
4457 let temp = inner.with_doc_state(|state| {
4458 state
4459 .get_map_entries(inner.container_idx)
4460 .into_iter()
4461 .map(|(key, value)| {
4462 let translated = loro_common::translate_mergeable_marker_value(
4463 &inner.id,
4464 key.as_ref(),
4465 value,
4466 );
4467 (
4468 key.to_string(),
4469 value_to_value_or_handler(inner, translated),
4470 )
4471 })
4472 .collect::<Vec<_>>()
4473 });
4474
4475 for (k, v) in temp.into_iter() {
4476 f(&k, v.clone());
4477 }
4478 }
4479 }
4480 }
4481
4482 pub fn get_child_handler(&self, key: &str) -> LoroResult<Handler> {
4483 match &self.inner {
4484 MaybeDetached::Detached(m) => {
4485 let m = m.lock();
4486 let value = m.value.get(key).unwrap();
4487 match value {
4488 ValueOrHandler::Value(v) => Err(LoroError::ArgErr(
4489 format!("Expected Handler but found {:?}", v).into_boxed_str(),
4490 )),
4491 ValueOrHandler::Handler(h) => Ok(h.clone()),
4492 }
4493 }
4494 MaybeDetached::Attached(_) => {
4495 let Some(value) = self.get_(key) else {
4496 return Err(LoroError::ArgErr(
4497 format!("Key {key} does not exist").into_boxed_str(),
4498 ));
4499 };
4500 match value {
4501 ValueOrHandler::Handler(handler) => Ok(handler),
4502 ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
4503 format!("Expected Handler but found {:?}", value).into_boxed_str(),
4504 )),
4505 }
4506 }
4507 }
4508 }
4509
4510 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
4511 match &self.inner {
4512 MaybeDetached::Detached(_) => Err(LoroError::MisuseDetachedContainer {
4513 method: "get_deep_value_with_id",
4514 }),
4515 MaybeDetached::Attached(inner) => Ok(inner.with_doc_state(|state| {
4516 state.get_container_deep_value_with_id(inner.container_idx, None)
4517 })),
4518 }
4519 }
4520
4521 pub fn get(&self, key: &str) -> Option<LoroValue> {
4522 match &self.inner {
4523 MaybeDetached::Detached(m) => {
4524 let m = m.lock();
4525 m.value.get(key).map(|v| v.to_value())
4526 }
4527 MaybeDetached::Attached(inner) => {
4528 let value = inner
4529 .with_doc_state(|state| state.get_map_value_by_key(inner.container_idx, key))?;
4530 Some(loro_common::translate_mergeable_marker_value(
4531 &inner.id, key, value,
4532 ))
4533 }
4534 }
4535 }
4536
4537 pub fn get_(&self, key: &str) -> Option<ValueOrHandler> {
4539 match &self.inner {
4540 MaybeDetached::Detached(m) => {
4541 let m = m.lock();
4542 m.value.get(key).cloned()
4543 }
4544 MaybeDetached::Attached(inner) => {
4545 let value = inner
4546 .with_doc_state(|state| state.get_map_value_by_key(inner.container_idx, key))?;
4547 let value = loro_common::translate_mergeable_marker_value(&inner.id, key, value);
4548 Some(value_to_value_or_handler(inner, value))
4549 }
4550 }
4551 }
4552
4553 #[deprecated(
4560 note = "use ensure_mergeable_map/list/movable_list/text/tree/counter for lazy map-key child creation; this method creates regular op-id children"
4561 )]
4562 pub fn get_or_create_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4563 if let Some(ans) = self.get_(key) {
4564 if let ValueOrHandler::Handler(h) = ans {
4565 let kind = h.kind();
4566 return C::from_handler(h).ok_or_else(move || {
4567 LoroError::ArgErr(
4568 format!("Expected value type {} but found {:?}", child.kind(), kind)
4569 .into_boxed_str(),
4570 )
4571 });
4572 } else if let ValueOrHandler::Value(LoroValue::Null) = ans {
4573 } else {
4575 return Err(LoroError::ArgErr(
4576 format!("Expected value type {} but found {:?}", child.kind(), ans)
4577 .into_boxed_str(),
4578 ));
4579 }
4580 }
4581
4582 self.insert_container(key, child)
4583 }
4584
4585 fn ensure_mergeable_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4603 let MaybeDetached::Attached(parent) = &self.inner else {
4604 return Err(LoroError::MisuseDetachedContainer {
4605 method: "ensure_mergeable_container",
4606 });
4607 };
4608
4609 let existing_raw =
4613 parent.with_doc_state(|state| state.get_map_value_by_key(parent.container_idx, key));
4614
4615 if let Some(existing) = &existing_raw {
4620 if !matches!(existing, LoroValue::Null)
4621 && loro_common::parse_mergeable_marker(&parent.id, key, existing).is_none()
4622 {
4623 return Err(LoroError::ArgErr(
4624 format!(
4625 "Cannot create a mergeable {} at key {key:?}: the key already holds a non-mergeable value",
4626 child.kind()
4627 )
4628 .into_boxed_str(),
4629 ));
4630 }
4631 }
4632
4633 let cid = ContainerID::new_mergeable(&parent.id, key, child.kind());
4634 let marker = loro_common::mergeable_marker(&parent.id, key, child.kind());
4635
4636 if existing_raw.as_ref() != Some(&marker) {
4640 self.insert(key, marker)?;
4641 }
4642
4643 C::from_handler(create_handler(parent, cid.clone())).ok_or_else(|| {
4644 LoroError::ArgErr(
4645 format!(
4646 "Expected value type {} but found {}",
4647 child.kind(),
4648 cid.container_type()
4649 )
4650 .into_boxed_str(),
4651 )
4652 })
4653 }
4654
4655 #[cfg(feature = "counter")]
4656 pub fn ensure_mergeable_counter(&self, key: &str) -> LoroResult<counter::CounterHandler> {
4663 self.ensure_mergeable_container(key, counter::CounterHandler::new_detached())
4664 }
4665
4666 pub fn ensure_mergeable_map(&self, key: &str) -> LoroResult<MapHandler> {
4677 self.ensure_mergeable_container(key, MapHandler::new_detached())
4678 }
4679
4680 pub fn ensure_mergeable_list(&self, key: &str) -> LoroResult<ListHandler> {
4687 self.ensure_mergeable_container(key, ListHandler::new_detached())
4688 }
4689
4690 pub fn ensure_mergeable_movable_list(&self, key: &str) -> LoroResult<MovableListHandler> {
4697 self.ensure_mergeable_container(key, MovableListHandler::new_detached())
4698 }
4699
4700 pub fn ensure_mergeable_text(&self, key: &str) -> LoroResult<TextHandler> {
4707 self.ensure_mergeable_container(key, TextHandler::new_detached())
4708 }
4709
4710 pub fn ensure_mergeable_tree(&self, key: &str) -> LoroResult<TreeHandler> {
4717 self.ensure_mergeable_container(key, TreeHandler::new_detached())
4718 }
4719
4720 pub fn contains_key(&self, key: &str) -> bool {
4721 self.get(key).is_some()
4722 }
4723
4724 pub fn len(&self) -> usize {
4725 match &self.inner {
4726 MaybeDetached::Detached(m) => m.lock().value.len(),
4727 MaybeDetached::Attached(a) => {
4728 a.with_doc_state(|state| state.get_map_len(a.container_idx))
4729 }
4730 }
4731 }
4732
4733 pub fn is_empty(&self) -> bool {
4734 self.len() == 0
4735 }
4736
4737 pub fn is_deleted(&self) -> bool {
4738 match &self.inner {
4739 MaybeDetached::Detached(_) => false,
4740 MaybeDetached::Attached(a) => a.is_deleted(),
4741 }
4742 }
4743
4744 pub fn clear(&self) -> LoroResult<()> {
4745 match &self.inner {
4746 MaybeDetached::Detached(m) => {
4747 let mut m = m.lock();
4748 m.value.clear();
4749 Ok(())
4750 }
4751 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
4752 }
4753 }
4754
4755 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
4756 let keys: Vec<InternalString> = self.inner.try_attached_state()?.with_state(|state| {
4757 state
4758 .as_map_state()
4759 .unwrap()
4760 .iter()
4761 .map(|(k, _)| k.clone())
4762 .collect()
4763 });
4764
4765 for key in keys {
4766 self.delete_with_txn(txn, &key)?;
4767 }
4768
4769 Ok(())
4770 }
4771
4772 pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
4773 let keys: Vec<InternalString> = match &self.inner {
4774 MaybeDetached::Detached(m) => {
4775 let m = m.lock();
4776 m.value.keys().map(|x| x.as_str().into()).collect()
4777 }
4778 MaybeDetached::Attached(a) => {
4779 a.with_doc_state(|state| state.get_map_keys(a.container_idx))
4780 }
4781 };
4782
4783 keys.into_iter()
4784 }
4785
4786 pub fn values(&self) -> impl Iterator<Item = ValueOrHandler> + '_ {
4787 let values: Vec<ValueOrHandler> = match &self.inner {
4788 MaybeDetached::Detached(m) => {
4789 let m = m.lock();
4790 m.value.values().cloned().collect()
4791 }
4792 MaybeDetached::Attached(a) => a.with_doc_state(|state| {
4793 state
4797 .get_map_entries(a.container_idx)
4798 .into_iter()
4799 .map(|(key, value)| {
4800 let translated = loro_common::translate_mergeable_marker_value(
4801 &a.id,
4802 key.as_ref(),
4803 value,
4804 );
4805 value_to_value_or_handler(a, translated)
4806 })
4807 .collect()
4808 }),
4809 };
4810
4811 values.into_iter()
4812 }
4813
4814 pub fn get_last_editor(&self, key: &str) -> Option<PeerID> {
4815 match &self.inner {
4816 MaybeDetached::Detached(_) => None,
4817 MaybeDetached::Attached(a) => a.with_state(|state| {
4818 let m = state.as_map_state().unwrap();
4819 m.get_last_edit_peer(key)
4820 }),
4821 }
4822 }
4823}
4824
4825fn with_txn<R>(doc: &LoroDoc, f: impl FnOnce(&mut Transaction) -> LoroResult<R>) -> LoroResult<R> {
4826 let txn = &doc.txn;
4827 let mut txn = txn.lock();
4828 loop {
4829 if let Some(txn) = &mut *txn {
4830 return f(txn);
4831 } else if cfg!(target_arch = "wasm32") || !doc.can_edit() {
4832 return Err(LoroError::AutoCommitNotStarted);
4833 } else {
4834 drop(txn);
4835 #[cfg(loom)]
4836 loom::thread::yield_now();
4837 doc.start_auto_commit();
4838 txn = doc.txn.lock();
4839 }
4840 }
4841}
4842
4843#[cfg(feature = "counter")]
4844pub mod counter {
4845
4846 use loro_common::LoroResult;
4847
4848 use crate::{
4849 txn::{EventHint, Transaction},
4850 HandlerTrait,
4851 };
4852
4853 use super::{create_handler, Handler, MaybeDetached};
4854
4855 #[derive(Clone)]
4856 pub struct CounterHandler {
4857 pub(super) inner: MaybeDetached<f64>,
4858 }
4859
4860 impl CounterHandler {
4861 pub fn new_detached() -> Self {
4862 Self {
4863 inner: MaybeDetached::new_detached(0.),
4864 }
4865 }
4866
4867 pub fn increment(&self, n: f64) -> LoroResult<()> {
4868 match &self.inner {
4869 MaybeDetached::Detached(d) => {
4870 let d = &mut d.lock().value;
4871 *d += n;
4872 Ok(())
4873 }
4874 MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, n)),
4875 }
4876 }
4877
4878 pub fn decrement(&self, n: f64) -> LoroResult<()> {
4879 match &self.inner {
4880 MaybeDetached::Detached(d) => {
4881 let d = &mut d.lock().value;
4882 *d -= n;
4883 Ok(())
4884 }
4885 MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, -n)),
4886 }
4887 }
4888
4889 fn increment_with_txn(&self, txn: &mut Transaction, n: f64) -> LoroResult<()> {
4890 let inner = self.inner.try_attached_state()?;
4891 txn.apply_local_op(
4892 inner.container_idx,
4893 crate::op::RawOpContent::Counter(n),
4894 EventHint::Counter(n),
4895 &inner.doc,
4896 )
4897 }
4898
4899 pub fn is_deleted(&self) -> bool {
4900 match &self.inner {
4901 MaybeDetached::Detached(_) => false,
4902 MaybeDetached::Attached(a) => a.is_deleted(),
4903 }
4904 }
4905
4906 pub fn clear(&self) -> LoroResult<()> {
4907 self.decrement(self.get_value().into_double().unwrap())
4908 }
4909 }
4910
4911 impl std::fmt::Debug for CounterHandler {
4912 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4913 match &self.inner {
4914 MaybeDetached::Detached(_) => write!(f, "CounterHandler Detached"),
4915 MaybeDetached::Attached(a) => write!(f, "CounterHandler {}", a.id),
4916 }
4917 }
4918 }
4919
4920 impl HandlerTrait for CounterHandler {
4921 fn is_attached(&self) -> bool {
4922 matches!(&self.inner, MaybeDetached::Attached(..))
4923 }
4924
4925 fn attached_handler(&self) -> Option<&crate::BasicHandler> {
4926 self.inner.attached_handler()
4927 }
4928
4929 fn get_value(&self) -> loro_common::LoroValue {
4930 match &self.inner {
4931 MaybeDetached::Detached(t) => {
4932 let t = t.lock();
4933 t.value.into()
4934 }
4935 MaybeDetached::Attached(a) => a.get_value(),
4936 }
4937 }
4938
4939 fn get_deep_value(&self) -> loro_common::LoroValue {
4940 self.get_value()
4941 }
4942
4943 fn kind(&self) -> loro_common::ContainerType {
4944 loro_common::ContainerType::Counter
4945 }
4946
4947 fn to_handler(&self) -> super::Handler {
4948 Handler::Counter(self.clone())
4949 }
4950
4951 fn from_handler(h: super::Handler) -> Option<Self> {
4952 match h {
4953 Handler::Counter(x) => Some(x),
4954 _ => None,
4955 }
4956 }
4957
4958 fn attach(
4959 &self,
4960 txn: &mut crate::txn::Transaction,
4961 parent: &crate::BasicHandler,
4962 self_id: loro_common::ContainerID,
4963 ) -> loro_common::LoroResult<Self> {
4964 match &self.inner {
4965 MaybeDetached::Detached(v) => {
4966 let mut v = v.lock();
4967 let inner = create_handler(parent, self_id);
4968 let c = inner.into_counter().unwrap();
4969
4970 c.increment_with_txn(txn, v.value)?;
4971
4972 v.attached = c.attached_handler().cloned();
4973 Ok(c)
4974 }
4975 MaybeDetached::Attached(a) => {
4976 let new_inner = create_handler(a, self_id);
4977 let ans = new_inner.into_counter().unwrap();
4978 let delta = *self.get_value().as_double().unwrap();
4979 ans.increment_with_txn(txn, delta)?;
4980 Ok(ans)
4981 }
4982 }
4983 }
4984
4985 fn get_attached(&self) -> Option<Self> {
4986 match &self.inner {
4987 MaybeDetached::Attached(a) => Some(Self {
4988 inner: MaybeDetached::Attached(a.clone()),
4989 }),
4990 MaybeDetached::Detached(v) => v.lock().attached.clone().map(|x| Self {
4991 inner: MaybeDetached::Attached(x),
4992 }),
4993 }
4994 }
4995
4996 fn doc(&self) -> Option<crate::LoroDoc> {
4997 match &self.inner {
4998 MaybeDetached::Detached(_) => None,
4999 MaybeDetached::Attached(a) => Some(a.doc()),
5000 }
5001 }
5002 }
5003}
5004
5005#[cfg(test)]
5006mod test {
5007 use std::borrow::Cow;
5008
5009 use super::{
5010 Handler, HandlerTrait, ListHandler, MapHandler, MovableListHandler, TextDelta, TextHandler,
5011 ValueOrHandler,
5012 };
5013 use crate::container::list::list_op::ListOp;
5014 use crate::cursor::PosType;
5015 use crate::loro::ExportMode;
5016 use crate::op::ListSlice;
5017 use crate::state::TreeParentId;
5018 use crate::txn::EventHint;
5019 use crate::version::Frontiers;
5020 use crate::LoroDoc;
5021 use crate::{fx_map, ToJson};
5022 use loro_common::{ContainerID, ContainerType, LoroError, LoroValue, ID};
5023 use serde_json::json;
5024
5025 fn recheck_fast_blob(mut bytes: Vec<u8>) -> Vec<u8> {
5026 let checksum = xxhash_rust::xxh32::xxh32(&bytes[20..], u32::from_le_bytes(*b"LORO"));
5027 bytes[16..20].copy_from_slice(&checksum.to_le_bytes());
5028 bytes
5029 }
5030
5031 fn replace_fast_snapshot_state_bytes(mut snapshot: Vec<u8>, state_bytes: &[u8]) -> Vec<u8> {
5032 let mut body = &snapshot[22..];
5033 let oplog_len = u32::from_le_bytes(body[..4].try_into().unwrap()) as usize;
5034 body = &body[4 + oplog_len..];
5035 let old_state_len = u32::from_le_bytes(body[..4].try_into().unwrap()) as usize;
5036 let state_len_pos = 22 + 4 + oplog_len;
5037 let state_start = state_len_pos + 4;
5038 let state_end = state_start + old_state_len;
5039 snapshot[state_len_pos..state_start]
5040 .copy_from_slice(&(state_bytes.len() as u32).to_le_bytes());
5041 snapshot.splice(state_start..state_end, state_bytes.iter().copied());
5042 recheck_fast_blob(snapshot)
5043 }
5044
5045 fn insert_many_with_single_list_op(
5046 txn: &mut crate::txn::Transaction,
5047 list: &crate::handler::ListHandler,
5048 pos: usize,
5049 values: Vec<LoroValue>,
5050 ) {
5051 let len = values.len();
5052 let inner = list.inner.try_attached_state().unwrap();
5053 txn.apply_local_op(
5054 inner.container_idx,
5055 crate::op::RawOpContent::List(ListOp::Insert {
5056 slice: ListSlice::RawData(Cow::Owned(values)),
5057 pos,
5058 }),
5059 EventHint::InsertList {
5060 len: len as u32,
5061 pos,
5062 },
5063 &inner.doc,
5064 )
5065 .unwrap();
5066 }
5067
5068 #[test]
5069 fn richtext_handler() {
5070 let loro = LoroDoc::new();
5071 loro.set_peer_id(1).unwrap();
5072 let loro2 = LoroDoc::new();
5073 loro2.set_peer_id(2).unwrap();
5074
5075 let mut txn = loro.txn().unwrap();
5076 let text = txn.get_text("hello");
5077 text.insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
5078 .unwrap();
5079 txn.commit().unwrap();
5080 let exported = loro.export(ExportMode::all_updates()).unwrap();
5081
5082 loro2.import(&exported).unwrap();
5083 let mut txn = loro2.txn().unwrap();
5084 let text = txn.get_text("hello");
5085 assert_eq!(&**text.get_value().as_string().unwrap(), "hello");
5086 text.insert_with_txn(&mut txn, 5, " world", PosType::Unicode)
5087 .unwrap();
5088 assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
5089 txn.commit().unwrap();
5090
5091 loro.import(&loro2.export(ExportMode::all_updates()).unwrap())
5092 .unwrap();
5093 let txn = loro.txn().unwrap();
5094 let text = txn.get_text("hello");
5095 assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
5096 txn.commit().unwrap();
5097
5098 loro.checkout(&Frontiers::from_id(ID::new(2, 1))).unwrap();
5100 assert_eq!(&**text.get_value().as_string().unwrap(), "hello w");
5101 }
5102
5103 #[test]
5104 fn richtext_handler_concurrent() {
5105 let loro = LoroDoc::new();
5106 let mut txn = loro.txn().unwrap();
5107 let handler = loro.get_text("richtext");
5108 handler
5109 .insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
5110 .unwrap();
5111 txn.commit().unwrap();
5112 for i in 0..100 {
5113 let new_loro = LoroDoc::new();
5114 new_loro
5115 .import(&loro.export(ExportMode::all_updates()).unwrap())
5116 .unwrap();
5117 let mut txn = new_loro.txn().unwrap();
5118 let handler = new_loro.get_text("richtext");
5119 handler
5120 .insert_with_txn(&mut txn, i % 5, &i.to_string(), PosType::Unicode)
5121 .unwrap();
5122 txn.commit().unwrap();
5123 loro.import(
5124 &new_loro
5125 .export(ExportMode::updates(&loro.oplog_vv()))
5126 .unwrap(),
5127 )
5128 .unwrap();
5129 }
5130 }
5131
5132 #[test]
5133 fn cross_doc_txn_is_rejected() {
5134 let doc_a = LoroDoc::new();
5141 doc_a.set_peer_id(1).unwrap();
5142 let doc_b = LoroDoc::new();
5143 doc_b.set_peer_id(2).unwrap();
5144
5145 {
5147 let mut txn_b = doc_b.txn().unwrap();
5148 doc_b
5149 .get_text("text")
5150 .insert_with_txn(&mut txn_b, 0, "ok", PosType::Unicode)
5151 .unwrap();
5152 txn_b.commit().unwrap();
5153 }
5154 let vv_before = doc_b.oplog_vv();
5155
5156 let mut txn_a = doc_a.txn().unwrap();
5158 let text_b = doc_b.get_text("text");
5159 let insert_err = text_b
5160 .insert_with_txn(&mut txn_a, 0, "x", PosType::Unicode)
5161 .unwrap_err();
5162 assert!(matches!(insert_err, LoroError::UnmatchedContext { .. }));
5163 let delete_err = text_b
5164 .delete_with_txn(&mut txn_a, 0, 1, PosType::Unicode)
5165 .unwrap_err();
5166 assert!(matches!(delete_err, LoroError::UnmatchedContext { .. }));
5167 txn_a.commit().unwrap();
5168
5169 assert_eq!(&**text_b.get_value().as_string().unwrap(), "ok");
5171 assert_eq!(doc_b.oplog_vv(), vv_before);
5172 }
5173
5174 #[test]
5175 fn list_import_batch_stays_consistent_after_repeated_tail_splits() {
5176 let doc_a = LoroDoc::new();
5177 doc_a.set_peer_id(1).unwrap();
5178 let mut txn = doc_a.txn().unwrap();
5179 let list_a = txn.get_list("list");
5180 insert_many_with_single_list_op(
5181 &mut txn,
5182 &list_a,
5183 0,
5184 (0..300).map(|i| LoroValue::I64(i)).collect(),
5185 );
5186 txn.commit().unwrap();
5187
5188 let doc_b = LoroDoc::new();
5189 doc_b.set_peer_id(2).unwrap();
5190 doc_b
5191 .import(&doc_a.export(ExportMode::all_updates()).unwrap())
5192 .unwrap();
5193
5194 let list_b = doc_b.get_list("list");
5195 let mut vv = doc_a.oplog_vv();
5196 let mut updates = Vec::new();
5197 for (i, pos) in [100, 201, 252, 278].into_iter().enumerate() {
5198 list_b.insert(pos, 1000 + i as i64).unwrap();
5199 updates.push(doc_b.export(ExportMode::updates(&vv)).unwrap());
5200 vv = doc_b.oplog_vv();
5201 }
5202
5203 doc_a.import_batch(&updates).unwrap();
5204 doc_a.check_state_diff_calc_consistency_slow();
5205 doc_b.check_state_diff_calc_consistency_slow();
5206 assert_eq!(doc_a.get_deep_value(), doc_b.get_deep_value());
5207 }
5208
5209 #[test]
5210 fn richtext_handler_mark() {
5211 let loro = LoroDoc::new_auto_commit();
5212 let handler = loro.get_text("richtext");
5213 handler.insert(0, "hello world", PosType::Unicode).unwrap();
5214 handler
5215 .mark(0, 5, "bold", true.into(), PosType::Event)
5216 .unwrap();
5217 loro.commit_then_renew();
5218
5219 let value = handler.get_richtext_value();
5221 assert_eq!(value[0]["insert"], "hello".into());
5222 let meta = value[0]["attributes"].as_map().unwrap();
5223 assert_eq!(meta.len(), 1);
5224 meta.get("bold").unwrap();
5225
5226 let loro2 = LoroDoc::new_auto_commit();
5227 loro2
5228 .import(&loro.export(ExportMode::all_updates()).unwrap())
5229 .unwrap();
5230 let handler2 = loro2.get_text("richtext");
5231 assert_eq!(&**handler2.get_value().as_string().unwrap(), "hello world");
5232
5233 let value = handler2.get_richtext_value();
5235 assert_eq!(value[0]["insert"], "hello".into());
5236 let meta = value[0]["attributes"].as_map().unwrap();
5237 assert_eq!(meta.len(), 1);
5238 meta.get("bold").unwrap();
5239
5240 {
5242 handler2.insert(5, " new", PosType::Unicode).unwrap();
5243 let value = handler2.get_richtext_value();
5244 assert_eq!(
5245 value.to_json_value(),
5246 serde_json::json!([
5247 {"insert": "hello new", "attributes": {"bold": true}},
5248 {"insert": " world"}
5249 ])
5250 );
5251 }
5252 }
5253
5254 #[test]
5255 fn richtext_snapshot() {
5256 let loro = LoroDoc::new();
5257 let mut txn = loro.txn().unwrap();
5258 let handler = loro.get_text("richtext");
5259 handler
5260 .insert_with_txn(&mut txn, 0, "hello world", PosType::Unicode)
5261 .unwrap();
5262 handler
5263 .mark_with_txn(&mut txn, 0, 5, "bold", true.into(), PosType::Event)
5264 .unwrap();
5265 txn.commit().unwrap();
5266
5267 let loro2 = LoroDoc::new();
5268 loro2
5269 .import(&loro.export(ExportMode::snapshot()).unwrap())
5270 .unwrap();
5271 let handler2 = loro2.get_text("richtext");
5272 assert_eq!(
5273 handler2.get_richtext_value().to_json_value(),
5274 serde_json::json!([
5275 {"insert": "hello", "attributes": {"bold": true}},
5276 {"insert": " world"}
5277 ])
5278 );
5279 }
5280
5281 #[test]
5282 fn text_snapshot_string_queries_do_not_decode_state() {
5283 let loro = LoroDoc::new_auto_commit();
5284 let text = loro.get_text("text");
5285 text.insert(0, "a😀文", PosType::Unicode).unwrap();
5286 text.mark(1, 3, "bold", true.into(), PosType::Unicode)
5287 .unwrap();
5288
5289 let restored = LoroDoc::new();
5290 restored
5291 .import(&loro.export(ExportMode::snapshot()).unwrap())
5292 .unwrap();
5293 let text = restored.get_text("text");
5294 assert!(!text.attached_handler().unwrap().has_decoded_state());
5295
5296 assert_eq!(text.len_unicode(), 3);
5297 assert_eq!(text.len_utf16(), 4);
5298 assert_eq!(text.len_utf8(), "a😀文".len());
5299 assert_eq!(text.char_at(1, PosType::Unicode).unwrap(), '😀');
5300 assert_eq!(text.slice(1, 3, PosType::Unicode).unwrap(), "😀文");
5301 assert_eq!(
5302 text.convert_pos(2, PosType::Unicode, PosType::Utf16),
5303 Some(3)
5304 );
5305 assert!(matches!(
5306 text.delete_utf16(2, 1),
5307 Err(LoroError::UTF16InUnicodeCodePoint { pos: 2 })
5308 ));
5309 assert!(matches!(
5310 text.delete_utf8(2, 1),
5311 Err(LoroError::UTF8InUnicodeCodePoint { pos: 2 })
5312 ));
5313 assert!(matches!(
5314 text.slice_delta(2, 3, PosType::Utf16),
5315 Err(LoroError::UTF16InUnicodeCodePoint { pos: 2 })
5316 ));
5317 assert!(matches!(
5318 text.slice_delta(2, 3, PosType::Bytes),
5319 Err(LoroError::UTF8InUnicodeCodePoint { pos: 2 })
5320 ));
5321 assert!(!text.attached_handler().unwrap().has_decoded_state());
5322
5323 assert_eq!(text.get_delta().len(), 2);
5324 assert!(text.attached_handler().unwrap().has_decoded_state());
5325 }
5326
5327 #[test]
5328 fn text_lazy_event_queries_match_decoded_state() {
5329 let loro = LoroDoc::new_auto_commit();
5330 let text = loro.get_text("text");
5331 text.insert(0, "ab😀cd", PosType::Unicode).unwrap();
5332 text.mark(1, 4, "bold", true.into(), PosType::Unicode)
5333 .unwrap();
5334 text.mark(2, 3, "link", "x".into(), PosType::Unicode)
5335 .unwrap();
5336
5337 let lazy_doc = LoroDoc::new();
5338 lazy_doc
5339 .import(&loro.export(ExportMode::snapshot()).unwrap())
5340 .unwrap();
5341 let lazy_text = lazy_doc.get_text("text");
5342
5343 let decoded_doc = LoroDoc::new();
5344 decoded_doc
5345 .import(&loro.export(ExportMode::snapshot()).unwrap())
5346 .unwrap();
5347 let decoded_text = decoded_doc.get_text("text");
5348 decoded_text.get_delta();
5349
5350 assert!(!lazy_text.attached_handler().unwrap().has_decoded_state());
5351 assert!(decoded_text.attached_handler().unwrap().has_decoded_state());
5352
5353 for pos_type in [
5354 PosType::Event,
5355 PosType::Unicode,
5356 PosType::Utf16,
5357 PosType::Bytes,
5358 ] {
5359 assert_eq!(lazy_text.len(pos_type), decoded_text.len(pos_type));
5360 for pos in 0..=decoded_text.len(pos_type) {
5361 assert_eq!(
5362 lazy_text.convert_pos(pos, pos_type, PosType::Unicode),
5363 decoded_text.convert_pos(pos, pos_type, PosType::Unicode),
5364 "convert {pos_type:?} pos {pos} to unicode"
5365 );
5366 assert_eq!(
5367 lazy_text.convert_pos(pos, pos_type, PosType::Event),
5368 decoded_text.convert_pos(pos, pos_type, PosType::Event),
5369 "convert {pos_type:?} pos {pos} to event"
5370 );
5371 if pos < decoded_text.len(pos_type) {
5372 assert_eq!(
5373 lazy_text.char_at(pos, pos_type),
5374 decoded_text.char_at(pos, pos_type),
5375 "char_at {pos_type:?} pos {pos}"
5376 );
5377 }
5378 for end in pos..=decoded_text.len(pos_type) {
5379 assert_eq!(
5380 lazy_text.slice(pos, end, pos_type),
5381 decoded_text.slice(pos, end, pos_type),
5382 "slice {pos_type:?} {pos}..{end}"
5383 );
5384 }
5385 }
5386 }
5387 }
5388
5389 #[test]
5390 fn deep_value_with_id_uses_lazy_values_for_snapshot_roots() {
5391 let loro = LoroDoc::new_auto_commit();
5392 let text = loro.get_text("text");
5393 text.insert(0, "hello", PosType::Unicode).unwrap();
5394 let map = loro.get_map("map");
5395 map.insert("key", "value").unwrap();
5396 let list = loro.get_list("list");
5397 list.push("item").unwrap();
5398
5399 let restored = LoroDoc::new();
5400 restored
5401 .import(&loro.export(ExportMode::snapshot()).unwrap())
5402 .unwrap();
5403 let text = restored.get_text("text");
5404 let map = restored.get_map("map");
5405 let list = restored.get_list("list");
5406
5407 let value = restored.get_deep_value_with_id();
5408 assert_eq!(value["text"]["value"], "hello".into());
5409 assert_eq!(value["map"]["value"]["key"], "value".into());
5410 assert_eq!(value["list"]["value"][0], "item".into());
5411 assert!(!text.attached_handler().unwrap().has_decoded_state());
5412 assert!(!map.attached_handler().unwrap().has_decoded_state());
5413 assert!(!list.attached_handler().unwrap().has_decoded_state());
5414 }
5415
5416 #[test]
5417 fn lazy_value_reads_do_not_write_stale_snapshot_after_mutation() {
5418 let loro = LoroDoc::new_auto_commit();
5419 let map = loro.get_map("map");
5420 map.insert("key", "old").unwrap();
5421 let child = map
5422 .insert_container("child", MapHandler::new_detached())
5423 .unwrap();
5424 child.insert("nested", "old").unwrap();
5425 let list = loro.get_list("list");
5426 list.push("old").unwrap();
5427 let child_list = list.push_container(ListHandler::new_detached()).unwrap();
5428 child_list.push("nested-old").unwrap();
5429
5430 let restored = LoroDoc::new();
5431 restored
5432 .import(&loro.export(ExportMode::snapshot()).unwrap())
5433 .unwrap();
5434 let map = restored.get_map("map");
5435 let list = restored.get_list("list");
5436
5437 assert_eq!(map.get("key").unwrap(), "old".into());
5438 assert_eq!(list.get(0).unwrap(), "old".into());
5439 let child = match map.get_("child").unwrap() {
5440 ValueOrHandler::Handler(handler) => handler.into_map().unwrap(),
5441 ValueOrHandler::Value(value) => panic!("expected child map, got {value:?}"),
5442 };
5443 let child_list = match list.get_(1).unwrap() {
5444 ValueOrHandler::Handler(handler) => handler.into_list().unwrap(),
5445 ValueOrHandler::Value(value) => panic!("expected child list, got {value:?}"),
5446 };
5447
5448 map.insert("key", "new").unwrap();
5449 child.insert("nested", "new").unwrap();
5450 list.delete(0, 1).unwrap();
5451 list.insert(0, "new").unwrap();
5452 child_list.delete(0, 1).unwrap();
5453 child_list.insert(0, "nested-new").unwrap();
5454 restored.commit_then_renew();
5455
5456 let roundtrip = LoroDoc::new();
5457 roundtrip
5458 .import(&restored.export(ExportMode::snapshot()).unwrap())
5459 .unwrap();
5460 assert_eq!(
5461 roundtrip.get_deep_value().to_json_value(),
5462 serde_json::json!({
5463 "map": { "key": "new", "child": { "nested": "new" } },
5464 "list": ["new", ["nested-new"]]
5465 })
5466 );
5467 }
5468
5469 #[test]
5470 fn fast_snapshot_with_trailing_bytes_is_rejected_on_import() {
5471 let loro = LoroDoc::new_auto_commit();
5472 let map = loro.get_map("map");
5473 map.insert("key", "value").unwrap();
5474 let mut snapshot = loro.export(ExportMode::snapshot()).unwrap();
5475 snapshot.push(0xff);
5476 let corrupted = recheck_fast_blob(snapshot);
5477
5478 let doc = LoroDoc::new();
5479 assert!(doc.import(&corrupted).is_err());
5480 }
5481
5482 #[test]
5483 fn fast_snapshot_with_trailing_bytes_is_rejected_by_meta_decoder() {
5484 let loro = LoroDoc::new_auto_commit();
5485 let map = loro.get_map("map");
5486 map.insert("key", "value").unwrap();
5487 let mut snapshot = loro.export(ExportMode::snapshot()).unwrap();
5488 snapshot.push(0xff);
5489 let corrupted = recheck_fast_blob(snapshot);
5490
5491 assert!(LoroDoc::decode_import_blob_meta(&corrupted, true).is_err());
5492 }
5493
5494 #[test]
5495 fn fast_snapshot_empty_sstable_meta_is_rejected_on_import() {
5496 let loro = LoroDoc::new_auto_commit();
5497 let map = loro.get_map("map");
5498 map.insert("key", "value").unwrap();
5499 let snapshot = loro.export(ExportMode::snapshot()).unwrap();
5500
5501 let mut malformed_state = Vec::new();
5502 malformed_state.extend_from_slice(b"LORO");
5503 malformed_state.push(0);
5504 malformed_state.extend_from_slice(&0u32.to_le_bytes());
5505 let checksum = xxhash_rust::xxh32::xxh32(&[], u32::from_le_bytes(*b"LORO"));
5506 malformed_state.extend_from_slice(&checksum.to_le_bytes());
5507 malformed_state.extend_from_slice(&5u32.to_le_bytes());
5508 let corrupted = replace_fast_snapshot_state_bytes(snapshot, &malformed_state);
5509
5510 let doc = LoroDoc::new();
5511 assert!(doc.import(&corrupted).is_err());
5512 }
5513
5514 #[test]
5515 fn tree_meta() {
5516 let loro = LoroDoc::new_auto_commit();
5517 loro.set_peer_id(1).unwrap();
5518 let tree = loro.get_tree("root");
5519 let id = tree.create(TreeParentId::Root).unwrap();
5520 let meta = tree.get_meta(id).unwrap();
5521 meta.insert("a", 123).unwrap();
5522 loro.commit_then_renew();
5523 let meta = tree.get_meta(id).unwrap();
5524 assert_eq!(meta.get("a").unwrap(), 123.into());
5525 assert_eq!(
5526 json!([{"parent":null,"meta":{"a":123},"id":"0@1","index":0,"children":[],"fractional_index":"80"}]),
5527 tree.get_deep_value().to_json_value()
5528 );
5529 let bytes = loro.export(ExportMode::snapshot()).unwrap();
5530 let loro2 = LoroDoc::new();
5531 loro2.import(&bytes).unwrap();
5532 }
5533
5534 #[test]
5535 fn tree_meta_event() {
5536 use std::sync::Arc;
5537 let loro = LoroDoc::new_auto_commit();
5538 let tree = loro.get_tree("root");
5539 let text = loro.get_text("text");
5540
5541 let id = tree.create(TreeParentId::Root).unwrap();
5542 let meta = tree.get_meta(id).unwrap();
5543 meta.insert("a", 1).unwrap();
5544 text.insert(0, "abc", PosType::Unicode).unwrap();
5545 let _id2 = tree.create(TreeParentId::Root).unwrap();
5546 meta.insert("b", 2).unwrap();
5547
5548 let loro2 = LoroDoc::new_auto_commit();
5549 let _g = loro2.subscribe_root(Arc::new(|e| {
5550 println!("{} {:?} ", e.event_meta.by, e.event_meta.diff)
5551 }));
5552 loro2
5553 .import(&loro.export(ExportMode::all_updates()).unwrap())
5554 .unwrap();
5555 assert_eq!(loro.get_deep_value(), loro2.get_deep_value());
5556 }
5557
5558 #[test]
5559 fn richtext_apply_delta() {
5560 let loro = LoroDoc::new_auto_commit();
5561 let text = loro.get_text("text");
5562 text.apply_delta(&[TextDelta::Insert {
5563 insert: "Hello World!".into(),
5564 attributes: None,
5565 }])
5566 .unwrap();
5567 dbg!(text.get_richtext_value());
5568 text.apply_delta(&[
5569 TextDelta::Retain {
5570 retain: 6,
5571 attributes: Some(fx_map!("italic".into() => loro_common::LoroValue::Bool(true))),
5572 },
5573 TextDelta::Insert {
5574 insert: "New ".into(),
5575 attributes: Some(fx_map!("bold".into() => loro_common::LoroValue::Bool(true))),
5576 },
5577 ])
5578 .unwrap();
5579 dbg!(text.get_richtext_value());
5580 loro.commit_then_renew();
5581 assert_eq!(
5582 text.get_richtext_value().to_json_value(),
5583 json!([
5584 {"insert": "Hello ", "attributes": {"italic": true}},
5585 {"insert": "New ", "attributes": {"bold": true}},
5586 {"insert": "World!"}
5587
5588 ])
5589 )
5590 }
5591
5592 #[test]
5593 fn richtext_apply_delta_marks_without_growth() {
5594 let loro = LoroDoc::new_auto_commit();
5595 let text = loro.get_text("text");
5596 text.insert(0, "abc", PosType::Unicode).unwrap();
5597
5598 text.apply_delta(&[TextDelta::Retain {
5599 retain: 3,
5600 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5601 }])
5602 .unwrap();
5603 loro.commit_then_renew();
5604
5605 assert_eq!(text.to_string(), "abc");
5606 assert_eq!(
5607 text.get_richtext_value().to_json_value(),
5608 json!([{"insert": "abc", "attributes": {"bold": true}}])
5609 );
5610 }
5611
5612 #[test]
5613 fn richtext_apply_delta_grows_for_mark_gap() {
5614 let loro = LoroDoc::new_auto_commit();
5615 let text = loro.get_text("text");
5616
5617 text.apply_delta(&[TextDelta::Retain {
5618 retain: 1,
5619 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5620 }])
5621 .unwrap();
5622 loro.commit_then_renew();
5623
5624 assert_eq!(text.to_string(), "\n");
5625 assert_eq!(
5626 text.get_richtext_value().to_json_value(),
5627 json!([{"insert": "\n", "attributes": {"bold": true}}])
5628 );
5629 }
5630
5631 #[test]
5632 fn richtext_apply_delta_ignores_empty_inserts() {
5633 let loro = LoroDoc::new_auto_commit();
5634 let text = loro.get_text("text");
5635 text.insert(0, "seed", PosType::Unicode).unwrap();
5636
5637 text.apply_delta(&[TextDelta::Insert {
5638 insert: "".into(),
5639 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5640 }])
5641 .unwrap();
5642 loro.commit_then_renew();
5643
5644 assert_eq!(text.to_string(), "seed");
5645 assert_eq!(
5646 text.get_richtext_value().to_json_value(),
5647 json!([{"insert": "seed"}])
5648 );
5649 }
5650
5651 #[test]
5652 fn handler_trait_dispatch_reports_attached_container_identity() {
5653 let loro = LoroDoc::new_auto_commit();
5654 let handlers = [
5655 (loro.get_text("text").to_handler(), ContainerType::Text),
5656 (loro.get_map("map").to_handler(), ContainerType::Map),
5657 (loro.get_list("list").to_handler(), ContainerType::List),
5658 (
5659 loro.get_movable_list("movable").to_handler(),
5660 ContainerType::MovableList,
5661 ),
5662 (loro.get_tree("tree").to_handler(), ContainerType::Tree),
5663 ];
5664
5665 for (handler, expected_type) in handlers {
5666 assert!(handler.is_attached());
5667 assert!(handler.attached_handler().is_some());
5668 assert!(handler.doc().is_some());
5669 assert!(handler.get_attached().is_some());
5670 assert_eq!(handler.kind(), expected_type);
5671 assert_eq!(handler.c_type(), expected_type);
5672 assert_eq!(handler.id().container_type(), expected_type);
5673 assert_eq!(
5674 Handler::from_handler(handler.clone()).unwrap().c_type(),
5675 expected_type
5676 );
5677
5678 handler.get_value();
5679 handler.get_deep_value();
5680 handler.clear().unwrap();
5681 }
5682 }
5683
5684 #[test]
5685 fn handler_trait_dispatch_reports_detached_container_identity() {
5686 let handlers = [
5687 (
5688 Handler::new_unattached(ContainerType::Text),
5689 ContainerType::Text,
5690 ),
5691 (
5692 Handler::new_unattached(ContainerType::Map),
5693 ContainerType::Map,
5694 ),
5695 (
5696 Handler::new_unattached(ContainerType::List),
5697 ContainerType::List,
5698 ),
5699 (
5700 Handler::new_unattached(ContainerType::MovableList),
5701 ContainerType::MovableList,
5702 ),
5703 (
5704 Handler::new_unattached(ContainerType::Tree),
5705 ContainerType::Tree,
5706 ),
5707 ];
5708
5709 for (handler, expected_type) in handlers {
5710 assert!(!handler.is_attached());
5711 assert!(handler.attached_handler().is_none());
5712 assert!(handler.doc().is_none());
5713 assert!(handler.get_attached().is_none());
5714 assert_eq!(handler.kind(), expected_type);
5715 assert_eq!(handler.c_type(), expected_type);
5716 assert_eq!(handler.id().container_type(), expected_type);
5717 assert_eq!(handler.idx().get_type(), expected_type);
5718 assert_eq!(
5719 Handler::from_handler(handler.clone()).unwrap().c_type(),
5720 expected_type
5721 );
5722 }
5723 }
5724
5725 #[test]
5726 fn attaching_detached_handlers_sets_parent_and_attached_back_reference() {
5727 let loro = LoroDoc::new_auto_commit();
5728
5729 let map = loro.get_map("map");
5730 let detached_text = TextHandler::new_detached();
5731 detached_text
5732 .insert(0, "detached", PosType::Unicode)
5733 .unwrap();
5734 let attached_text = map.insert_container("text", detached_text.clone()).unwrap();
5735 assert!(attached_text.is_attached());
5736 assert_eq!(attached_text.to_string(), "detached");
5737 assert_eq!(attached_text.parent().unwrap().c_type(), ContainerType::Map);
5738 assert_eq!(
5739 detached_text.get_attached().unwrap().id(),
5740 attached_text.id()
5741 );
5742
5743 let list = loro.get_list("list");
5744 let detached_map = MapHandler::new_detached();
5745 detached_map.insert("k", 1_i64).unwrap();
5746 let attached_map = list.insert_container(0, detached_map.clone()).unwrap();
5747 assert!(attached_map.is_attached());
5748 assert_eq!(attached_map.parent().unwrap().c_type(), ContainerType::List);
5749 assert_eq!(detached_map.get_attached().unwrap().id(), attached_map.id());
5750
5751 let movable = loro.get_movable_list("movable");
5752 let detached_list = ListHandler::new_detached();
5753 detached_list.push("item").unwrap();
5754 let attached_list = movable.insert_container(0, detached_list.clone()).unwrap();
5755 assert!(attached_list.is_attached());
5756 assert_eq!(
5757 attached_list.parent().unwrap().c_type(),
5758 ContainerType::MovableList
5759 );
5760 assert_eq!(
5761 detached_list.get_attached().unwrap().id(),
5762 attached_list.id()
5763 );
5764
5765 let nested = attached_map
5766 .insert_container("movable", MovableListHandler::new_detached())
5767 .unwrap();
5768 assert_eq!(nested.parent().unwrap().id(), attached_map.id());
5769 }
5770
5771 #[test]
5772 fn unknown_handler_reports_identity_without_materializing_value() {
5773 let loro = LoroDoc::new_auto_commit();
5774 let id = ContainerID::Root {
5775 name: "unknown".into(),
5776 container_type: ContainerType::Unknown(7),
5777 };
5778 let handler = Handler::new_attached(id.clone(), loro.clone());
5779 let unknown = handler.as_unknown().unwrap();
5780
5781 assert!(unknown.is_attached());
5782 assert_eq!(unknown.kind(), ContainerType::Unknown(7));
5783 assert_eq!(unknown.id(), id);
5784 assert_eq!(unknown.to_handler().c_type(), ContainerType::Unknown(7));
5785 assert!(unknown.doc().is_some());
5786 assert!(!unknown.is_deleted());
5787 assert_eq!(format!("{unknown:?}"), "UnknownHandler");
5788 assert!(unknown.get_attached().is_some());
5789 assert!(super::UnknownHandler::from_handler(handler).is_some());
5790 }
5791
5792 #[test]
5793 fn deep_value_with_id_cid_matches_container_id_string() {
5794 let loro = LoroDoc::new_auto_commit();
5795 let map = loro.get_map("map");
5796 map.insert("key", "value").unwrap();
5797 let child = map
5798 .insert_container("child", ListHandler::new_detached())
5799 .unwrap();
5800 child.push("item").unwrap();
5801
5802 let value = loro.get_deep_value_with_id();
5803 assert_eq!(
5804 value["map"]["cid"].to_json_value(),
5805 json!(map.id().to_string())
5806 );
5807 assert_eq!(
5808 value["map"]["cid"].to_json_value(),
5809 json!("cid:root-map:Map")
5810 );
5811 let child_cid = value["map"]["value"]["child"]["cid"].to_json_value();
5812 assert_eq!(child_cid, json!(child.id().to_string()));
5813 let child_cid = child_cid.as_str().unwrap();
5814 assert!(child_cid.starts_with("cid:"), "unexpected cid: {child_cid}");
5815 assert!(!child_cid.contains("idx:"), "unexpected cid: {child_cid}");
5816
5817 let map_value = map.get_deep_value_with_id().unwrap();
5819 assert_eq!(
5820 map_value["cid"].to_json_value(),
5821 json!(map.id().to_string())
5822 );
5823 assert_eq!(
5824 map_value["value"]["child"]["cid"].to_json_value(),
5825 json!(child.id().to_string())
5826 );
5827 assert_eq!(
5828 map_value["value"]["child"]["value"][0].to_json_value(),
5829 json!("item")
5830 );
5831 }
5832
5833 #[test]
5834 fn text_and_tree_deep_value_with_id() {
5835 let loro = LoroDoc::new_auto_commit();
5836 let text = loro.get_text("text");
5837 text.insert(0, "hello", PosType::Unicode).unwrap();
5838 let text_value = text.get_deep_value_with_id().unwrap();
5839 assert_eq!(
5840 text_value.to_json_value(),
5841 json!({"cid": text.id().to_string(), "value": "hello"})
5842 );
5843
5844 let tree = loro.get_tree("tree");
5845 let node = tree.create(TreeParentId::Root).unwrap();
5846 tree.get_meta(node).unwrap().insert("name", "root").unwrap();
5847 let tree_value = tree.get_deep_value_with_id().unwrap();
5848 assert_eq!(
5849 tree_value["cid"].to_json_value(),
5850 json!(tree.id().to_string())
5851 );
5852 assert_eq!(
5853 tree_value["value"][0]["meta"]["name"].to_json_value(),
5854 json!("root")
5855 );
5856
5857 let detached_text = TextHandler::new_detached();
5859 assert!(matches!(
5860 detached_text.get_deep_value_with_id(),
5861 Err(LoroError::MisuseDetachedContainer { .. })
5862 ));
5863 let detached_tree = crate::handler::TreeHandler::new_detached();
5864 assert!(matches!(
5865 detached_tree.get_deep_value_with_id(),
5866 Err(LoroError::MisuseDetachedContainer { .. })
5867 ));
5868 }
5869
5870 #[test]
5871 fn list_slice_deep_value() {
5872 let loro = LoroDoc::new_auto_commit();
5873 let list = loro.get_list("list");
5874 list.push("a").unwrap();
5875 list.push("b").unwrap();
5876 let child = list.push_container(MapHandler::new_detached()).unwrap();
5877 child.insert("k", "v").unwrap();
5878 list.push("d").unwrap();
5879
5880 let all = list.get_slice_deep_value_with_id(0, 4).unwrap();
5882 assert_eq!(all[0].to_json_value(), json!("a"));
5883 assert_eq!(
5884 all[2].to_json_value(),
5885 json!({"cid": child.id().to_string(), "value": {"k": "v"}})
5886 );
5887
5888 let mid = list.get_slice_deep_value(1, 3).unwrap();
5890 assert_eq!(mid.to_json_value(), json!(["b", {"k": "v"}]));
5891
5892 let clamped = list.get_slice_deep_value_with_id(2, 100).unwrap();
5894 assert_eq!(clamped.to_json_value().as_array().unwrap().len(), 2);
5895 let clamped_start = list.get_slice_deep_value_with_id(100, 200).unwrap();
5896 assert_eq!(clamped_start.to_json_value(), json!([]));
5897
5898 assert_eq!(
5900 list.get_slice_deep_value(2, 2).unwrap().to_json_value(),
5901 json!([])
5902 );
5903 assert_eq!(
5904 list.get_slice_deep_value(3, 1).unwrap().to_json_value(),
5905 json!([])
5906 );
5907
5908 let detached = ListHandler::new_detached();
5910 assert!(matches!(
5911 detached.get_slice_deep_value_with_id(0, 1),
5912 Err(LoroError::MisuseDetachedContainer { .. })
5913 ));
5914 assert!(matches!(
5915 detached.get_slice_deep_value(0, 1),
5916 Err(LoroError::MisuseDetachedContainer { .. })
5917 ));
5918 }
5919
5920 #[test]
5921 fn movable_list_slice_deep_value() {
5922 let loro = LoroDoc::new_auto_commit();
5923 let list = loro.get_movable_list("list");
5924 list.push("a".into()).unwrap();
5925 let child = list.push_container(ListHandler::new_detached()).unwrap();
5926 child.push("x").unwrap();
5927 list.push("b".into()).unwrap();
5928
5929 let all = list.get_slice_deep_value_with_id(0, 3).unwrap();
5930 assert_eq!(
5931 all[1].to_json_value(),
5932 json!({"cid": child.id().to_string(), "value": ["x"]})
5933 );
5934
5935 let plain = list.get_slice_deep_value(0, 2).unwrap();
5936 assert_eq!(plain.to_json_value(), json!(["a", ["x"]]));
5937
5938 let attached = list.get_deep_value_with_id().unwrap();
5940 assert_eq!(
5941 attached["cid"].to_json_value(),
5942 json!(list.id().to_string())
5943 );
5944 let detached = MovableListHandler::new_detached();
5945 assert!(matches!(
5946 detached.get_deep_value_with_id(),
5947 Err(LoroError::MisuseDetachedContainer { .. })
5948 ));
5949 assert!(matches!(
5950 detached.get_slice_deep_value_with_id(0, 1),
5951 Err(LoroError::MisuseDetachedContainer { .. })
5952 ));
5953 assert!(matches!(
5954 detached.get_slice_deep_value(0, 1),
5955 Err(LoroError::MisuseDetachedContainer { .. })
5956 ));
5957 }
5958}