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(feature = "wasm")]
522pub mod wasm {
523    use crate::{
524        delta::{Delta, DeltaItem, Meta, StyleMeta, TreeDiff, TreeDiffItem, TreeExternalDiff},
525        event::{Index, TextDiff, TextDiffItem, TextMeta},
526        utils::string_slice::StringSlice,
527        TreeParentId,
528    };
529    use fractional_index::FractionalIndex;
530    use generic_btree::rle::HasLength;
531    use js_sys::{Array, Object};
532    use loro_common::{LoroValue, TreeID};
533    use wasm_bindgen::{JsCast, JsValue, __rt::IntoJsResult};
534
535    impl From<Index> for JsValue {
536        fn from(value: Index) -> Self {
537            match value {
538                Index::Key(key) => JsValue::from_str(&key),
539                Index::Seq(num) => JsValue::from_f64(num as f64),
540                Index::Node(node) => node.into(),
541            }
542        }
543    }
544
545    impl From<&TreeDiff> for JsValue {
546        fn from(value: &TreeDiff) -> Self {
547            let array = Array::new();
548            for diff in value.diff.iter() {
549                let obj = Object::new();
550                js_sys::Reflect::set(&obj, &"target".into(), &diff.target.into()).unwrap();
551                match &diff.action {
552                    TreeExternalDiff::Create {
553                        parent,
554                        index,
555                        position,
556                    } => {
557                        js_sys::Reflect::set(&obj, &"action".into(), &"create".into()).unwrap();
558                        js_sys::Reflect::set(
559                            &obj,
560                            &"parent".into(),
561                            &JsValue::from(parent.tree_id()),
562                        )
563                        .unwrap();
564                        js_sys::Reflect::set(&obj, &"index".into(), &(*index).into()).unwrap();
565                        js_sys::Reflect::set(
566                            &obj,
567                            &"fractionalIndex".into(),
568                            &position.to_string().into(),
569                        )
570                        .unwrap();
571                    }
572                    TreeExternalDiff::Delete {
573                        old_parent,
574                        old_index,
575                    } => {
576                        js_sys::Reflect::set(&obj, &"action".into(), &"delete".into()).unwrap();
577                        js_sys::Reflect::set(
578                            &obj,
579                            &"oldParent".into(),
580                            &JsValue::from(old_parent.tree_id()),
581                        )
582                        .unwrap();
583                        js_sys::Reflect::set(&obj, &"oldIndex".into(), &(*old_index).into())
584                            .unwrap();
585                    }
586                    TreeExternalDiff::Move {
587                        parent,
588                        index,
589                        position,
590                        old_parent,
591                        old_index,
592                    } => {
593                        js_sys::Reflect::set(&obj, &"action".into(), &"move".into()).unwrap();
594                        js_sys::Reflect::set(
595                            &obj,
596                            &"parent".into(),
597                            &JsValue::from(parent.tree_id()),
598                        )
599                        .unwrap();
600                        js_sys::Reflect::set(&obj, &"index".into(), &(*index).into()).unwrap();
601                        js_sys::Reflect::set(
602                            &obj,
603                            &"fractionalIndex".into(),
604                            &position.to_string().into(),
605                        )
606                        .unwrap();
607                        js_sys::Reflect::set(
608                            &obj,
609                            &"oldParent".into(),
610                            &JsValue::from(old_parent.tree_id()),
611                        )
612                        .unwrap();
613                        js_sys::Reflect::set(&obj, &"oldIndex".into(), &(*old_index).into())
614                            .unwrap();
615                    }
616                }
617                array.push(&obj);
618            }
619            array.into_js_result().unwrap()
620        }
621    }
622
623    impl TryFrom<&JsValue> for TreeDiff {
624        type Error = String;
625
626        fn try_from(value: &JsValue) -> Result<Self, Self::Error> {
627            if !value.is_array() {
628                return Err("Expected an array".to_string());
629            }
630
631            let array = js_sys::Array::from(value);
632            let mut diff = Vec::new();
633
634            for i in 0..array.length() {
635                let item = array.get(i);
636                if !item.is_object() {
637                    return Err(format!("Item at index {i} is not an object"));
638                }
639
640                let obj = js_sys::Object::from(item);
641                let target = js_sys::Reflect::get(&obj, &"target".into())
642                    .map_err(|e| format!("Failed to get target: {:?}", e))?;
643                let target = TreeID::try_from(target)
644                    .map_err(|e| format!("Failed to parse target: {:?}", e))?;
645
646                let action = js_sys::Reflect::get(&obj, &"action".into())
647                    .map_err(|e| format!("Failed to get action: {:?}", e))?;
648                let action = action
649                    .as_string()
650                    .ok_or_else(|| "action is not a string".to_string())?;
651
652                let action = match action.as_str() {
653                    "create" => {
654                        let parent = js_sys::Reflect::get(&obj, &"parent".into())
655                            .map_err(|e| format!("Failed to get parent: {:?}", e))?;
656                        let parent_id = if parent.is_null() || parent.is_undefined() {
657                            None
658                        } else {
659                            Some(
660                                TreeID::try_from(parent)
661                                    .map_err(|e| format!("Failed to parse parent: {:?}", e))?,
662                            )
663                        };
664                        let parent = TreeParentId::from(parent_id);
665                        let index = js_sys::Reflect::get(&obj, &"index".into())
666                            .map_err(|e| format!("Failed to get index: {:?}", e))?;
667                        let index = index
668                            .as_f64()
669                            .ok_or_else(|| "index is not a number".to_string())?
670                            as usize;
671
672                        let position = js_sys::Reflect::get(&obj, &"fractionalIndex".into())
673                            .map_err(|e| format!("Failed to get fractionalIndex: {:?}", e))?;
674                        let position = position
675                            .as_string()
676                            .ok_or_else(|| "fractionalIndex is not a string".to_string())?;
677                        let position = FractionalIndex::from_hex_string(position);
678
679                        TreeExternalDiff::Create {
680                            parent,
681                            index,
682                            position,
683                        }
684                    }
685                    "move" => {
686                        let parent = js_sys::Reflect::get(&obj, &"parent".into())
687                            .map_err(|e| format!("Failed to get parent: {:?}", e))?;
688                        let parent_id = if parent.is_null() || parent.is_undefined() {
689                            None
690                        } else {
691                            Some(
692                                TreeID::try_from(parent)
693                                    .map_err(|e| format!("Failed to parse parent: {:?}", e))?,
694                            )
695                        };
696                        let parent = TreeParentId::from(parent_id);
697
698                        let index = js_sys::Reflect::get(&obj, &"index".into())
699                            .map_err(|e| format!("Failed to get index: {:?}", e))?;
700                        let index = index
701                            .as_f64()
702                            .ok_or_else(|| "index is not a number".to_string())?
703                            as usize;
704
705                        let position = js_sys::Reflect::get(&obj, &"fractionalIndex".into())
706                            .map_err(|e| format!("Failed to get fractionalIndex: {:?}", e))?;
707                        let position = position
708                            .as_string()
709                            .ok_or_else(|| "fractionalIndex is not a string".to_string())?;
710                        let position = FractionalIndex::from_hex_string(position);
711
712                        let old_parent = js_sys::Reflect::get(&obj, &"oldParent".into())
713                            .map_err(|e| format!("Failed to get oldParent: {:?}", e))?;
714                        let old_parent_id = if old_parent.is_null() || old_parent.is_undefined() {
715                            None
716                        } else {
717                            Some(
718                                TreeID::try_from(old_parent)
719                                    .map_err(|e| format!("Failed to parse oldParent: {:?}", e))?,
720                            )
721                        };
722                        let old_parent = TreeParentId::from(old_parent_id);
723
724                        let old_index = js_sys::Reflect::get(&obj, &"oldIndex".into())
725                            .map_err(|e| format!("Failed to get oldIndex: {:?}", e))?;
726                        let old_index = old_index
727                            .as_f64()
728                            .ok_or_else(|| "oldIndex is not a number".to_string())?
729                            as usize;
730
731                        TreeExternalDiff::Move {
732                            parent,
733                            index,
734                            position,
735                            old_parent,
736                            old_index,
737                        }
738                    }
739                    "delete" => {
740                        let old_parent = js_sys::Reflect::get(&obj, &"oldParent".into())
741                            .map_err(|e| format!("Failed to get oldParent: {:?}", e))?;
742                        let old_parent_id = if old_parent.is_null() || old_parent.is_undefined() {
743                            None
744                        } else {
745                            Some(
746                                TreeID::try_from(old_parent)
747                                    .map_err(|e| format!("Failed to parse oldParent: {:?}", e))?,
748                            )
749                        };
750                        let old_parent = TreeParentId::from(old_parent_id);
751
752                        let old_index = js_sys::Reflect::get(&obj, &"oldIndex".into())
753                            .map_err(|e| format!("Failed to get oldIndex: {:?}", e))?;
754                        let old_index = old_index
755                            .as_f64()
756                            .ok_or_else(|| "oldIndex is not a number".to_string())?
757                            as usize;
758
759                        TreeExternalDiff::Delete {
760                            old_parent,
761                            old_index,
762                        }
763                    }
764                    action => Err(format!("Unknown tree diff action: {action}"))?,
765                };
766
767                diff.push(TreeDiffItem { target, action });
768            }
769
770            Ok(TreeDiff { diff })
771        }
772    }
773
774    impl From<&Delta<StringSlice, StyleMeta>> for JsValue {
775        fn from(value: &Delta<StringSlice, StyleMeta>) -> Self {
776            let arr = Array::new_with_length(value.len() as u32);
777            for (i, v) in value.iter().enumerate() {
778                arr.set(i as u32, JsValue::from(v.clone()));
779            }
780
781            arr.into_js_result().unwrap()
782        }
783    }
784
785    impl From<DeltaItem<StringSlice, StyleMeta>> for JsValue {
786        fn from(value: DeltaItem<StringSlice, StyleMeta>) -> Self {
787            let obj = Object::new();
788            match value {
789                DeltaItem::Retain {
790                    retain: len,
791                    attributes: meta,
792                } => {
793                    js_sys::Reflect::set(
794                        &obj,
795                        &JsValue::from_str("retain"),
796                        &JsValue::from_f64(len as f64),
797                    )
798                    .unwrap();
799                    if !meta.is_empty() {
800                        js_sys::Reflect::set(
801                            &obj,
802                            &JsValue::from_str("attributes"),
803                            &JsValue::from(&meta),
804                        )
805                        .unwrap();
806                    }
807                }
808                DeltaItem::Insert {
809                    insert: value,
810                    attributes: meta,
811                } => {
812                    js_sys::Reflect::set(
813                        &obj,
814                        &JsValue::from_str("insert"),
815                        &JsValue::from_str(value.as_str()),
816                    )
817                    .unwrap();
818                    if !meta.is_empty() {
819                        js_sys::Reflect::set(
820                            &obj,
821                            &JsValue::from_str("attributes"),
822                            &JsValue::from(&meta),
823                        )
824                        .unwrap();
825                    }
826                }
827                DeltaItem::Delete {
828                    delete: len,
829                    attributes: _,
830                } => {
831                    js_sys::Reflect::set(
832                        &obj,
833                        &JsValue::from_str("delete"),
834                        &JsValue::from_f64(len as f64),
835                    )
836                    .unwrap();
837                }
838            }
839
840            obj.into_js_result().unwrap()
841        }
842    }
843
844    pub fn text_diff_to_js_value(diff: &TextDiff) -> JsValue {
845        let arr = Array::new();
846        let mut i = 0;
847        for v in diff.iter() {
848            let (a, b) = text_diff_item_to_js_value(v);
849            arr.set(i as u32, a);
850            i += 1;
851            if let Some(b) = b {
852                arr.set(i as u32, b);
853                i += 1;
854            }
855        }
856
857        arr.into_js_result().unwrap()
858    }
859
860    fn text_diff_item_to_js_value(value: &TextDiffItem) -> (JsValue, Option<JsValue>) {
861        match value {
862            loro_delta::DeltaItem::Retain { len, attr } => {
863                let obj = Object::new();
864                js_sys::Reflect::set(
865                    &obj,
866                    &JsValue::from_str("retain"),
867                    &JsValue::from_f64(*len as f64),
868                )
869                .unwrap();
870                if !attr.is_empty() {
871                    js_sys::Reflect::set(
872                        &obj,
873                        &JsValue::from_str("attributes"),
874                        &JsValue::from(attr),
875                    )
876                    .unwrap();
877                }
878                (obj.into_js_result().unwrap(), None)
879            }
880            loro_delta::DeltaItem::Replace {
881                value,
882                attr,
883                delete,
884            } => {
885                let mut a = None;
886                let mut b = None;
887                if value.rle_len() > 0 {
888                    let obj = Object::new();
889                    js_sys::Reflect::set(
890                        &obj,
891                        &JsValue::from_str("insert"),
892                        &JsValue::from_str(value.as_str()),
893                    )
894                    .unwrap();
895                    if !attr.is_empty() {
896                        js_sys::Reflect::set(
897                            &obj,
898                            &JsValue::from_str("attributes"),
899                            &JsValue::from(attr),
900                        )
901                        .unwrap();
902                    }
903                    a = Some(obj.into_js_result().unwrap());
904                }
905
906                if *delete > 0 {
907                    let obj = Object::new();
908                    js_sys::Reflect::set(
909                        &obj,
910                        &JsValue::from_str("delete"),
911                        &JsValue::from_f64(*delete as f64),
912                    )
913                    .unwrap();
914                    b = Some(obj.into_js_result().unwrap());
915                }
916
917                if a.is_none() {
918                    a = std::mem::take(&mut b);
919                }
920
921                (a.unwrap(), b)
922            }
923        }
924    }
925
926    impl From<&StyleMeta> for JsValue {
927        fn from(value: &StyleMeta) -> Self {
928            // TODO: refactor: should we extract the common code of ToJson and ToJsValue
929            let obj = Object::new();
930            for (key, style) in value.iter() {
931                let value = JsValue::from(style.data);
932                js_sys::Reflect::set(&obj, &JsValue::from_str(&key), &value).unwrap();
933            }
934
935            obj.into_js_result().unwrap()
936        }
937    }
938
939    impl From<&TextMeta> for JsValue {
940        fn from(value: &TextMeta) -> Self {
941            let obj = Object::new();
942            for (key, value) in value.0.iter() {
943                js_sys::Reflect::set(&obj, &JsValue::from_str(key), &JsValue::from(value.clone()))
944                    .unwrap();
945            }
946
947            obj.into_js_result().unwrap()
948        }
949    }
950
951    impl TryFrom<&JsValue> for TextMeta {
952        type Error = JsValue;
953
954        fn try_from(value: &JsValue) -> Result<Self, Self::Error> {
955            if value.is_null() || value.is_undefined() {
956                return Ok(TextMeta::default());
957            }
958
959            let obj = value.dyn_ref::<Object>().ok_or("Expected an object")?;
960            let mut meta = TextMeta::default();
961
962            let entries = Object::entries(obj);
963            for i in 0..entries.length() {
964                let entry = entries.get(i);
965                let entry_arr = entry.dyn_ref::<Array>().ok_or("Expected an array")?;
966                let key = entry_arr
967                    .get(0)
968                    .as_string()
969                    .ok_or("Expected a string key")?;
970                let value = entry_arr.get(1);
971                meta.0.insert(key, LoroValue::from(value));
972            }
973
974            Ok(meta)
975        }
976    }
977}