Skip to main content

loro_internal/
value.rs

1use crate::{
2    container::richtext::richtext_state::{unicode_to_utf8_index, utf16_to_utf8_index},
3    delta::{Delta, DeltaItem, Meta},
4    event::{Diff, Index, Path, TextDiff, TextDiffItem, TextMeta},
5    handler::ValueOrHandler,
6    utils::string_slice::StringSlice,
7};
8use generic_btree::rle::HasLength;
9use loro_common::ContainerType;
10pub use loro_common::LoroValue;
11
12// TODO: rename this trait
13pub trait ToJson {
14    fn to_json_value(&self) -> serde_json::Value;
15    fn to_json(&self) -> String {
16        self.to_json_value().to_string()
17    }
18    fn to_json_pretty(&self) -> String {
19        serde_json::to_string_pretty(&self.to_json_value()).unwrap()
20    }
21    fn from_json(s: &str) -> Self;
22}
23
24impl ToJson for LoroValue {
25    fn to_json_value(&self) -> serde_json::Value {
26        serde_json::to_value(self).unwrap()
27    }
28
29    fn to_json(&self) -> String {
30        serde_json::to_string(self).unwrap()
31    }
32
33    fn to_json_pretty(&self) -> String {
34        serde_json::to_string_pretty(self).unwrap()
35    }
36
37    #[allow(unused)]
38    fn from_json(s: &str) -> Self {
39        serde_json::from_str(s).unwrap()
40    }
41}
42
43impl ToJson for DeltaItem<StringSlice, TextMeta> {
44    fn to_json_value(&self) -> serde_json::Value {
45        match self {
46            DeltaItem::Retain {
47                retain: len,
48                attributes: meta,
49            } => {
50                let mut map = serde_json::Map::new();
51                map.insert("retain".into(), serde_json::to_value(len).unwrap());
52                if !meta.is_empty() {
53                    map.insert("attributes".into(), meta.to_json_value());
54                }
55                serde_json::Value::Object(map)
56            }
57            DeltaItem::Insert {
58                insert: value,
59                attributes: meta,
60            } => {
61                let mut map = serde_json::Map::new();
62                map.insert("insert".into(), serde_json::to_value(value).unwrap());
63                if !meta.is_empty() {
64                    map.insert("attributes".into(), meta.to_json_value());
65                }
66                serde_json::Value::Object(map)
67            }
68            DeltaItem::Delete {
69                delete: len,
70                attributes: _,
71            } => {
72                let mut map = serde_json::Map::new();
73                map.insert("delete".into(), serde_json::to_value(len).unwrap());
74                serde_json::Value::Object(map)
75            }
76        }
77    }
78
79    fn from_json(s: &str) -> Self {
80        let map: serde_json::Map<String, serde_json::Value> = serde_json::from_str(s).unwrap();
81        if map.contains_key("retain") {
82            let len = map["retain"].as_u64().unwrap();
83            let meta = if let Some(meta) = map.get("attributes") {
84                TextMeta::from_json(meta.to_string().as_str())
85            } else {
86                TextMeta::default()
87            };
88            DeltaItem::Retain {
89                retain: len as usize,
90                attributes: meta,
91            }
92        } else if map.contains_key("insert") {
93            let value = map["insert"].as_str().unwrap().to_string().into();
94            let meta = if let Some(meta) = map.get("attributes") {
95                TextMeta::from_json(meta.to_string().as_str())
96            } else {
97                TextMeta::default()
98            };
99            DeltaItem::Insert {
100                insert: value,
101                attributes: meta,
102            }
103        } else if map.contains_key("delete") {
104            let len = map["delete"].as_u64().unwrap();
105            DeltaItem::Delete {
106                delete: len as usize,
107                attributes: Default::default(),
108            }
109        } else {
110            panic!("Invalid delta item: {}", s);
111        }
112    }
113}
114
115fn diff_item_to_json_value(item: &TextDiffItem) -> (serde_json::Value, Option<serde_json::Value>) {
116    match item {
117        loro_delta::DeltaItem::Retain { len, attr } => {
118            let mut map = serde_json::Map::new();
119            map.insert("retain".into(), serde_json::to_value(len).unwrap());
120            if !attr.is_empty() {
121                map.insert("attributes".into(), attr.to_json_value());
122            }
123            (serde_json::Value::Object(map), None)
124        }
125        loro_delta::DeltaItem::Replace {
126            value,
127            attr,
128            delete,
129        } => {
130            let mut a = None;
131            let mut b = None;
132            if value.rle_len() > 0 {
133                let mut map = serde_json::Map::new();
134                map.insert("insert".into(), serde_json::to_value(value).unwrap());
135                if !attr.is_empty() {
136                    map.insert("attributes".into(), attr.to_json_value());
137                }
138                a = Some(serde_json::Value::Object(map));
139            }
140            if *delete > 0 {
141                let mut map = serde_json::Map::new();
142                map.insert("delete".into(), serde_json::to_value(delete).unwrap());
143                b = Some(serde_json::Value::Object(map));
144            }
145
146            if a.is_none() {
147                a = std::mem::take(&mut b);
148                if a.is_none() {
149                    let mut map = serde_json::Map::new();
150                    map.insert("retain".into(), serde_json::to_value(0).unwrap());
151                    a = Some(serde_json::Value::Object(map));
152                }
153            }
154            (a.unwrap(), b)
155        }
156    }
157}
158
159fn diff_item_from_json(v: serde_json::Value) -> TextDiffItem {
160    let serde_json::Value::Object(map) = v else {
161        panic!("Invalid delta item: {:?}", v);
162    };
163    if map.contains_key("retain") {
164        let len = map["retain"].as_u64().unwrap();
165        let meta = if let Some(meta) = map.get("attributes") {
166            TextMeta::from_json(meta.to_string().as_str())
167        } else {
168            TextMeta::default()
169        };
170        TextDiffItem::Retain {
171            len: len as usize,
172            attr: meta,
173        }
174    } else if map.contains_key("insert") {
175        let value = map["insert"].as_str().unwrap().to_string().into();
176        let meta = if let Some(meta) = map.get("attributes") {
177            TextMeta::from_json(meta.to_string().as_str())
178        } else {
179            TextMeta::default()
180        };
181        TextDiffItem::Replace {
182            value,
183            attr: meta,
184            delete: 0,
185        }
186    } else if map.contains_key("delete") {
187        let len = map["delete"].as_u64().unwrap();
188        TextDiffItem::new_delete(len as usize)
189    } else {
190        panic!("Invalid delta item: {:?}", map);
191    }
192}
193
194impl ToJson for TextDiff {
195    fn to_json_value(&self) -> serde_json::Value {
196        let mut vec = Vec::new();
197        for item in self.iter() {
198            let (a, b) = diff_item_to_json_value(item);
199            vec.push(a);
200            if let Some(b) = b {
201                vec.push(b);
202            }
203        }
204        serde_json::Value::Array(vec)
205    }
206
207    fn from_json(s: &str) -> Self {
208        let vec: Vec<serde_json::Value> = serde_json::from_str(s).unwrap();
209        let mut ans = TextDiff::new();
210        for item in vec.into_iter() {
211            ans.push(diff_item_from_json(item));
212        }
213        ans
214    }
215}
216
217impl ToJson for Delta<StringSlice, TextMeta> {
218    fn to_json_value(&self) -> serde_json::Value {
219        let mut vec = Vec::new();
220        for item in self.iter() {
221            vec.push(item.to_json_value());
222        }
223        serde_json::Value::Array(vec)
224    }
225
226    fn from_json(s: &str) -> Self {
227        let vec: Vec<serde_json::Value> = serde_json::from_str(s).unwrap();
228        let mut ans = Delta::new();
229        for item in vec.into_iter() {
230            ans.push(DeltaItem::from_json(item.to_string().as_str()));
231        }
232        ans
233    }
234}
235
236#[derive(Debug, PartialEq, Eq)]
237enum TypeHint {
238    Map,
239    Text,
240    List,
241    Tree,
242    #[cfg(feature = "counter")]
243    Counter,
244}
245
246pub trait ApplyDiff {
247    fn apply_diff_shallow(&mut self, diff: &[Diff]);
248    fn apply_diff(&mut self, diff: &[Diff]);
249    fn apply(&mut self, path: &Path, diff: &[Diff]);
250}
251
252impl ApplyDiff for LoroValue {
253    fn apply_diff_shallow(&mut self, diff: &[Diff]) {
254        match self {
255            LoroValue::String(value) => {
256                let mut s = value.to_string();
257                for item in diff.iter() {
258                    let delta = item.as_text().unwrap();
259                    let mut index = 0;
260                    for delta_item in delta.iter() {
261                        match delta_item {
262                            loro_delta::DeltaItem::Retain { len, attr: _ } => {
263                                index += len;
264                            }
265                            loro_delta::DeltaItem::Replace {
266                                value,
267                                attr: _,
268                                delete,
269                            } => {
270                                let (start, end) = if cfg!(feature = "wasm") {
271                                    (
272                                        utf16_to_utf8_index(&s, index).unwrap(),
273                                        utf16_to_utf8_index(&s, index + *delete).unwrap(),
274                                    )
275                                } else {
276                                    (
277                                        unicode_to_utf8_index(&s, index).unwrap(),
278                                        unicode_to_utf8_index(&s, index + *delete).unwrap(),
279                                    )
280                                };
281                                s.replace_range(start..end, value.as_str());
282                                index += value.len_bytes();
283                            }
284                        }
285                    }
286                }
287                *value = s.into()
288            }
289            LoroValue::List(seq) => {
290                let is_tree = matches!(diff.first(), Some(Diff::Tree(_)));
291                if !is_tree {
292                    let seq = seq.make_mut();
293                    for item in diff.iter() {
294                        let delta = item.as_list().unwrap();
295                        let mut index = 0;
296                        for delta_item in delta.iter() {
297                            match delta_item {
298                                loro_delta::DeltaItem::Retain { len, attr: _ } => {
299                                    index += len;
300                                }
301                                loro_delta::DeltaItem::Replace {
302                                    value,
303                                    attr: _,
304                                    delete,
305                                } => {
306                                    let len = value.len();
307                                    seq.splice(
308                                        index..index + delete,
309                                        value.iter().map(|x| x.to_value()),
310                                    );
311                                    index += len;
312                                }
313                            }
314                        }
315                    }
316                } else {
317                    // let seq = Arc::make_mut(seq);
318                    // for item in diff.iter() {
319                    //     match item {
320                    //         Diff::Tree(tree) => {
321                    //             let mut v = TreeValue(seq);
322                    //             v.apply_diff(tree);
323                    //         }
324                    //         _ => unreachable!(),
325                    //     }
326                    // }
327                    unimplemented!()
328                }
329            }
330            LoroValue::Map(map) => {
331                for item in diff.iter() {
332                    match item {
333                        Diff::Map(diff) => {
334                            let map = map.make_mut();
335                            for (key, value) in diff.updated.iter() {
336                                match &value.value {
337                                    Some(value) => {
338                                        map.insert(key.to_string(), value.to_value());
339                                    }
340                                    None => {
341                                        map.remove(&key.to_string());
342                                    }
343                                }
344                            }
345                        }
346                        _ => unreachable!(),
347                    }
348                }
349            }
350            _ => unreachable!(),
351        }
352    }
353
354    fn apply_diff(&mut self, diff: &[Diff]) {
355        match self {
356            LoroValue::String(value) => {
357                let mut s = value.to_string();
358                for item in diff.iter() {
359                    let delta = item.as_text().unwrap();
360                    let mut index = 0;
361                    for delta_item in delta.iter() {
362                        match delta_item {
363                            loro_delta::DeltaItem::Retain { len, attr: _ } => {
364                                index += len;
365                            }
366                            loro_delta::DeltaItem::Replace {
367                                value,
368                                attr: _,
369                                delete,
370                            } => {
371                                s.replace_range(index..index + *delete, value.as_str());
372                                index += value.len_bytes();
373                            }
374                        }
375                    }
376                }
377                *value = s.into();
378            }
379            LoroValue::List(seq) => {
380                let is_tree = matches!(diff.first(), Some(Diff::Tree(_)));
381                if !is_tree {
382                    let seq = seq.make_mut();
383                    for item in diff.iter() {
384                        let delta = item.as_list().unwrap();
385                        let mut index = 0;
386                        for delta_item in delta.iter() {
387                            match delta_item {
388                                loro_delta::DeltaItem::Retain { len, .. } => {
389                                    index += len;
390                                }
391                                loro_delta::DeltaItem::Replace {
392                                    value,
393                                    attr: _,
394                                    delete,
395                                } => {
396                                    let value_iter = value.iter().map(unresolved_to_collection);
397                                    seq.splice(index..index + *delete, value_iter);
398                                    index += value.len();
399                                }
400                            }
401                        }
402                    }
403                } else {
404                    // let seq = Arc::make_mut(seq);
405                    // for item in diff.iter() {
406                    //     match item {
407                    //         Diff::Tree(tree) => {
408                    //             let mut v = TreeValue(seq);
409                    //             v.apply_diff(tree);
410                    //         }
411                    //         _ => unreachable!(),
412                    //     }
413                    // }
414                    unimplemented!()
415                }
416            }
417            LoroValue::Map(map) => {
418                for item in diff.iter() {
419                    match item {
420                        Diff::Map(diff) => {
421                            let map = map.make_mut();
422                            for (key, value) in diff.updated.iter() {
423                                match &value.value {
424                                    Some(value) => {
425                                        map.insert(
426                                            key.to_string(),
427                                            unresolved_to_collection(value),
428                                        );
429                                    }
430                                    None => {
431                                        map.remove(&key.to_string());
432                                    }
433                                }
434                            }
435                        }
436                        _ => unreachable!(),
437                    }
438                }
439            }
440            _ => unreachable!(),
441        }
442    }
443
444    fn apply(&mut self, path: &Path, diff: &[Diff]) {
445        if diff.is_empty() {
446            return;
447        }
448
449        let hint = match diff[0] {
450            Diff::List(_) => TypeHint::List,
451            Diff::Text(_) => TypeHint::Text,
452            Diff::Map(_) => TypeHint::Map,
453            Diff::Tree(_) => TypeHint::Tree,
454            #[cfg(feature = "counter")]
455            Diff::Counter(_) => TypeHint::Counter,
456            Diff::Unknown => unreachable!(),
457        };
458        let value = {
459            let mut hints = Vec::with_capacity(path.len());
460            for item in path.iter().skip(1) {
461                match item {
462                    Index::Key(_) => hints.push(TypeHint::Map),
463                    Index::Seq(_) => hints.push(TypeHint::List),
464                    Index::Node(_) => hints.push(TypeHint::Tree),
465                }
466            }
467
468            hints.push(hint);
469            let mut value: &mut LoroValue = self;
470            for (item, hint) in path.iter().zip(hints.iter()) {
471                match item {
472                    Index::Key(key) => {
473                        let m = value.as_map_mut().unwrap();
474                        let map = m.make_mut();
475                        value = map.entry(key.to_string()).or_insert_with(|| match hint {
476                            TypeHint::Map => LoroValue::Map(Default::default()),
477                            TypeHint::Text => LoroValue::String(Default::default()),
478                            TypeHint::List => LoroValue::List(Default::default()),
479                            TypeHint::Tree => LoroValue::List(Default::default()),
480                            #[cfg(feature = "counter")]
481                            TypeHint::Counter => LoroValue::Double(0.),
482                        })
483                    }
484                    Index::Seq(index) => {
485                        let l = value.as_list_mut().unwrap();
486                        let list = l.make_mut();
487                        value = list.get_mut(*index).unwrap();
488                    }
489                    Index::Node(tree_id) => {
490                        let l = value.as_list_mut().unwrap();
491                        let list = l.make_mut();
492                        let Some(map) = list.iter_mut().find(|x| {
493                            let id = x.as_map().unwrap().get("id").unwrap().as_string().unwrap();
494                            id.as_ref() == tree_id.to_string()
495                        }) else {
496                            // delete node first
497                            return;
498                        };
499                        let map_mut = map.as_map_mut().unwrap().make_mut();
500                        let meta = map_mut.get_mut("meta").unwrap();
501                        if meta.is_container() {
502                            *meta = ContainerType::Map.default_value();
503                        }
504                        value = meta
505                    }
506                }
507            }
508            value
509        };
510        value.apply_diff(diff);
511    }
512}
513
514pub(crate) fn unresolved_to_collection(v: &ValueOrHandler) -> LoroValue {
515    match v {
516        ValueOrHandler::Value(v) => v.clone(),
517        ValueOrHandler::Handler(c) => c.c_type().default_value(),
518    }
519}
520
521#[cfg(test)]
522mod tests {
523    use loro_common::IdLp;
524    use loro_delta::{array_vec::ArrayVec, DeltaItem as EventDeltaItem};
525    use rustc_hash::FxHashMap;
526
527    use crate::{
528        delta::{Delta, DeltaItem},
529        event::{ListDeltaMeta, ListDiff, ListDiffInsertItem},
530        handler::{Handler, HandlerTrait},
531        LoroDoc,
532    };
533
534    use super::*;
535
536    fn text_diff(items: Vec<TextDiffItem>) -> TextDiff {
537        let mut diff = TextDiff::new();
538        for item in items {
539            diff.push(item);
540        }
541        diff
542    }
543
544    fn list_values(values: Vec<LoroValue>) -> ListDiffInsertItem {
545        let mut array = ArrayVec::new();
546        for value in values {
547            array.push(ValueOrHandler::Value(value)).unwrap();
548        }
549        array
550    }
551
552    fn list_diff(items: Vec<crate::event::ListDiffItem>) -> ListDiff {
553        let mut diff = ListDiff::new();
554        for item in items {
555            diff.push(item);
556        }
557        diff
558    }
559
560    fn map_value(value: Option<LoroValue>, lamport: u32) -> crate::delta::ResolvedMapValue {
561        crate::delta::ResolvedMapValue {
562            value: value.map(ValueOrHandler::Value),
563            idlp: IdLp::new(1, lamport),
564        }
565    }
566
567    #[test]
568    fn text_delta_json_roundtrip_preserves_attributes_and_operation_order() {
569        let mut attributes = FxHashMap::default();
570        attributes.insert("bold".to_string(), LoroValue::Bool(true));
571        let meta = TextMeta(attributes);
572
573        let item = DeltaItem::Insert {
574            insert: StringSlice::from("hi"),
575            attributes: meta.clone(),
576        };
577        assert_eq!(
578            item.to_json_value(),
579            serde_json::json!({"insert": "hi", "attributes": {"bold": true}})
580        );
581        assert_eq!(
582            DeltaItem::<StringSlice, TextMeta>::from_json(
583                r#"{"insert":"hi","attributes":{"bold":true}}"#
584            ),
585            item
586        );
587
588        let mut delta = Delta::<StringSlice, TextMeta>::new();
589        delta.push(DeltaItem::Retain {
590            retain: 2,
591            attributes: meta.clone(),
592        });
593        delta.push(DeltaItem::Insert {
594            insert: StringSlice::from("!"),
595            attributes: TextMeta::default(),
596        });
597        delta.push(DeltaItem::Delete {
598            delete: 1,
599            attributes: TextMeta::default(),
600        });
601
602        let json = delta.to_json();
603        assert_eq!(
604            serde_json::from_str::<serde_json::Value>(&json).unwrap(),
605            serde_json::json!([
606                {"retain": 2, "attributes": {"bold": true}},
607                {"insert": "!"},
608                {"delete": 1}
609            ])
610        );
611        assert_eq!(Delta::<StringSlice, TextMeta>::from_json(&json), delta);
612    }
613
614    #[test]
615    fn text_diff_json_splits_replace_with_insert_and_delete_entries() {
616        let diff = text_diff(vec![TextDiffItem::Replace {
617            value: StringSlice::from("new"),
618            attr: TextMeta::default(),
619            delete: 2,
620        }]);
621
622        assert_eq!(
623            diff.to_json_value(),
624            serde_json::json!([{"insert": "new"}, {"delete": 2}])
625        );
626        assert_eq!(
627            TextDiff::from_json(r#"[{"retain":1},{"insert":"x"},{"delete":2}]"#).to_json_value(),
628            serde_json::json!([{"retain": 1}, {"insert": "x"}, {"delete": 2}])
629        );
630
631        let (empty_replace, follow_up) = diff_item_to_json_value(&TextDiffItem::Replace {
632            value: StringSlice::from(""),
633            attr: TextMeta::default(),
634            delete: 0,
635        });
636        assert_eq!(empty_replace, serde_json::json!({"retain": 0}));
637        assert_eq!(follow_up, None);
638    }
639
640    #[test]
641    fn apply_diff_updates_string_list_and_map_values() {
642        let mut text = LoroValue::String("aéz".into());
643        text.apply_diff_shallow(&[Diff::Text(text_diff(vec![
644            TextDiffItem::Retain {
645                len: 1,
646                attr: TextMeta::default(),
647            },
648            TextDiffItem::Replace {
649                value: StringSlice::from("bc"),
650                attr: TextMeta::default(),
651                delete: 1,
652            },
653        ]))]);
654        assert_eq!(text, LoroValue::String("abcz".into()));
655
656        let mut list =
657            LoroValue::List(vec![LoroValue::I64(1), LoroValue::I64(2), LoroValue::I64(3)].into());
658        list.apply_diff(&[Diff::List(list_diff(vec![
659            EventDeltaItem::Retain {
660                len: 1,
661                attr: ListDeltaMeta::default(),
662            },
663            EventDeltaItem::Replace {
664                value: list_values(vec![LoroValue::I64(20), LoroValue::I64(21)]),
665                attr: ListDeltaMeta::default(),
666                delete: 1,
667            },
668        ]))]);
669        assert_eq!(
670            list,
671            LoroValue::List(
672                vec![
673                    LoroValue::I64(1),
674                    LoroValue::I64(20),
675                    LoroValue::I64(21),
676                    LoroValue::I64(3),
677                ]
678                .into()
679            )
680        );
681
682        let mut map = LoroValue::Map(
683            FxHashMap::from_iter([
684                ("keep".to_string(), LoroValue::I64(1)),
685                ("remove".to_string(), LoroValue::I64(2)),
686            ])
687            .into(),
688        );
689        map.apply_diff(&[Diff::Map(
690            crate::delta::ResolvedMapDelta::new()
691                .with_entry("add".into(), map_value(Some(LoroValue::I64(3)), 1))
692                .with_entry("remove".into(), map_value(None, 2)),
693        )]);
694        let map = map.as_map().unwrap();
695        assert_eq!(map.get("keep"), Some(&LoroValue::I64(1)));
696        assert_eq!(map.get("add"), Some(&LoroValue::I64(3)));
697        assert_eq!(map.get("remove"), None);
698    }
699
700    #[test]
701    fn apply_creates_missing_nested_values_from_path_and_diff_hint() {
702        let mut root = LoroValue::Map(Default::default());
703        let mut title_path = Path::new();
704        title_path.push(Index::Key("doc".into()));
705        title_path.push(Index::Key("title".into()));
706        root.apply(
707            &title_path,
708            &[Diff::Text(text_diff(vec![TextDiffItem::Replace {
709                value: StringSlice::from("Loro"),
710                attr: TextMeta::default(),
711                delete: 0,
712            }]))],
713        );
714
715        assert_eq!(
716            root.to_json_value(),
717            serde_json::json!({"doc": {"title": "Loro"}})
718        );
719
720        let mut tags_path = Path::new();
721        tags_path.push(Index::Key("doc".into()));
722        tags_path.push(Index::Key("tags".into()));
723        root.apply(
724            &tags_path,
725            &[Diff::List(list_diff(vec![EventDeltaItem::Replace {
726                value: list_values(vec![
727                    LoroValue::String("crdt".into()),
728                    LoroValue::String("rust".into()),
729                ]),
730                attr: ListDeltaMeta::default(),
731                delete: 0,
732            }]))],
733        );
734        assert_eq!(
735            root.to_json_value(),
736            serde_json::json!({"doc": {"title": "Loro", "tags": ["crdt", "rust"]}})
737        );
738    }
739
740    #[test]
741    fn unresolved_container_handlers_apply_as_default_collection_values() {
742        let doc = LoroDoc::new_auto_commit();
743        let map_handler = Handler::new_unattached(ContainerType::Map);
744        let text_handler = Handler::new_unattached(ContainerType::Text);
745        let attached_list = doc.get_list("list").to_handler();
746
747        assert_eq!(
748            unresolved_to_collection(&ValueOrHandler::Handler(map_handler)),
749            LoroValue::Map(Default::default())
750        );
751        assert_eq!(
752            unresolved_to_collection(&ValueOrHandler::Handler(text_handler)),
753            LoroValue::String(Default::default())
754        );
755        assert_eq!(
756            unresolved_to_collection(&ValueOrHandler::Handler(attached_list)),
757            LoroValue::List(Default::default())
758        );
759        assert_eq!(
760            unresolved_to_collection(&ValueOrHandler::Value(LoroValue::Bool(true))),
761            LoroValue::Bool(true)
762        );
763    }
764}
765
766#[cfg(feature = "wasm")]
767pub mod wasm {
768    use crate::{
769        delta::{Delta, DeltaItem, Meta, StyleMeta, TreeDiff, TreeDiffItem, TreeExternalDiff},
770        event::{Index, TextDiff, TextDiffItem, TextMeta},
771        utils::string_slice::StringSlice,
772        TreeParentId,
773    };
774    use fractional_index::FractionalIndex;
775    use generic_btree::rle::HasLength;
776    use js_sys::{Array, Object};
777    use loro_common::{LoroValue, TreeID};
778    use wasm_bindgen::{__rt::IntoJsResult, JsCast, JsValue};
779
780    impl From<Index> for JsValue {
781        fn from(value: Index) -> Self {
782            match value {
783                Index::Key(key) => JsValue::from_str(&key),
784                Index::Seq(num) => JsValue::from_f64(num as f64),
785                Index::Node(node) => node.into(),
786            }
787        }
788    }
789
790    impl From<&TreeDiff> for JsValue {
791        fn from(value: &TreeDiff) -> Self {
792            let array = Array::new();
793            for diff in value.diff.iter() {
794                let obj = Object::new();
795                js_sys::Reflect::set(&obj, &"target".into(), &diff.target.into()).unwrap();
796                match &diff.action {
797                    TreeExternalDiff::Create {
798                        parent,
799                        index,
800                        position,
801                    } => {
802                        js_sys::Reflect::set(&obj, &"action".into(), &"create".into()).unwrap();
803                        js_sys::Reflect::set(
804                            &obj,
805                            &"parent".into(),
806                            &JsValue::from(parent.tree_id()),
807                        )
808                        .unwrap();
809                        js_sys::Reflect::set(&obj, &"index".into(), &(*index).into()).unwrap();
810                        js_sys::Reflect::set(
811                            &obj,
812                            &"fractionalIndex".into(),
813                            &position.to_string().into(),
814                        )
815                        .unwrap();
816                    }
817                    TreeExternalDiff::Delete {
818                        old_parent,
819                        old_index,
820                    } => {
821                        js_sys::Reflect::set(&obj, &"action".into(), &"delete".into()).unwrap();
822                        js_sys::Reflect::set(
823                            &obj,
824                            &"oldParent".into(),
825                            &JsValue::from(old_parent.tree_id()),
826                        )
827                        .unwrap();
828                        js_sys::Reflect::set(&obj, &"oldIndex".into(), &(*old_index).into())
829                            .unwrap();
830                    }
831                    TreeExternalDiff::Move {
832                        parent,
833                        index,
834                        position,
835                        old_parent,
836                        old_index,
837                    } => {
838                        js_sys::Reflect::set(&obj, &"action".into(), &"move".into()).unwrap();
839                        js_sys::Reflect::set(
840                            &obj,
841                            &"parent".into(),
842                            &JsValue::from(parent.tree_id()),
843                        )
844                        .unwrap();
845                        js_sys::Reflect::set(&obj, &"index".into(), &(*index).into()).unwrap();
846                        js_sys::Reflect::set(
847                            &obj,
848                            &"fractionalIndex".into(),
849                            &position.to_string().into(),
850                        )
851                        .unwrap();
852                        js_sys::Reflect::set(
853                            &obj,
854                            &"oldParent".into(),
855                            &JsValue::from(old_parent.tree_id()),
856                        )
857                        .unwrap();
858                        js_sys::Reflect::set(&obj, &"oldIndex".into(), &(*old_index).into())
859                            .unwrap();
860                    }
861                }
862                array.push(&obj);
863            }
864            array.into_js_result().unwrap()
865        }
866    }
867
868    impl TryFrom<&JsValue> for TreeDiff {
869        type Error = String;
870
871        fn try_from(value: &JsValue) -> Result<Self, Self::Error> {
872            if !value.is_array() {
873                return Err("Expected an array".to_string());
874            }
875
876            let array = js_sys::Array::from(value);
877            let mut diff = Vec::new();
878
879            for i in 0..array.length() {
880                let item = array.get(i);
881                if !item.is_object() {
882                    return Err(format!("Item at index {i} is not an object"));
883                }
884
885                let obj = js_sys::Object::from(item);
886                let target = js_sys::Reflect::get(&obj, &"target".into())
887                    .map_err(|e| format!("Failed to get target: {:?}", e))?;
888                let target = TreeID::try_from(target)
889                    .map_err(|e| format!("Failed to parse target: {:?}", e))?;
890
891                let action = js_sys::Reflect::get(&obj, &"action".into())
892                    .map_err(|e| format!("Failed to get action: {:?}", e))?;
893                let action = action
894                    .as_string()
895                    .ok_or_else(|| "action is not a string".to_string())?;
896
897                let action = match action.as_str() {
898                    "create" => {
899                        let parent = js_sys::Reflect::get(&obj, &"parent".into())
900                            .map_err(|e| format!("Failed to get parent: {:?}", e))?;
901                        let parent_id = if parent.is_null() || parent.is_undefined() {
902                            None
903                        } else {
904                            Some(
905                                TreeID::try_from(parent)
906                                    .map_err(|e| format!("Failed to parse parent: {:?}", e))?,
907                            )
908                        };
909                        let parent = TreeParentId::from(parent_id);
910                        let index = js_sys::Reflect::get(&obj, &"index".into())
911                            .map_err(|e| format!("Failed to get index: {:?}", e))?;
912                        let index = index
913                            .as_f64()
914                            .ok_or_else(|| "index is not a number".to_string())?
915                            as usize;
916
917                        let position = js_sys::Reflect::get(&obj, &"fractionalIndex".into())
918                            .map_err(|e| format!("Failed to get fractionalIndex: {:?}", e))?;
919                        let position = position
920                            .as_string()
921                            .ok_or_else(|| "fractionalIndex is not a string".to_string())?;
922                        let position = FractionalIndex::from_hex_string(position);
923
924                        TreeExternalDiff::Create {
925                            parent,
926                            index,
927                            position,
928                        }
929                    }
930                    "move" => {
931                        let parent = js_sys::Reflect::get(&obj, &"parent".into())
932                            .map_err(|e| format!("Failed to get parent: {:?}", e))?;
933                        let parent_id = if parent.is_null() || parent.is_undefined() {
934                            None
935                        } else {
936                            Some(
937                                TreeID::try_from(parent)
938                                    .map_err(|e| format!("Failed to parse parent: {:?}", e))?,
939                            )
940                        };
941                        let parent = TreeParentId::from(parent_id);
942
943                        let index = js_sys::Reflect::get(&obj, &"index".into())
944                            .map_err(|e| format!("Failed to get index: {:?}", e))?;
945                        let index = index
946                            .as_f64()
947                            .ok_or_else(|| "index is not a number".to_string())?
948                            as usize;
949
950                        let position = js_sys::Reflect::get(&obj, &"fractionalIndex".into())
951                            .map_err(|e| format!("Failed to get fractionalIndex: {:?}", e))?;
952                        let position = position
953                            .as_string()
954                            .ok_or_else(|| "fractionalIndex is not a string".to_string())?;
955                        let position = FractionalIndex::from_hex_string(position);
956
957                        let old_parent = js_sys::Reflect::get(&obj, &"oldParent".into())
958                            .map_err(|e| format!("Failed to get oldParent: {:?}", e))?;
959                        let old_parent_id = if old_parent.is_null() || old_parent.is_undefined() {
960                            None
961                        } else {
962                            Some(
963                                TreeID::try_from(old_parent)
964                                    .map_err(|e| format!("Failed to parse oldParent: {:?}", e))?,
965                            )
966                        };
967                        let old_parent = TreeParentId::from(old_parent_id);
968
969                        let old_index = js_sys::Reflect::get(&obj, &"oldIndex".into())
970                            .map_err(|e| format!("Failed to get oldIndex: {:?}", e))?;
971                        let old_index = old_index
972                            .as_f64()
973                            .ok_or_else(|| "oldIndex is not a number".to_string())?
974                            as usize;
975
976                        TreeExternalDiff::Move {
977                            parent,
978                            index,
979                            position,
980                            old_parent,
981                            old_index,
982                        }
983                    }
984                    "delete" => {
985                        let old_parent = js_sys::Reflect::get(&obj, &"oldParent".into())
986                            .map_err(|e| format!("Failed to get oldParent: {:?}", e))?;
987                        let old_parent_id = if old_parent.is_null() || old_parent.is_undefined() {
988                            None
989                        } else {
990                            Some(
991                                TreeID::try_from(old_parent)
992                                    .map_err(|e| format!("Failed to parse oldParent: {:?}", e))?,
993                            )
994                        };
995                        let old_parent = TreeParentId::from(old_parent_id);
996
997                        let old_index = js_sys::Reflect::get(&obj, &"oldIndex".into())
998                            .map_err(|e| format!("Failed to get oldIndex: {:?}", e))?;
999                        let old_index = old_index
1000                            .as_f64()
1001                            .ok_or_else(|| "oldIndex is not a number".to_string())?
1002                            as usize;
1003
1004                        TreeExternalDiff::Delete {
1005                            old_parent,
1006                            old_index,
1007                        }
1008                    }
1009                    action => Err(format!("Unknown tree diff action: {action}"))?,
1010                };
1011
1012                diff.push(TreeDiffItem { target, action });
1013            }
1014
1015            Ok(TreeDiff { diff })
1016        }
1017    }
1018
1019    impl From<&Delta<StringSlice, StyleMeta>> for JsValue {
1020        fn from(value: &Delta<StringSlice, StyleMeta>) -> Self {
1021            let arr = Array::new_with_length(value.len() as u32);
1022            for (i, v) in value.iter().enumerate() {
1023                arr.set(i as u32, JsValue::from(v.clone()));
1024            }
1025
1026            arr.into_js_result().unwrap()
1027        }
1028    }
1029
1030    impl From<DeltaItem<StringSlice, StyleMeta>> for JsValue {
1031        fn from(value: DeltaItem<StringSlice, StyleMeta>) -> Self {
1032            let obj = Object::new();
1033            match value {
1034                DeltaItem::Retain {
1035                    retain: len,
1036                    attributes: meta,
1037                } => {
1038                    js_sys::Reflect::set(
1039                        &obj,
1040                        &JsValue::from_str("retain"),
1041                        &JsValue::from_f64(len as f64),
1042                    )
1043                    .unwrap();
1044                    if !meta.is_empty() {
1045                        js_sys::Reflect::set(
1046                            &obj,
1047                            &JsValue::from_str("attributes"),
1048                            &JsValue::from(&meta),
1049                        )
1050                        .unwrap();
1051                    }
1052                }
1053                DeltaItem::Insert {
1054                    insert: value,
1055                    attributes: meta,
1056                } => {
1057                    js_sys::Reflect::set(
1058                        &obj,
1059                        &JsValue::from_str("insert"),
1060                        &JsValue::from_str(value.as_str()),
1061                    )
1062                    .unwrap();
1063                    if !meta.is_empty() {
1064                        js_sys::Reflect::set(
1065                            &obj,
1066                            &JsValue::from_str("attributes"),
1067                            &JsValue::from(&meta),
1068                        )
1069                        .unwrap();
1070                    }
1071                }
1072                DeltaItem::Delete {
1073                    delete: len,
1074                    attributes: _,
1075                } => {
1076                    js_sys::Reflect::set(
1077                        &obj,
1078                        &JsValue::from_str("delete"),
1079                        &JsValue::from_f64(len as f64),
1080                    )
1081                    .unwrap();
1082                }
1083            }
1084
1085            obj.into_js_result().unwrap()
1086        }
1087    }
1088
1089    pub fn text_diff_to_js_value(diff: &TextDiff) -> JsValue {
1090        let arr = Array::new();
1091        let mut i = 0;
1092        for v in diff.iter() {
1093            let (a, b) = text_diff_item_to_js_value(v);
1094            arr.set(i as u32, a);
1095            i += 1;
1096            if let Some(b) = b {
1097                arr.set(i as u32, b);
1098                i += 1;
1099            }
1100        }
1101
1102        arr.into_js_result().unwrap()
1103    }
1104
1105    fn text_diff_item_to_js_value(value: &TextDiffItem) -> (JsValue, Option<JsValue>) {
1106        match value {
1107            loro_delta::DeltaItem::Retain { len, attr } => {
1108                let obj = Object::new();
1109                js_sys::Reflect::set(
1110                    &obj,
1111                    &JsValue::from_str("retain"),
1112                    &JsValue::from_f64(*len as f64),
1113                )
1114                .unwrap();
1115                if !attr.is_empty() {
1116                    js_sys::Reflect::set(
1117                        &obj,
1118                        &JsValue::from_str("attributes"),
1119                        &JsValue::from(attr),
1120                    )
1121                    .unwrap();
1122                }
1123                (obj.into_js_result().unwrap(), None)
1124            }
1125            loro_delta::DeltaItem::Replace {
1126                value,
1127                attr,
1128                delete,
1129            } => {
1130                let mut a = None;
1131                let mut b = None;
1132                if value.rle_len() > 0 {
1133                    let obj = Object::new();
1134                    js_sys::Reflect::set(
1135                        &obj,
1136                        &JsValue::from_str("insert"),
1137                        &JsValue::from_str(value.as_str()),
1138                    )
1139                    .unwrap();
1140                    if !attr.is_empty() {
1141                        js_sys::Reflect::set(
1142                            &obj,
1143                            &JsValue::from_str("attributes"),
1144                            &JsValue::from(attr),
1145                        )
1146                        .unwrap();
1147                    }
1148                    a = Some(obj.into_js_result().unwrap());
1149                }
1150
1151                if *delete > 0 {
1152                    let obj = Object::new();
1153                    js_sys::Reflect::set(
1154                        &obj,
1155                        &JsValue::from_str("delete"),
1156                        &JsValue::from_f64(*delete as f64),
1157                    )
1158                    .unwrap();
1159                    b = Some(obj.into_js_result().unwrap());
1160                }
1161
1162                if a.is_none() {
1163                    a = std::mem::take(&mut b);
1164                }
1165
1166                (a.unwrap(), b)
1167            }
1168        }
1169    }
1170
1171    impl From<&StyleMeta> for JsValue {
1172        fn from(value: &StyleMeta) -> Self {
1173            // TODO: refactor: should we extract the common code of ToJson and ToJsValue
1174            let obj = Object::new();
1175            for (key, style) in value.iter() {
1176                let value = JsValue::from(style.data);
1177                js_sys::Reflect::set(&obj, &JsValue::from_str(&key), &value).unwrap();
1178            }
1179
1180            obj.into_js_result().unwrap()
1181        }
1182    }
1183
1184    impl From<&TextMeta> for JsValue {
1185        fn from(value: &TextMeta) -> Self {
1186            let obj = Object::new();
1187            for (key, value) in value.0.iter() {
1188                js_sys::Reflect::set(&obj, &JsValue::from_str(key), &JsValue::from(value.clone()))
1189                    .unwrap();
1190            }
1191
1192            obj.into_js_result().unwrap()
1193        }
1194    }
1195
1196    impl TryFrom<&JsValue> for TextMeta {
1197        type Error = JsValue;
1198
1199        fn try_from(value: &JsValue) -> Result<Self, Self::Error> {
1200            if value.is_null() || value.is_undefined() {
1201                return Ok(TextMeta::default());
1202            }
1203
1204            let obj = value.dyn_ref::<Object>().ok_or("Expected an object")?;
1205            let mut meta = TextMeta::default();
1206
1207            let entries = Object::entries(obj);
1208            for i in 0..entries.length() {
1209                let entry = entries.get(i);
1210                let entry_arr = entry.dyn_ref::<Array>().ok_or("Expected an array")?;
1211                let key = entry_arr
1212                    .get(0)
1213                    .as_string()
1214                    .ok_or("Expected a string key")?;
1215                let value = entry_arr.get(1);
1216                meta.0.insert(key, LoroValue::from(value));
1217            }
1218
1219            Ok(meta)
1220        }
1221    }
1222}