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
12pub 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 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 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 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 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}