1use super::outdated_encode_reordered::ValueRegister;
2use crate::{
3 arena::SharedArena,
4 change::{Change, ChangeRef},
5 container::{
6 list::list_op::{DeleteSpan, DeleteSpanWithId, InnerListOp},
7 map::MapSet,
8 richtext::TextStyleInfoFlag,
9 tree::tree_op::TreeOp,
10 },
11 op::{FutureInnerContent, InnerContent, Op, SliceRange},
12 oplog::BlockChangeRef,
13 version::Frontiers,
14 OpLog, VersionVector,
15};
16use either::Either;
17use itertools::Itertools;
18use json::{JsonChange, JsonOpContent, JsonSchema};
19use loro_common::{
20 ContainerID, ContainerType, Counter, HasCounterSpan, HasId, IdLp, IdSpan, LoroError,
21 LoroResult, LoroValue, PeerID, TreeID, ID,
22};
23use rle::{HasLength, RleVec, Sliceable};
24use std::sync::Arc;
25
26const SCHEMA_VERSION: u8 = 1;
27
28fn refine_vv(vv: &VersionVector, oplog: &OpLog) -> VersionVector {
29 let mut refined = VersionVector::new();
30 for (&peer, &counter) in vv.iter() {
31 if counter <= 0 {
32 continue;
33 }
34 let end = oplog.vv().get(&peer).copied().unwrap_or(0);
35 if end <= counter {
36 refined.insert(peer, end);
37 } else {
38 refined.insert(peer, counter);
39 }
40 }
41 refined
42}
43
44pub(crate) fn export_json<'a, 'c: 'a>(
45 oplog: &'c OpLog,
46 start_vv: &VersionVector,
47 end_vv: &VersionVector,
48 with_peer_compression: bool,
49) -> JsonSchema {
50 let actual_start_vv = refine_vv(start_vv, oplog);
51 let actual_end_vv = refine_vv(end_vv, oplog);
52
53 let frontiers = oplog.dag.vv_to_frontiers(&actual_start_vv);
54
55 let diff_changes = init_encode(oplog, &actual_start_vv, &actual_end_vv);
56 if with_peer_compression {
57 let mut peer_register = ValueRegister::<PeerID>::new();
58 let changes = encode_changes(&diff_changes, &oplog.arena, Some(&mut peer_register));
59 JsonSchema {
60 changes,
61 schema_version: SCHEMA_VERSION,
62 peers: Some(peer_register.unwrap_vec()),
63 start_version: frontiers,
64 }
65 } else {
66 let changes = encode_changes(&diff_changes, &oplog.arena, None);
67 JsonSchema {
68 changes,
69 schema_version: SCHEMA_VERSION,
70 peers: None,
71 start_version: frontiers,
72 }
73 }
74}
75
76pub(crate) fn export_json_in_id_span(oplog: &OpLog, mut id_span: IdSpan) -> Vec<json::JsonChange> {
77 id_span.normalize_();
78 if id_span.counter.end <= 0 {
79 return vec![];
80 }
81 id_span.counter.start = id_span.counter.start.max(0);
82 id_span.counter.end = id_span.counter.end.max(0);
83
84 let end = oplog.vv().get(&id_span.peer).copied().unwrap_or(0);
85 if id_span.counter.start >= end {
86 return vec![];
87 }
88
89 id_span.counter.end = id_span.counter.end.min(end);
90 let mut diff_changes: Vec<Either<BlockChangeRef, Change>> = Vec::new();
91 while id_span.counter.start < id_span.counter.end {
92 let Some(change) = oplog.get_change_at(id_span.id_start()) else {
93 break;
94 };
95 let ctr_end = change.ctr_end();
96 if change.id.counter >= id_span.counter.start && change.ctr_end() <= id_span.counter.end {
97 diff_changes.push(Either::Left(change));
98 } else {
99 let start = if change.id.counter < id_span.counter.start {
100 (id_span.counter.start - change.id.counter) as usize
101 } else {
102 0
103 };
104
105 let end = if change.ctr_end() > id_span.counter.end {
106 (id_span.counter.end - change.id.counter) as usize
107 } else {
108 change.atom_len()
109 };
110
111 diff_changes.push(Either::Right(change.slice(start, end)));
112 }
113
114 id_span.counter.start = ctr_end;
115 }
116
117 encode_changes(&diff_changes, &oplog.arena, None)
118}
119
120pub(crate) fn decode_json_changes(
121 json: JsonSchema,
122 arena: &SharedArena,
123) -> LoroResult<Vec<Change>> {
124 decode_changes(json, arena)
125}
126
127fn init_encode<'s, 'a: 's>(
128 oplog: &'a OpLog,
129 start_vv: &VersionVector,
130 end_vv: &VersionVector,
131) -> Vec<Either<BlockChangeRef, Change>> {
132 let mut diff_changes: Vec<Either<BlockChangeRef, Change>> = Vec::new();
133 for change in oplog.iter_changes_peer_by_peer(start_vv, end_vv) {
134 let start_cnt = start_vv.get(&change.id.peer).copied().unwrap_or(0);
135 let end_cnt = end_vv.get(&change.id.peer).copied().unwrap_or(0);
136 if change.id.counter < start_cnt {
137 if change.ctr_end() <= start_cnt {
138 continue;
139 }
140
141 let offset = start_cnt - change.id.counter;
142 let to = change
143 .atom_len()
144 .min((end_cnt - change.id.counter) as usize);
145 diff_changes.push(Either::Right(change.slice(offset as usize, to)));
146 } else if change.id.counter + change.atom_len() as i32 > end_cnt {
147 let len = end_cnt - change.id.counter;
148 diff_changes.push(Either::Right(change.slice(0, len as usize)));
149 } else {
150 diff_changes.push(Either::Left(change));
151 }
152 }
153 diff_changes.sort_by_key(|x| match x {
154 Either::Left(c) => c.lamport,
155 Either::Right(c) => c.lamport,
156 });
157 diff_changes
158}
159
160fn register_id(id: &ID, peer_register: Option<&mut ValueRegister<PeerID>>) -> ID {
161 let peer = match peer_register {
162 Some(peer_register) => peer_register.register(&id.peer) as PeerID,
163 None => id.peer,
164 };
165 ID::new(peer as PeerID, id.counter)
166}
167
168fn register_idlp(idlp: &IdLp, peer_register: Option<&mut ValueRegister<PeerID>>) -> IdLp {
169 let peer = match peer_register {
170 Some(peer_register) => peer_register.register(&idlp.peer) as PeerID,
171 None => idlp.peer,
172 };
173 IdLp {
174 peer,
175 lamport: idlp.lamport,
176 }
177}
178
179fn register_tree_id(tree: &TreeID, peer_register: Option<&mut ValueRegister<PeerID>>) -> TreeID {
180 TreeID {
181 peer: match peer_register {
182 Some(peer_register) => peer_register.register(&tree.peer) as PeerID,
183 None => tree.peer,
184 },
185 counter: tree.counter,
186 }
187}
188
189fn register_container_id(
190 container: ContainerID,
191 peer_register: Option<&mut ValueRegister<PeerID>>,
192) -> ContainerID {
193 match container {
194 ContainerID::Normal {
195 peer,
196 counter,
197 container_type,
198 } => ContainerID::Normal {
199 peer: match peer_register {
200 Some(peer_register) => peer_register.register(&peer) as PeerID,
201 None => peer,
202 },
203 counter,
204 container_type,
205 },
206 r => r,
207 }
208}
209
210fn convert_container_id(
211 container: ContainerID,
212 peers: &Option<Vec<PeerID>>,
213) -> LoroResult<ContainerID> {
214 match container {
215 ContainerID::Normal {
216 peer,
217 counter,
218 container_type,
219 } => Ok(ContainerID::Normal {
220 peer: get_peer_from_peers(peers, peer)?,
221 counter,
222 container_type,
223 }),
224 r => Ok(r),
225 }
226}
227
228pub(crate) fn get_peer_from_peers(peers: &Option<Vec<PeerID>>, peer: PeerID) -> LoroResult<PeerID> {
229 match peers {
230 Some(peers) => peers.get(peer as usize).copied().ok_or_else(|| {
231 LoroError::DecodeError("peer index out of range in compressed peer table".into())
232 }),
233 None => Ok(peer),
234 }
235}
236
237fn convert_id(id: &ID, peers: &Option<Vec<PeerID>>) -> LoroResult<ID> {
238 Ok(ID {
239 peer: get_peer_from_peers(peers, id.peer)?,
240 counter: id.counter,
241 })
242}
243
244fn convert_idlp(idlp: &IdLp, peers: &Option<Vec<PeerID>>) -> LoroResult<IdLp> {
245 Ok(IdLp {
246 lamport: idlp.lamport,
247 peer: get_peer_from_peers(peers, idlp.peer)?,
248 })
249}
250
251fn convert_tree_id(tree: &TreeID, peers: &Option<Vec<PeerID>>) -> LoroResult<TreeID> {
252 Ok(TreeID {
253 peer: get_peer_from_peers(peers, tree.peer)?,
254 counter: tree.counter,
255 })
256}
257pub(crate) fn encode_change_to_json(change: ChangeRef<'_>, arena: &SharedArena) -> JsonSchema {
258 let f = change.deps.clone();
259 let changes = vec![encode_change(change, arena, None)];
260 JsonSchema {
261 changes,
262 schema_version: SCHEMA_VERSION,
263 peers: None,
264 start_version: f,
265 }
266}
267
268fn encode_changes(
269 diff_changes: &[Either<BlockChangeRef, Change>],
270 arena: &SharedArena,
271 mut peer_register: Option<&mut ValueRegister<PeerID>>,
272) -> Vec<json::JsonChange> {
273 let mut changes = Vec::with_capacity(diff_changes.len());
274 for change in diff_changes.iter() {
275 let change: &Change = match change {
276 Either::Left(c) => c,
277 Either::Right(c) => c,
278 };
279 let c = encode_change(
280 ChangeRef::from_change(change),
281 arena,
282 peer_register.as_deref_mut(),
283 );
284 changes.push(c);
285 }
286 changes
287}
288
289pub(crate) fn encode_change(
290 change: ChangeRef<'_, Op>,
291 arena: &SharedArena,
292 mut peer_register: Option<&mut ValueRegister<PeerID>>,
293) -> JsonChange {
294 let mut ops = Vec::with_capacity(change.ops.len());
295 for Op {
296 counter,
297 container,
298 content,
299 } in change.ops.iter()
300 {
301 let mut container = arena.get_container_id(*container).unwrap();
302 if container.is_normal() {
303 container = register_container_id(container, peer_register.as_deref_mut());
304 }
305 let op = match container.container_type() {
306 ContainerType::List => match content {
307 InnerContent::List(list) => JsonOpContent::List(match list {
308 InnerListOp::Insert { slice, pos } => {
309 let mut values =
310 arena.get_values(slice.0.start as usize..slice.0.end as usize);
311 values.iter_mut().for_each(|x| {
312 if let LoroValue::Container(id) = x {
313 if id.is_normal() {
314 *id = register_container_id(
315 id.clone(),
316 peer_register.as_deref_mut(),
317 );
318 }
319 }
320 });
321 json::ListOp::Insert {
322 pos: *pos as u32,
323 value: values,
324 }
325 }
326 InnerListOp::Delete(DeleteSpanWithId {
327 id_start,
328 span: DeleteSpan { pos, signed_len },
329 }) => json::ListOp::Delete {
330 pos: *pos as i32,
331 len: *signed_len as i32,
332 start_id: register_id(id_start, peer_register.as_deref_mut()),
333 },
334 _ => unreachable!(),
335 }),
336 _ => unreachable!(),
337 },
338 ContainerType::MovableList => match content {
339 InnerContent::List(list) => JsonOpContent::MovableList(match list {
340 InnerListOp::Insert { slice, pos } => {
341 let mut values =
342 arena.get_values(slice.0.start as usize..slice.0.end as usize);
343 values.iter_mut().for_each(|x| {
344 if let LoroValue::Container(id) = x {
345 if id.is_normal() {
346 *id = register_container_id(
347 id.clone(),
348 peer_register.as_deref_mut(),
349 );
350 }
351 }
352 });
353 json::MovableListOp::Insert {
354 pos: *pos as u32,
355 value: values,
356 }
357 }
358 InnerListOp::Delete(DeleteSpanWithId {
359 id_start,
360 span: DeleteSpan { pos, signed_len },
361 }) => json::MovableListOp::Delete {
362 pos: *pos as i32,
363 len: *signed_len as i32,
364 start_id: register_id(id_start, peer_register.as_deref_mut()),
365 },
366 InnerListOp::Move {
367 from,
368 elem_id: from_id,
369 to,
370 } => json::MovableListOp::Move {
371 from: *from,
372 to: *to,
373 elem_id: register_idlp(from_id, peer_register.as_deref_mut()),
374 },
375 InnerListOp::Set { elem_id, value } => {
376 let value = if let LoroValue::Container(id) = value {
377 if id.is_normal() {
378 LoroValue::Container(register_container_id(
379 id.clone(),
380 peer_register.as_deref_mut(),
381 ))
382 } else {
383 value.clone()
384 }
385 } else {
386 value.clone()
387 };
388 json::MovableListOp::Set {
389 elem_id: register_idlp(elem_id, peer_register.as_deref_mut()),
390 value,
391 }
392 }
393 _ => unreachable!(),
394 }),
395 _ => unreachable!(),
396 },
397 ContainerType::Text => match content {
398 InnerContent::List(list) => JsonOpContent::Text(match list {
399 InnerListOp::InsertText {
400 slice,
401 unicode_start: _,
402 unicode_len: _,
403 pos,
404 } => {
405 let text = String::from_utf8(slice.as_bytes().to_vec()).unwrap();
406 json::TextOp::Insert { pos: *pos, text }
407 }
408 InnerListOp::Delete(DeleteSpanWithId {
409 id_start,
410 span: DeleteSpan { pos, signed_len },
411 }) => json::TextOp::Delete {
412 pos: *pos as i32,
413 len: *signed_len as i32,
414 start_id: register_id(id_start, peer_register.as_deref_mut()),
415 },
416 InnerListOp::StyleStart {
417 start,
418 end,
419 key,
420 value,
421 info,
422 } => json::TextOp::Mark {
423 start: *start,
424 end: *end,
425 style_key: key.to_string(),
426 style_value: value.clone(),
427 info: info.to_byte(),
428 },
429 InnerListOp::StyleEnd => json::TextOp::MarkEnd,
430 _ => unreachable!(),
431 }),
432 _ => unreachable!(),
433 },
434 ContainerType::Map => match content {
435 InnerContent::Map(MapSet { key, value }) => {
436 JsonOpContent::Map(if let Some(v) = value {
437 let value = if let LoroValue::Container(id) = v {
438 if id.is_normal() {
439 LoroValue::Container(register_container_id(
440 id.clone(),
441 peer_register.as_deref_mut(),
442 ))
443 } else {
444 v.clone()
445 }
446 } else {
447 v.clone()
448 };
449 json::MapOp::Insert {
450 key: key.to_string(),
451 value,
452 }
453 } else {
454 json::MapOp::Delete {
455 key: key.to_string(),
456 }
457 })
458 }
459
460 _ => unreachable!(),
461 },
462
463 ContainerType::Tree => match content {
464 InnerContent::Tree(op) => JsonOpContent::Tree(match &**op {
465 TreeOp::Create {
466 target,
467 parent,
468 position,
469 } => json::TreeOp::Create {
470 target: register_tree_id(target, peer_register.as_deref_mut()),
471 parent: parent.map(|p| register_tree_id(&p, peer_register.as_deref_mut())),
472 fractional_index: position.clone(),
473 },
474 TreeOp::Move {
475 target,
476 parent,
477 position,
478 } => json::TreeOp::Move {
479 target: register_tree_id(target, peer_register.as_deref_mut()),
480 parent: parent.map(|p| register_tree_id(&p, peer_register.as_deref_mut())),
481 fractional_index: position.clone(),
482 },
483 TreeOp::Delete { target } => json::TreeOp::Delete {
484 target: register_tree_id(target, peer_register.as_deref_mut()),
485 },
486 }),
487 _ => unreachable!(),
488 },
489 ContainerType::Unknown(_) => {
490 let InnerContent::Future(FutureInnerContent::Unknown { prop, value }) = content
491 else {
492 unreachable!();
493 };
494 JsonOpContent::Future(json::FutureOpWrapper {
495 prop: *prop,
496 value: json::FutureOp::Unknown((**value).clone()),
497 })
498 }
499 #[cfg(feature = "counter")]
500 ContainerType::Counter => {
501 let InnerContent::Future(f) = content else {
502 unreachable!()
503 };
504 match f {
505 FutureInnerContent::Counter(x) => {
506 JsonOpContent::Future(json::FutureOpWrapper {
507 prop: 0,
508 value: json::FutureOp::Counter(super::OwnedValue::F64(*x)),
509 })
510 }
511 _ => unreachable!(),
512 }
513 }
514 };
515 ops.push(json::JsonOp {
516 counter: *counter,
517 container,
518 content: op,
519 });
520 }
521 let c = json::JsonChange {
522 id: register_id(change.id, peer_register.as_deref_mut()),
523 ops,
524 deps: change
525 .deps
526 .iter()
527 .sorted()
529 .map(|id| register_id(&id, peer_register.as_deref_mut()))
530 .collect(),
531 lamport: *change.lamport,
532 timestamp: *change.timestamp,
533 msg: change.commit_msg.as_deref().map(|x| x.to_string()),
534 };
535 c
536}
537
538fn decode_changes(json: JsonSchema, arena: &SharedArena) -> LoroResult<Vec<Change>> {
539 let JsonSchema {
540 schema_version,
541 start_version,
542 peers,
543 changes,
544 } = json;
545 if schema_version != SCHEMA_VERSION {
546 return Err(LoroError::DecodeError(
547 format!(
548 "unsupported json schema version: expected {SCHEMA_VERSION}, got {schema_version}"
549 )
550 .into_boxed_str(),
551 ));
552 }
553 validate_json_frontiers(&start_version)?;
554
555 let mut ans = Vec::with_capacity(changes.len());
556 for json::JsonChange {
557 id,
558 timestamp,
559 deps,
560 lamport,
561 msg,
562 ops: json_ops,
563 } in changes
564 {
565 let id = convert_id(&id, &peers)?;
566 validate_json_id_counter(id, "change id")?;
567 let mut ops: RleVec<[Op; 1]> = RleVec::new();
568 let mut expected_counter = id.counter;
569 if json_ops.is_empty() {
570 return Err(LoroError::DecodeError(
571 "invalid json change: change must contain at least one op".into(),
572 ));
573 }
574
575 for op in json_ops {
576 let next_counter = validate_json_op_counter(expected_counter, &op)?;
577 let op = decode_op(op, arena, &peers)?;
578 validate_json_op_created_container_ids(id.peer, &op, arena)?;
579 ops.push(op);
580 expected_counter = next_counter;
581 }
582
583 let deps = deps
584 .into_iter()
585 .map(|id| convert_id(&id, &peers))
586 .collect::<LoroResult<Vec<_>>>()?;
587 for dep in &deps {
588 validate_json_id_counter(*dep, "dependency id")?;
589 }
590
591 let change = Change {
592 id,
593 timestamp,
594 deps: Frontiers::from_iter(deps),
595 lamport,
596 ops,
597 commit_msg: msg.map(|x| x.into()),
598 };
599 ans.push(change);
600 }
601 Ok(ans)
602}
603
604fn validate_json_frontiers(frontiers: &Frontiers) -> LoroResult<()> {
605 for id in frontiers.iter() {
606 validate_json_id_counter(id, "start version id")?;
607 }
608
609 Ok(())
610}
611
612fn validate_json_id_counter(id: ID, name: &str) -> LoroResult<()> {
613 if id.counter < 0 {
614 return Err(LoroError::DecodeError(
615 format!("invalid json counter: {name} counter must be non-negative").into_boxed_str(),
616 ));
617 }
618
619 Ok(())
620}
621
622fn validate_json_tree_id_counter(id: &TreeID, name: &str) -> LoroResult<()> {
623 if id.counter < 0 {
624 return Err(LoroError::DecodeError(
625 format!("invalid json counter: {name} counter must be non-negative").into_boxed_str(),
626 ));
627 }
628
629 Ok(())
630}
631
632fn validate_json_container_id_counter(id: &ContainerID, name: &str) -> LoroResult<()> {
633 if let ContainerID::Normal { counter, .. } = id {
634 if *counter < 0 {
635 return Err(LoroError::DecodeError(
636 format!("invalid json counter: {name} counter must be non-negative")
637 .into_boxed_str(),
638 ));
639 }
640 }
641
642 Ok(())
643}
644
645fn validate_json_op_counter(expected_counter: Counter, op: &json::JsonOp) -> LoroResult<Counter> {
646 let op_len = op.content.op_len();
647 if op_len == 0 {
648 return Err(LoroError::DecodeError(
649 "invalid json op counter: op length must be greater than zero".into(),
650 ));
651 }
652
653 if expected_counter < 0 || op.counter < 0 {
654 return Err(LoroError::DecodeError(
655 "invalid json op counter: op counters must be non-negative".into(),
656 ));
657 }
658
659 let op_len = Counter::try_from(op_len).map_err(|_| {
660 LoroError::DecodeError("invalid json op counter: op length is too large".into())
661 })?;
662 if op.counter != expected_counter {
663 return Err(LoroError::DecodeError(
664 "invalid json op counter: op counters must be contiguous with the change id".into(),
665 ));
666 }
667
668 expected_counter
669 .checked_add(op_len)
670 .ok_or_else(|| LoroError::DecodeError("invalid json op counter: counter overflow".into()))
671}
672
673fn validate_json_op_created_container_ids(
674 peer: PeerID,
675 op: &Op,
676 arena: &SharedArena,
677) -> LoroResult<()> {
678 match &op.content {
679 InnerContent::List(list) => match list {
680 InnerListOp::Insert { slice, .. } => {
681 for (offset, value) in arena.iter_value_slice(slice.to_range()).enumerate() {
682 let LoroValue::Container(id) = value else {
683 validate_json_value_has_no_container_refs(&value)?;
684 continue;
685 };
686 let offset = Counter::try_from(offset).map_err(|_| {
687 LoroError::DecodeError(
688 "invalid json container id: list offset is too large".into(),
689 )
690 })?;
691 let counter = op.counter.checked_add(offset).ok_or_else(|| {
692 LoroError::DecodeError("invalid json container id: counter overflow".into())
693 })?;
694 validate_json_created_container_id(&id, ID::new(peer, counter))?;
695 }
696 }
697 InnerListOp::Set { value, .. } => {
698 if let LoroValue::Container(id) = value {
699 validate_json_created_container_id(id, ID::new(peer, op.counter))?;
700 } else {
701 validate_json_value_has_no_container_refs(value)?;
702 }
703 }
704 InnerListOp::Move { .. }
705 | InnerListOp::InsertText { .. }
706 | InnerListOp::Delete(_)
707 | InnerListOp::StyleEnd => {}
708 InnerListOp::StyleStart { value, .. } => {
709 validate_json_value_has_no_container_refs(value)?;
710 }
711 },
712 InnerContent::Map(map) => {
713 if let Some(value) = &map.value {
714 if let LoroValue::Container(id) = value {
715 validate_json_created_container_id(id, ID::new(peer, op.counter))?;
716 } else {
717 validate_json_value_has_no_container_refs(value)?;
718 }
719 }
720 }
721 InnerContent::Tree(tree) => {
722 if let TreeOp::Create { target, .. } = tree.as_ref() {
723 validate_json_tree_create_target(target, ID::new(peer, op.counter))?;
724 }
725 }
726 InnerContent::Future(_) => {}
727 }
728
729 Ok(())
730}
731
732fn validate_json_value_has_no_container_refs(value: &LoroValue) -> LoroResult<()> {
733 let mut stack = vec![value];
734 while let Some(value) = stack.pop() {
735 match value {
736 LoroValue::Container(_) => {
737 return Err(LoroError::DecodeError(
738 "invalid json container id: container values must not be nested".into(),
739 ));
740 }
741 LoroValue::List(list) => {
742 stack.extend(list.iter());
743 }
744 LoroValue::Map(map) => {
745 stack.extend(map.values());
746 }
747 LoroValue::Null
748 | LoroValue::Bool(_)
749 | LoroValue::Double(_)
750 | LoroValue::I64(_)
751 | LoroValue::Binary(_)
752 | LoroValue::String(_) => {}
753 }
754 }
755
756 Ok(())
757}
758
759fn validate_json_created_container_id(id: &ContainerID, expected_id: ID) -> LoroResult<()> {
760 let ContainerID::Normal { peer, counter, .. } = id else {
761 return Err(LoroError::DecodeError(
762 "invalid json container id: created child containers must be normal".into(),
763 ));
764 };
765
766 if *peer != expected_id.peer || *counter != expected_id.counter {
767 return Err(LoroError::DecodeError(
768 "invalid json container id: created child container id must match the op id".into(),
769 ));
770 }
771
772 Ok(())
773}
774
775fn validate_json_tree_create_target(target: &TreeID, expected_id: ID) -> LoroResult<()> {
776 if target.peer != expected_id.peer || target.counter != expected_id.counter {
777 return Err(LoroError::DecodeError(
778 "invalid json tree target: tree create target must match the op id".into(),
779 ));
780 }
781
782 Ok(())
783}
784
785fn decode_op(op: json::JsonOp, arena: &SharedArena, peers: &Option<Vec<PeerID>>) -> LoroResult<Op> {
786 let json::JsonOp {
787 counter,
788 container,
789 content,
790 } = op;
791 let container = convert_container_id(container, peers)?;
792 validate_json_container_id_counter(&container, "op container")?;
793 let idx = arena.register_container(&container);
794 let content = match container.container_type() {
795 ContainerType::Text => match content {
796 JsonOpContent::Text(text) => match text {
797 json::TextOp::Insert { pos, text } => {
798 let (slice, result) = arena.alloc_str_with_slice(&text);
799 InnerContent::List(InnerListOp::InsertText {
800 slice,
801 unicode_start: result.start as u32,
802 unicode_len: (result.end - result.start) as u32,
803 pos,
804 })
805 }
806 json::TextOp::Delete {
807 pos,
808 len,
809 start_id: id_start,
810 } => {
811 let id_start = convert_id(&id_start, peers)?;
812 validate_json_id_counter(id_start, "text delete start id")?;
813 InnerContent::List(InnerListOp::Delete(DeleteSpanWithId {
814 id_start,
815 span: DeleteSpan {
816 pos: pos as isize,
817 signed_len: len as isize,
818 },
819 }))
820 }
821 json::TextOp::Mark {
822 start,
823 end,
824 style_key,
825 style_value,
826 info,
827 } => InnerContent::List(InnerListOp::StyleStart {
828 start,
829 end,
830 key: style_key.into(),
831 value: style_value,
832 info: TextStyleInfoFlag::from_byte(info),
833 }),
834 json::TextOp::MarkEnd => InnerContent::List(InnerListOp::StyleEnd),
835 },
836 _ => {
837 return Err(LoroError::DecodeError(
838 "invalid op content for text container".into(),
839 ))
840 }
841 },
842 ContainerType::List => match content {
843 JsonOpContent::List(list) => match list {
844 json::ListOp::Insert {
845 pos,
846 value: mut values,
847 } => {
848 for v in values.iter_mut() {
849 if let LoroValue::Container(id) = v {
850 if id.is_normal() {
851 *id = convert_container_id(id.clone(), peers)?;
852 }
853 }
854 }
855 let range = arena.alloc_values(values.iter().cloned());
856 InnerContent::List(InnerListOp::Insert {
857 slice: SliceRange::new(range.start as u32..range.end as u32),
858 pos: pos as usize,
859 })
860 }
861 json::ListOp::Delete { pos, len, start_id } => {
862 let start_id = convert_id(&start_id, peers)?;
863 validate_json_id_counter(start_id, "list delete start id")?;
864 InnerContent::List(InnerListOp::Delete(DeleteSpanWithId {
865 id_start: start_id,
866 span: DeleteSpan {
867 pos: pos as isize,
868 signed_len: len as isize,
869 },
870 }))
871 }
872 },
873 _ => {
874 return Err(LoroError::DecodeError(
875 "invalid op content for list container".into(),
876 ))
877 }
878 },
879 ContainerType::MovableList => match content {
880 JsonOpContent::MovableList(list) => match list {
881 json::MovableListOp::Insert {
882 pos,
883 value: mut values,
884 } => {
885 for v in values.iter_mut() {
886 if let LoroValue::Container(id) = v {
887 if id.is_normal() {
888 *id = convert_container_id(id.clone(), peers)?;
889 }
890 }
891 }
892 let range = arena.alloc_values(values.iter().cloned());
893 InnerContent::List(InnerListOp::Insert {
894 slice: SliceRange::new(range.start as u32..range.end as u32),
895 pos: pos as usize,
896 })
897 }
898 json::MovableListOp::Delete { pos, len, start_id } => {
899 let start_id = convert_id(&start_id, peers)?;
900 validate_json_id_counter(start_id, "movable list delete start id")?;
901 InnerContent::List(InnerListOp::Delete(DeleteSpanWithId {
902 id_start: start_id,
903 span: DeleteSpan {
904 pos: pos as isize,
905 signed_len: len as isize,
906 },
907 }))
908 }
909 json::MovableListOp::Move {
910 from,
911 elem_id: from_id,
912 to,
913 } => {
914 let from_id = convert_idlp(&from_id, peers)?;
915 InnerContent::List(InnerListOp::Move {
916 from,
917 elem_id: from_id,
918 to,
919 })
920 }
921 json::MovableListOp::Set { elem_id, mut value } => {
922 let elem_id = convert_idlp(&elem_id, peers)?;
923 if let LoroValue::Container(id) = &mut value {
924 *id = convert_container_id(id.clone(), peers)?;
925 }
926 InnerContent::List(InnerListOp::Set { elem_id, value })
927 }
928 },
929 _ => {
930 return Err(LoroError::DecodeError(
931 "invalid op content for movable list container".into(),
932 ))
933 }
934 },
935 ContainerType::Map => match content {
936 JsonOpContent::Map(map) => match map {
937 json::MapOp::Insert { key, mut value } => {
938 if let LoroValue::Container(id) = &mut value {
939 *id = convert_container_id(id.clone(), peers)?;
940 }
941 InnerContent::Map(MapSet {
942 key: key.into(),
943 value: Some(value),
944 })
945 }
946 json::MapOp::Delete { key } => InnerContent::Map(MapSet {
947 key: key.into(),
948 value: None,
949 }),
950 },
951 _ => {
952 return Err(LoroError::DecodeError(
953 "invalid op content for map container".into(),
954 ))
955 }
956 },
957 ContainerType::Tree => match content {
958 JsonOpContent::Tree(tree) => match tree {
959 json::TreeOp::Create {
960 target,
961 parent,
962 fractional_index,
963 } => {
964 let target = convert_tree_id(&target, peers)?;
965 validate_json_tree_id_counter(&target, "tree create target")?;
966 let parent = match parent {
967 Some(p) => {
968 let parent = convert_tree_id(&p, peers)?;
969 validate_json_tree_id_counter(&parent, "tree create parent")?;
970 Some(parent)
971 }
972 None => None,
973 };
974 InnerContent::Tree(Arc::new(TreeOp::Create {
975 target,
976 parent,
977 position: fractional_index,
978 }))
979 }
980 json::TreeOp::Move {
981 target,
982 parent,
983 fractional_index,
984 } => {
985 let target = convert_tree_id(&target, peers)?;
986 validate_json_tree_id_counter(&target, "tree move target")?;
987 let parent = match parent {
988 Some(p) => {
989 let parent = convert_tree_id(&p, peers)?;
990 validate_json_tree_id_counter(&parent, "tree move parent")?;
991 Some(parent)
992 }
993 None => None,
994 };
995 InnerContent::Tree(Arc::new(TreeOp::Move {
996 target,
997 parent,
998 position: fractional_index,
999 }))
1000 }
1001 json::TreeOp::Delete { target } => {
1002 let target = convert_tree_id(&target, peers)?;
1003 validate_json_tree_id_counter(&target, "tree delete target")?;
1004 InnerContent::Tree(Arc::new(TreeOp::Delete { target }))
1005 }
1006 },
1007 _ => {
1008 return Err(LoroError::DecodeError(
1009 "invalid op content for tree container".into(),
1010 ))
1011 }
1012 },
1013 ContainerType::Unknown(_) => match content {
1014 JsonOpContent::Future(json::FutureOpWrapper {
1015 prop,
1016 value: json::FutureOp::Unknown(value),
1017 }) => InnerContent::Future(FutureInnerContent::Unknown {
1018 prop,
1019 value: Box::new(value),
1020 }),
1021 _ => {
1022 return Err(LoroError::DecodeError(
1023 "invalid op content for unknown container".into(),
1024 ))
1025 }
1026 },
1027 #[cfg(feature = "counter")]
1028 ContainerType::Counter => {
1029 let JsonOpContent::Future(json::FutureOpWrapper { prop: _, value }) = content else {
1030 return Err(LoroError::DecodeError(
1031 "invalid op content for counter container".into(),
1032 ));
1033 };
1034 use crate::encoding::OwnedValue;
1035 match value {
1036 json::FutureOp::Counter(OwnedValue::F64(c))
1037 | json::FutureOp::Unknown(OwnedValue::F64(c)) => {
1038 InnerContent::Future(FutureInnerContent::Counter(c))
1039 }
1040 json::FutureOp::Counter(OwnedValue::I64(c))
1041 | json::FutureOp::Unknown(OwnedValue::I64(c)) => {
1042 InnerContent::Future(FutureInnerContent::Counter(c as f64))
1043 }
1044 _ => {
1045 return Err(LoroError::DecodeError(
1046 "invalid counter op value type".into(),
1047 ))
1048 }
1049 }
1050 } };
1052 Ok(Op {
1053 counter,
1054 container: idx,
1055 content,
1056 })
1057}
1058
1059impl TryFrom<&str> for JsonSchema {
1060 type Error = serde_json::Error;
1061
1062 fn try_from(value: &str) -> Result<Self, Self::Error> {
1063 serde_json::from_str(value)
1064 }
1065}
1066
1067impl TryFrom<&String> for JsonSchema {
1068 type Error = serde_json::Error;
1069
1070 fn try_from(value: &String) -> Result<Self, Self::Error> {
1071 serde_json::from_str(value)
1072 }
1073}
1074
1075impl TryFrom<String> for JsonSchema {
1076 type Error = serde_json::Error;
1077
1078 fn try_from(value: String) -> Result<Self, Self::Error> {
1079 serde_json::from_str(&value)
1080 }
1081}
1082
1083pub mod json {
1084 use crate::{
1085 encoding::OwnedValue,
1086 version::{Frontiers, VersionRange},
1087 };
1088 use fractional_index::FractionalIndex;
1089 use loro_common::{ContainerID, Counter, IdLp, Lamport, LoroValue, PeerID, TreeID, ID};
1090 use serde::{Deserialize, Serialize};
1091 use std::ops::Range;
1092
1093 use super::{get_peer_from_peers, redact_value};
1094
1095 #[derive(Debug, Clone, Serialize, Deserialize)]
1096 pub struct JsonSchema {
1097 pub schema_version: u8,
1098 #[serde(with = "self::serde_impl::frontiers")]
1099 pub start_version: Frontiers,
1100 #[serde(with = "self::serde_impl::peer_id")]
1101 pub peers: Option<Vec<PeerID>>,
1102 pub changes: Vec<JsonChange>,
1103 }
1104
1105 #[derive(Debug, Clone, Serialize, Deserialize)]
1106 pub struct JsonChange {
1107 #[serde(with = "self::serde_impl::id")]
1108 pub id: ID,
1109 pub timestamp: i64,
1110 #[serde(with = "self::serde_impl::deps")]
1111 pub deps: Vec<ID>,
1112 pub lamport: Lamport,
1113 pub msg: Option<String>,
1114 pub ops: Vec<JsonOp>,
1115 }
1116
1117 impl JsonChange {
1118 pub fn checked_op_len(&self) -> Option<usize> {
1119 let Some(last_op) = self.ops.last() else {
1120 return Some(0);
1121 };
1122 if last_op.counter < self.id.counter {
1123 return None;
1124 }
1125
1126 let counter_delta = last_op.counter.checked_sub(self.id.counter)?;
1127 let counter_delta = usize::try_from(counter_delta).ok()?;
1128 counter_delta.checked_add(last_op.content.op_len())
1129 }
1130
1131 pub fn op_len(&self) -> usize {
1132 self.checked_op_len().unwrap_or(0)
1133 }
1134 }
1135
1136 #[derive(Debug, Clone)]
1137 pub struct JsonOp {
1138 pub content: JsonOpContent,
1139 pub container: ContainerID,
1140 pub counter: i32,
1141 }
1142
1143 #[derive(Debug, Clone, Serialize, Deserialize)]
1144 #[serde(untagged)]
1145 pub enum JsonOpContent {
1146 List(ListOp),
1147 MovableList(MovableListOp),
1148 Map(MapOp),
1149 Text(TextOp),
1150 Tree(TreeOp),
1151 Future(FutureOpWrapper),
1153 }
1154
1155 impl JsonOpContent {
1156 pub fn op_len(&self) -> usize {
1157 match self {
1158 JsonOpContent::List(list_op) => list_op.op_len(),
1159 JsonOpContent::MovableList(movable_list_op) => movable_list_op.op_len(),
1160 JsonOpContent::Map(..) => 1,
1161 JsonOpContent::Text(text_op) => text_op.op_len(),
1162 JsonOpContent::Tree(..) => 1,
1163 JsonOpContent::Future(..) => 1,
1164 }
1165 }
1166 }
1167
1168 #[derive(Debug, Clone, Serialize, Deserialize)]
1169 pub struct FutureOpWrapper {
1170 #[serde(flatten)]
1171 pub value: FutureOp,
1172 pub prop: i32,
1173 }
1174
1175 #[derive(Debug, Clone, Serialize, Deserialize)]
1176 #[serde(tag = "type", rename_all = "snake_case")]
1177 pub enum ListOp {
1178 Insert {
1179 pos: u32,
1180 value: Vec<LoroValue>,
1181 },
1182 Delete {
1183 pos: i32,
1184 len: i32,
1185 #[serde(with = "self::serde_impl::id")]
1186 start_id: ID,
1187 },
1188 }
1189
1190 impl ListOp {
1191 fn op_len(&self) -> usize {
1192 match self {
1193 ListOp::Insert { value: values, .. } => values.len(),
1194 ListOp::Delete { len, .. } => (*len).unsigned_abs() as usize,
1195 }
1196 }
1197 }
1198
1199 #[derive(Debug, Clone, Serialize, Deserialize)]
1200 #[serde(tag = "type", rename_all = "snake_case")]
1201 pub enum MovableListOp {
1202 Insert {
1203 pos: u32,
1204 value: Vec<LoroValue>,
1205 },
1206 Delete {
1207 pos: i32,
1208 len: i32,
1209 #[serde(with = "self::serde_impl::id")]
1210 start_id: ID,
1211 },
1212 Move {
1213 from: u32,
1214 to: u32,
1215 #[serde(with = "self::serde_impl::idlp")]
1216 elem_id: IdLp,
1217 },
1218 Set {
1219 #[serde(with = "self::serde_impl::idlp")]
1220 elem_id: IdLp,
1221 value: LoroValue,
1222 },
1223 }
1224
1225 impl MovableListOp {
1226 fn op_len(&self) -> usize {
1227 match self {
1228 MovableListOp::Insert { value: values, .. } => values.len(),
1229 MovableListOp::Delete { len, .. } => (*len).unsigned_abs() as usize,
1230 MovableListOp::Move { .. } => 1,
1231 MovableListOp::Set { .. } => 1,
1232 }
1233 }
1234 }
1235
1236 #[derive(Debug, Clone, Serialize, Deserialize)]
1237 #[serde(tag = "type", rename_all = "snake_case")]
1238 pub enum MapOp {
1239 Insert { key: String, value: LoroValue },
1240 Delete { key: String },
1241 }
1242
1243 #[derive(Debug, Clone, Serialize, Deserialize)]
1244 #[serde(tag = "type", rename_all = "snake_case")]
1245 pub enum TextOp {
1246 Insert {
1247 pos: u32,
1248 text: String,
1249 },
1250 Delete {
1251 pos: i32,
1252 len: i32,
1253 #[serde(with = "self::serde_impl::id")]
1254 start_id: ID,
1255 },
1256 Mark {
1257 start: u32,
1258 end: u32,
1259 style_key: String,
1260 style_value: LoroValue,
1261 info: u8,
1262 },
1263 MarkEnd,
1264 }
1265
1266 impl TextOp {
1267 fn op_len(&self) -> usize {
1268 match self {
1269 TextOp::Insert { text, .. } => text.chars().count(),
1270 TextOp::Delete { len, .. } => len.unsigned_abs() as usize,
1271 TextOp::Mark { .. } => 1,
1272 TextOp::MarkEnd => 1,
1273 }
1274 }
1275 }
1276
1277 #[derive(Debug, Clone, Serialize, Deserialize)]
1278 #[serde(tag = "type", rename_all = "snake_case")]
1279 pub enum TreeOp {
1280 Create {
1281 #[serde(with = "self::serde_impl::tree_id")]
1282 target: TreeID,
1283 #[serde(with = "self::serde_impl::option_tree_id")]
1284 parent: Option<TreeID>,
1285 #[serde(default, with = "self::serde_impl::fractional_index")]
1286 fractional_index: FractionalIndex,
1287 },
1288 Move {
1289 #[serde(with = "self::serde_impl::tree_id")]
1290 target: TreeID,
1291 #[serde(with = "self::serde_impl::option_tree_id")]
1292 parent: Option<TreeID>,
1293 #[serde(default, with = "self::serde_impl::fractional_index")]
1294 fractional_index: FractionalIndex,
1295 },
1296 Delete {
1297 #[serde(with = "self::serde_impl::tree_id")]
1298 target: TreeID,
1299 },
1300 }
1301
1302 #[derive(Debug, Clone, Serialize, Deserialize)]
1303 #[serde(tag = "type", rename_all = "snake_case")]
1304 pub enum FutureOp {
1305 #[cfg(feature = "counter")]
1306 Counter(OwnedValue),
1307 Unknown(OwnedValue),
1308 }
1309
1310 mod serde_impl {
1311
1312 use loro_common::{ContainerID, ContainerType};
1313 use serde::{
1314 de::{Error, IgnoredAny, MapAccess, Visitor},
1315 ser::SerializeStruct,
1316 Deserialize, Deserializer, Serialize, Serializer,
1317 };
1318
1319 #[allow(unused_imports)]
1320 use crate::encoding::OwnedValue;
1321
1322 impl Serialize for super::JsonOp {
1323 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1324 where
1325 S: Serializer,
1326 {
1327 let mut s = serializer.serialize_struct("Op", 3)?;
1328 s.serialize_field("container", &self.container.to_string())?;
1329 s.serialize_field("content", &self.content)?;
1330 s.serialize_field("counter", &self.counter)?;
1331 s.end()
1332 }
1333 }
1334
1335 impl<'de> Deserialize<'de> for super::JsonOp {
1336 fn deserialize<D>(deserializer: D) -> Result<super::JsonOp, D::Error>
1337 where
1338 D: Deserializer<'de>,
1339 {
1340 struct __Visitor;
1341
1342 impl<'de> Visitor<'de> for __Visitor {
1343 type Value = super::JsonOp;
1344 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1345 formatter.write_str("struct Op")
1346 }
1347
1348 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1349 where
1350 A: MapAccess<'de>,
1351 {
1352 let mut container = None;
1353 let mut content = None;
1354 let mut counter = None;
1355
1356 while let Some(key) = map.next_key::<String>()? {
1357 match key.as_str() {
1358 "container" => {
1359 if container.is_some() {
1360 return Err(A::Error::duplicate_field("container"));
1361 }
1362 let value = map.next_value::<String>()?;
1363 container =
1364 Some(ContainerID::try_from(value.as_str()).map_err(
1365 |_| A::Error::custom("invalid container id"),
1366 )?);
1367 }
1368 "content" => {
1369 if content.is_some() {
1370 return Err(A::Error::duplicate_field("content"));
1371 }
1372 content = Some(map.next_value::<serde_json::Value>()?);
1373 }
1374 "counter" => {
1375 if counter.is_some() {
1376 return Err(A::Error::duplicate_field("counter"));
1377 }
1378 counter = Some(map.next_value::<i32>()?);
1379 }
1380 _ => {
1381 let _ = map.next_value::<IgnoredAny>()?;
1382 }
1383 }
1384 }
1385
1386 let container =
1387 container.ok_or_else(|| A::Error::missing_field("container"))?;
1388 let content = content.ok_or_else(|| A::Error::missing_field("content"))?;
1389 let op = json_op_content_from_value(&container, content)?;
1390 let counter = counter.ok_or_else(|| A::Error::missing_field("counter"))?;
1391 Ok(super::JsonOp {
1392 container,
1393 content: op,
1394 counter,
1395 })
1396 }
1397 }
1398 const FIELDS: &[&str] = &["container", "content", "counter"];
1399 deserializer.deserialize_struct("JsonOp", FIELDS, __Visitor)
1400 }
1401 }
1402
1403 fn json_op_content_from_value<E: Error>(
1404 container: &ContainerID,
1405 value: serde_json::Value,
1406 ) -> Result<super::JsonOpContent, E> {
1407 match container.container_type() {
1408 ContainerType::List => serde_json::from_value(value)
1409 .map(super::JsonOpContent::List)
1410 .map_err(E::custom),
1411 ContainerType::MovableList => serde_json::from_value(value)
1412 .map(super::JsonOpContent::MovableList)
1413 .map_err(E::custom),
1414 ContainerType::Map => serde_json::from_value(value)
1415 .map(super::JsonOpContent::Map)
1416 .map_err(E::custom),
1417 ContainerType::Text => serde_json::from_value(value)
1418 .map(super::JsonOpContent::Text)
1419 .map_err(E::custom),
1420 ContainerType::Tree => serde_json::from_value(value)
1421 .map(super::JsonOpContent::Tree)
1422 .map_err(E::custom),
1423 ContainerType::Unknown(_) => serde_json::from_value(value)
1424 .map(super::JsonOpContent::Future)
1425 .map_err(E::custom),
1426 #[cfg(feature = "counter")]
1427 ContainerType::Counter => {
1428 match serde_json::from_value::<super::FutureOpWrapper>(value.clone()) {
1429 Ok(op) => Ok(super::JsonOpContent::Future(op)),
1430 Err(_) => {
1431 let value =
1432 serde_json::from_value::<OwnedValue>(value).map_err(E::custom)?;
1433 Ok(super::JsonOpContent::Future(super::FutureOpWrapper {
1434 prop: 0,
1435 value: super::FutureOp::Counter(value),
1436 }))
1437 }
1438 }
1439 }
1440 }
1441 }
1442
1443 pub mod id {
1444 use loro_common::ID;
1445 use serde::{Deserialize, Deserializer, Serializer};
1446
1447 pub fn serialize<S>(id: &ID, s: S) -> Result<S::Ok, S::Error>
1448 where
1449 S: Serializer,
1450 {
1451 s.serialize_str(&id.to_string())
1452 }
1453
1454 pub fn deserialize<'de, 'a, D>(d: D) -> Result<ID, D::Error>
1455 where
1456 D: Deserializer<'de>,
1457 {
1458 let str: String = Deserialize::deserialize(d)?;
1460 let id: ID = ID::try_from(str.as_str())
1461 .map_err(|_| serde::de::Error::custom("invalid ID format"))?;
1462 Ok(id)
1463 }
1464 }
1465
1466 pub mod frontiers {
1467 use itertools::Itertools;
1468 use loro_common::ID;
1469 use serde::{ser::SerializeMap, Deserializer, Serializer};
1470
1471 use crate::version::Frontiers;
1472
1473 pub fn serialize<S>(f: &Frontiers, s: S) -> Result<S::Ok, S::Error>
1474 where
1475 S: Serializer,
1476 {
1477 let mut map = s.serialize_map(Some(f.len()))?;
1478 for id in f
1479 .iter()
1480 .sorted()
1482 {
1483 map.serialize_entry(&id.peer.to_string(), &id.counter)?;
1484 }
1485 map.end()
1486 }
1487
1488 pub fn deserialize<'de, 'a, D>(d: D) -> Result<Frontiers, D::Error>
1489 where
1490 D: Deserializer<'de>,
1491 {
1492 struct __Visitor;
1493 impl<'de> serde::de::Visitor<'de> for __Visitor {
1494 type Value = Frontiers;
1495 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1496 formatter.write_str("a Frontiers")
1497 }
1498
1499 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1500 where
1501 A: serde::de::MapAccess<'de>,
1502 {
1503 let mut f = Frontiers::default();
1504 while let Some((k, v)) = map.next_entry::<String, i32>()? {
1505 f.push(ID::new(
1506 k.parse().map_err(|_| {
1507 serde::de::Error::custom("invalid peer id in frontiers")
1508 })?,
1509 v,
1510 ))
1511 }
1512 Ok(f)
1513 }
1514 }
1515 d.deserialize_map(__Visitor)
1516 }
1517 }
1518
1519 pub mod deps {
1520 use loro_common::ID;
1521 use serde::{Deserialize, Deserializer, Serializer};
1522
1523 pub fn serialize<S>(deps: &[ID], s: S) -> Result<S::Ok, S::Error>
1524 where
1525 S: Serializer,
1526 {
1527 s.collect_seq(deps.iter().map(|x| x.to_string()))
1528 }
1529
1530 pub fn deserialize<'de, 'a, D>(d: D) -> Result<Vec<ID>, D::Error>
1531 where
1532 D: Deserializer<'de>,
1533 {
1534 let deps: Vec<String> = Deserialize::deserialize(d)?;
1535 deps.into_iter()
1536 .map(|x| {
1537 ID::try_from(x.as_str())
1538 .map_err(|_| serde::de::Error::custom("invalid ID in deps"))
1539 })
1540 .collect::<Result<Vec<_>, _>>()
1541 }
1542 }
1543
1544 pub mod peer_id {
1545 use loro_common::PeerID;
1546 use serde::{Deserialize, Deserializer, Serializer};
1547
1548 pub fn serialize<S>(peers: &Option<Vec<PeerID>>, s: S) -> Result<S::Ok, S::Error>
1549 where
1550 S: Serializer,
1551 {
1552 match peers {
1553 Some(peers) => s.collect_seq(peers.iter().map(|x| x.to_string())),
1554 None => s.serialize_none(),
1555 }
1556 }
1557
1558 pub fn deserialize<'de, 'a, D>(d: D) -> Result<Option<Vec<PeerID>>, D::Error>
1559 where
1560 D: Deserializer<'de>,
1561 {
1562 let peers: Option<Vec<String>> = Deserialize::deserialize(d)?;
1563 peers
1564 .map(|x| {
1565 x.into_iter()
1566 .map(|x| {
1567 x.parse()
1568 .map_err(|_| serde::de::Error::custom("invalid peer id"))
1569 })
1570 .collect::<Result<Vec<_>, _>>()
1571 })
1572 .transpose()
1573 }
1574 }
1575
1576 pub mod idlp {
1577 use loro_common::IdLp;
1578 use serde::{Deserialize, Deserializer, Serializer};
1579
1580 pub fn serialize<S>(idlp: &IdLp, s: S) -> Result<S::Ok, S::Error>
1581 where
1582 S: Serializer,
1583 {
1584 s.serialize_str(&idlp.to_string())
1585 }
1586
1587 pub fn deserialize<'de, 'a, D>(d: D) -> Result<IdLp, D::Error>
1588 where
1589 D: Deserializer<'de>,
1590 {
1591 let str: String = Deserialize::deserialize(d)?;
1592 let id: IdLp = IdLp::try_from(str.as_str())
1593 .map_err(|_| serde::de::Error::custom("invalid IdLp format"))?;
1594 Ok(id)
1595 }
1596 }
1597
1598 pub mod tree_id {
1599 use loro_common::TreeID;
1600 use serde::{Deserialize, Deserializer, Serializer};
1601
1602 pub fn serialize<S>(id: &TreeID, s: S) -> Result<S::Ok, S::Error>
1603 where
1604 S: Serializer,
1605 {
1606 s.serialize_str(&id.to_string())
1607 }
1608
1609 pub fn deserialize<'de, 'a, D>(d: D) -> Result<TreeID, D::Error>
1610 where
1611 D: Deserializer<'de>,
1612 {
1613 let str: String = Deserialize::deserialize(d)?;
1614 let id: TreeID = TreeID::try_from(str.as_str())
1615 .map_err(|_| serde::de::Error::custom("invalid TreeID format"))?;
1616 Ok(id)
1617 }
1618 }
1619
1620 pub mod option_tree_id {
1621 use loro_common::TreeID;
1622 use serde::{Deserialize, Deserializer, Serializer};
1623
1624 pub fn serialize<S>(id: &Option<TreeID>, s: S) -> Result<S::Ok, S::Error>
1625 where
1626 S: Serializer,
1627 {
1628 match id {
1629 Some(id) => s.serialize_str(&id.to_string()),
1630 None => s.serialize_none(),
1631 }
1632 }
1633
1634 pub fn deserialize<'de, 'a, D>(d: D) -> Result<Option<TreeID>, D::Error>
1635 where
1636 D: Deserializer<'de>,
1637 {
1638 let str: Option<String> = Deserialize::deserialize(d)?;
1639 match str {
1640 Some(str) => {
1641 let id: TreeID = TreeID::try_from(str.as_str())
1642 .map_err(|_| serde::de::Error::custom("invalid TreeID format"))?;
1643 Ok(Some(id))
1644 }
1645 None => Ok(None),
1646 }
1647 }
1648 }
1649
1650 pub mod fractional_index {
1651 use fractional_index::FractionalIndex;
1652 use serde::{Deserialize, Deserializer, Serializer};
1653
1654 pub fn serialize<S>(fi: &FractionalIndex, s: S) -> Result<S::Ok, S::Error>
1655 where
1656 S: Serializer,
1657 {
1658 s.serialize_str(&fi.to_string())
1659 }
1660
1661 pub fn deserialize<'de, 'a, D>(d: D) -> Result<FractionalIndex, D::Error>
1662 where
1663 D: Deserializer<'de>,
1664 {
1665 let str: String = Deserialize::deserialize(d)?;
1666 if !str.len().is_multiple_of(2) {
1667 return Err(serde::de::Error::custom(
1668 "invalid fractional index hex length",
1669 ));
1670 }
1671
1672 let mut bytes = Vec::with_capacity(str.len() / 2);
1673 for i in 0..str.len() / 2 {
1674 let byte = u8::from_str_radix(&str[i * 2..i * 2 + 2], 16).map_err(|e| {
1675 serde::de::Error::custom(format!("invalid fractional index hex: {e}"))
1676 })?;
1677 bytes.push(byte);
1678 }
1679
1680 Ok(FractionalIndex::from_bytes(bytes))
1681 }
1682 }
1683 }
1684
1685 #[derive(thiserror::Error, Debug, PartialEq, Eq)]
1686 pub enum RedactError {
1687 #[error("unknown operation type")]
1688 UnknownOperationType,
1689 #[error("invalid schema: {0}")]
1690 InvalidSchema(String),
1691 }
1692
1693 fn invalid_schema(message: impl Into<String>) -> RedactError {
1694 RedactError::InvalidSchema(message.into())
1695 }
1696
1697 fn checked_redact_op_len(op: &JsonOp) -> Result<Counter, RedactError> {
1698 let len = op.content.op_len();
1699 if len == 0 {
1700 return Err(invalid_schema("op length must be greater than zero"));
1701 }
1702
1703 Counter::try_from(len).map_err(|_| invalid_schema("op length is too large"))
1704 }
1705
1706 fn validate_redactable_change(change: &JsonChange) -> Result<usize, RedactError> {
1707 if change.id.counter < 0 {
1708 return Err(invalid_schema("change id counter must be non-negative"));
1709 }
1710
1711 let mut expected_counter = change.id.counter;
1712 for op in &change.ops {
1713 if op.counter < 0 {
1714 return Err(invalid_schema("op counter must be non-negative"));
1715 }
1716 if op.counter != expected_counter {
1717 return Err(invalid_schema(
1718 "op counters must be contiguous with the change id",
1719 ));
1720 }
1721
1722 let len = checked_redact_op_len(op)?;
1723 expected_counter = expected_counter
1724 .checked_add(len)
1725 .ok_or_else(|| invalid_schema("op counter overflow"))?;
1726 }
1727
1728 let len = expected_counter
1729 .checked_sub(change.id.counter)
1730 .ok_or_else(|| invalid_schema("change counter overflow"))?;
1731 usize::try_from(len).map_err(|_| invalid_schema("change length is too large"))
1732 }
1733
1734 pub fn redact(json: &mut JsonSchema, range: VersionRange) -> Result<(), RedactError> {
1751 let peers = json.peers.clone();
1752 let mut errors = Vec::new();
1753 for change in json.changes.iter_mut() {
1754 let real_peer = get_peer_from_peers(&peers, change.id.peer)
1755 .map_err(|_| RedactError::InvalidSchema("peer index out of range".to_string()))?;
1756 let real_id = ID::new(real_peer, change.id.counter);
1757 let change_len = validate_redactable_change(change)?;
1758 if !range.has_overlap_with(real_id.to_span(change_len)) {
1759 continue;
1760 }
1761
1762 let redact_range = range.get(&real_peer).copied().unwrap();
1763 for op in change.ops.iter_mut() {
1764 if op.counter >= redact_range.1 {
1765 break;
1766 }
1767
1768 let len = checked_redact_op_len(op)?;
1769 let op_end = op
1770 .counter
1771 .checked_add(len)
1772 .ok_or_else(|| invalid_schema("op counter overflow"))?;
1773 if op_end <= redact_range.0 {
1774 continue;
1775 }
1776
1777 let result = redact_op(
1778 &mut op.content,
1779 redact_range.0.saturating_sub(op.counter).max(0).min(len)
1780 ..redact_range.1.saturating_sub(op.counter).max(0).min(len),
1781 );
1782 match result {
1783 Ok(()) => {}
1784 Err(e) => errors.push(e),
1785 }
1786 }
1787 }
1788
1789 if errors.is_empty() {
1790 Ok(())
1791 } else {
1792 Err(errors.pop().unwrap())
1793 }
1794 }
1795
1796 fn redact_op(op: &mut JsonOpContent, range: Range<Counter>) -> Result<(), RedactError> {
1797 match op {
1798 JsonOpContent::List(list_op) => {
1799 match list_op {
1800 ListOp::Insert { value: values, .. } => {
1801 for i in range {
1802 redact_value(&mut values[i as usize]);
1803 }
1804 }
1805 ListOp::Delete { .. } => {
1806 }
1808 }
1809 }
1810 JsonOpContent::MovableList(movable_list_op) => {
1811 match movable_list_op {
1812 MovableListOp::Insert { value: values, .. } => {
1813 for i in range {
1814 redact_value(&mut values[i as usize]);
1815 }
1816 }
1817 MovableListOp::Delete { .. } | MovableListOp::Move { .. } => {
1818 }
1820 MovableListOp::Set { value, .. } => {
1821 assert!(range.start == 0 && range.len() == 1);
1822 redact_value(value);
1823 }
1824 }
1825 }
1826 JsonOpContent::Map(map_op) => {
1827 match map_op {
1828 MapOp::Insert { value, .. } => {
1829 assert!(range.start == 0 && range.len() == 1);
1830 redact_value(value);
1831 }
1832 MapOp::Delete { .. } => {
1833 }
1835 }
1836 }
1837 JsonOpContent::Text(text_op) => {
1838 match text_op {
1839 TextOp::Insert { text, .. } => {
1840 let mut chars = vec![];
1841 for (i, c) in text.chars().enumerate() {
1842 if i < range.start as usize || i >= range.end as usize {
1843 chars.push(c);
1844 } else {
1845 chars.push("� ".chars().next().unwrap());
1846 }
1847 }
1848 *text = chars.into_iter().collect();
1849 }
1850 TextOp::Delete { .. } => {
1851 }
1853 TextOp::Mark { style_value, .. } => {
1854 assert!(range.start == 0 && range.len() == 1);
1855 *style_value = LoroValue::Null;
1856 }
1857 TextOp::MarkEnd => {
1858 }
1860 }
1861 }
1862 JsonOpContent::Tree(..) => {
1863 }
1865 JsonOpContent::Future(future_op_wrapper) => match &mut future_op_wrapper.value {
1866 #[cfg(feature = "counter")]
1867 FutureOp::Counter(owned_value) => {
1868 *owned_value = OwnedValue::I64(0);
1869 }
1870 FutureOp::Unknown(..) => {
1871 return Err(RedactError::UnknownOperationType);
1872 }
1873 },
1874 }
1875
1876 Ok(())
1877 }
1878}
1879
1880fn redact_value(v: &mut LoroValue) {
1881 match v {
1882 LoroValue::Container(_) => {}
1883 _ => *v = LoroValue::Null,
1884 }
1885}
1886
1887#[cfg(test)]
1888mod tests {
1889 use crate::{LoroDoc, VersionVector};
1890
1891 #[test]
1892 fn json_range_version() {
1893 let doc = LoroDoc::new_auto_commit();
1894 doc.set_peer_id(0).unwrap();
1895 let list = doc.get_list("list");
1896 list.insert(0, "a").unwrap();
1897 list.insert(0, "b").unwrap();
1898 list.insert(0, "c").unwrap();
1899 let json = doc.export_json_updates(
1900 &VersionVector::from_iter(vec![(0, 1)]),
1901 &VersionVector::from_iter(vec![(0, 2)]),
1902 true,
1903 );
1904 assert_eq!(json.changes[0].ops.len(), 1);
1905 let json = doc.export_json_updates(
1906 &VersionVector::from_iter(vec![(0, 0)]),
1907 &VersionVector::from_iter(vec![(0, 2)]),
1908 true,
1909 );
1910 assert_eq!(json.changes[0].ops.len(), 2);
1911 }
1912}