loro_internal/encoding/
json_schema.rs

1use super::{
2    outdated_encode_reordered::{import_changes_to_oplog, ImportChangesResult, ValueRegister},
3    ImportStatus,
4};
5use crate::{
6    arena::SharedArena,
7    change::{Change, ChangeRef},
8    container::{
9        list::list_op::{DeleteSpan, DeleteSpanWithId, InnerListOp},
10        map::MapSet,
11        richtext::TextStyleInfoFlag,
12        tree::tree_op::TreeOp,
13    },
14    op::{FutureInnerContent, InnerContent, Op, SliceRange},
15    oplog::BlockChangeRef,
16    version::{Frontiers, VersionRange},
17    OpLog, VersionVector,
18};
19use either::Either;
20use json::{JsonChange, JsonOpContent, JsonSchema};
21use loro_common::{
22    ContainerID, ContainerType, HasCounterSpan, HasId, HasIdSpan, IdLp, IdSpan, LoroError,
23    LoroResult, LoroValue, PeerID, TreeID, ID,
24};
25use rle::{HasLength, RleVec, Sliceable};
26use std::sync::Arc;
27
28const SCHEMA_VERSION: u8 = 1;
29
30fn refine_vv(vv: &VersionVector, oplog: &OpLog) -> VersionVector {
31    let mut refined = VersionVector::new();
32    for (&peer, &counter) in vv.iter() {
33        if counter == 0 {
34            continue;
35        }
36        let end = oplog.vv().get(&peer).copied().unwrap_or(0);
37        if end <= counter {
38            refined.insert(peer, end);
39        } else {
40            refined.insert(peer, counter);
41        }
42    }
43    refined
44}
45
46pub(crate) fn export_json<'a, 'c: 'a>(
47    oplog: &'c OpLog,
48    start_vv: &VersionVector,
49    end_vv: &VersionVector,
50    with_peer_compression: bool,
51) -> JsonSchema {
52    let actual_start_vv = refine_vv(start_vv, oplog);
53    let actual_end_vv = refine_vv(end_vv, oplog);
54
55    let frontiers = oplog.dag.vv_to_frontiers(&actual_start_vv);
56
57    let diff_changes = init_encode(oplog, &actual_start_vv, &actual_end_vv);
58    if with_peer_compression {
59        let mut peer_register = ValueRegister::<PeerID>::new();
60        let changes = encode_changes(&diff_changes, &oplog.arena, Some(&mut peer_register));
61        JsonSchema {
62            changes,
63            schema_version: SCHEMA_VERSION,
64            peers: Some(peer_register.unwrap_vec()),
65            start_version: frontiers,
66        }
67    } else {
68        let changes = encode_changes(&diff_changes, &oplog.arena, None);
69        JsonSchema {
70            changes,
71            schema_version: SCHEMA_VERSION,
72            peers: None,
73            start_version: frontiers,
74        }
75    }
76}
77
78pub(crate) fn export_json_in_id_span(oplog: &OpLog, mut id_span: IdSpan) -> Vec<json::JsonChange> {
79    id_span.normalize_();
80    let end = oplog.vv().get(&id_span.peer).copied().unwrap_or(0);
81    if id_span.counter.start >= end {
82        return vec![];
83    }
84
85    id_span.counter.end = id_span.counter.end.min(end);
86    let mut diff_changes: Vec<Either<BlockChangeRef, Change>> = Vec::new();
87    while id_span.counter.end - id_span.counter.start > 0 {
88        let change: BlockChangeRef = oplog.get_change_at(id_span.id_start()).unwrap();
89        let ctr_end = change.ctr_end();
90        if change.id.counter >= id_span.counter.start && change.ctr_end() <= id_span.counter.end {
91            diff_changes.push(Either::Left(change));
92        } else {
93            let start = if change.id.counter < id_span.counter.start {
94                (id_span.counter.start - change.id.counter) as usize
95            } else {
96                0
97            };
98
99            let end = if change.ctr_end() > id_span.counter.end {
100                (id_span.counter.end - change.id.counter) as usize
101            } else {
102                change.atom_len()
103            };
104
105            diff_changes.push(Either::Right(change.slice(start, end)));
106        }
107
108        id_span.counter.start = ctr_end;
109    }
110
111    encode_changes(&diff_changes, &oplog.arena, None)
112}
113
114pub(crate) fn import_json(oplog: &mut OpLog, json: JsonSchema) -> LoroResult<ImportStatus> {
115    let changes = decode_changes(json, &oplog.arena)?;
116    let ImportChangesResult {
117        latest_ids,
118        pending_changes,
119        changes_that_have_deps_before_shallow_root,
120        mut imported,
121    } = import_changes_to_oplog(changes, oplog);
122    let mut pending = VersionRange::default();
123    pending_changes.iter().for_each(|c| {
124        pending.extends_to_include_id_span(c.id_span());
125    });
126    oplog.try_apply_pending(latest_ids, Some(&mut imported));
127    oplog.import_unknown_lamport_pending_changes(pending_changes)?;
128    if !changes_that_have_deps_before_shallow_root.is_empty() {
129        return Err(LoroError::ImportUpdatesThatDependsOnOutdatedVersion);
130    };
131    Ok(ImportStatus {
132        success: imported,
133        pending: if pending.is_empty() {
134            None
135        } else {
136            Some(pending)
137        },
138    })
139}
140
141fn init_encode<'s, 'a: 's>(
142    oplog: &'a OpLog,
143    start_vv: &VersionVector,
144    end_vv: &VersionVector,
145) -> Vec<Either<BlockChangeRef, Change>> {
146    let mut diff_changes: Vec<Either<BlockChangeRef, Change>> = Vec::new();
147    for change in oplog.iter_changes_peer_by_peer(start_vv, end_vv) {
148        let start_cnt = start_vv.get(&change.id.peer).copied().unwrap_or(0);
149        let end_cnt = end_vv.get(&change.id.peer).copied().unwrap_or(0);
150        if change.id.counter < start_cnt {
151            if change.ctr_end() <= start_cnt {
152                continue;
153            }
154
155            let offset = start_cnt - change.id.counter;
156            let to = change
157                .atom_len()
158                .min((end_cnt - change.id.counter) as usize);
159            diff_changes.push(Either::Right(change.slice(offset as usize, to)));
160        } else if change.id.counter + change.atom_len() as i32 > end_cnt {
161            let len = end_cnt - change.id.counter;
162            diff_changes.push(Either::Right(change.slice(0, len as usize)));
163        } else {
164            diff_changes.push(Either::Left(change));
165        }
166    }
167    diff_changes.sort_by_key(|x| match x {
168        Either::Left(c) => c.lamport,
169        Either::Right(c) => c.lamport,
170    });
171    diff_changes
172}
173
174fn register_id(id: &ID, peer_register: Option<&mut ValueRegister<PeerID>>) -> ID {
175    let peer = match peer_register {
176        Some(peer_register) => peer_register.register(&id.peer) as PeerID,
177        None => id.peer,
178    };
179    ID::new(peer as PeerID, id.counter)
180}
181
182fn register_idlp(idlp: &IdLp, peer_register: Option<&mut ValueRegister<PeerID>>) -> IdLp {
183    let peer = match peer_register {
184        Some(peer_register) => peer_register.register(&idlp.peer) as PeerID,
185        None => idlp.peer,
186    };
187    IdLp {
188        peer,
189        lamport: idlp.lamport,
190    }
191}
192
193fn register_tree_id(tree: &TreeID, peer_register: Option<&mut ValueRegister<PeerID>>) -> TreeID {
194    TreeID {
195        peer: match peer_register {
196            Some(peer_register) => peer_register.register(&tree.peer) as PeerID,
197            None => tree.peer,
198        },
199        counter: tree.counter,
200    }
201}
202
203fn register_container_id(
204    container: ContainerID,
205    peer_register: Option<&mut ValueRegister<PeerID>>,
206) -> ContainerID {
207    match container {
208        ContainerID::Normal {
209            peer,
210            counter,
211            container_type,
212        } => ContainerID::Normal {
213            peer: match peer_register {
214                Some(peer_register) => peer_register.register(&peer) as PeerID,
215                None => peer,
216            },
217            counter,
218            container_type,
219        },
220        r => r,
221    }
222}
223
224fn convert_container_id(container: ContainerID, peers: &Option<Vec<PeerID>>) -> ContainerID {
225    match container {
226        ContainerID::Normal {
227            peer,
228            counter,
229            container_type,
230        } => ContainerID::Normal {
231            peer: get_peer_from_peers(peers, peer),
232            counter,
233            container_type,
234        },
235        r => r,
236    }
237}
238
239pub(crate) fn get_peer_from_peers(peers: &Option<Vec<PeerID>>, peer: PeerID) -> PeerID {
240    match peers {
241        Some(peers) => peers[peer as usize],
242        None => peer,
243    }
244}
245
246fn convert_id(id: &ID, peers: &Option<Vec<PeerID>>) -> ID {
247    ID {
248        peer: get_peer_from_peers(peers, id.peer),
249        counter: id.counter,
250    }
251}
252
253fn convert_idlp(idlp: &IdLp, peers: &Option<Vec<PeerID>>) -> IdLp {
254    IdLp {
255        lamport: idlp.lamport,
256        peer: get_peer_from_peers(peers, idlp.peer),
257    }
258}
259
260fn convert_tree_id(tree: &TreeID, peers: &Option<Vec<PeerID>>) -> TreeID {
261    TreeID {
262        peer: get_peer_from_peers(peers, tree.peer),
263        counter: tree.counter,
264    }
265}
266pub(crate) fn encode_change_to_json(change: ChangeRef<'_>, arena: &SharedArena) -> JsonSchema {
267    let f = change.deps.clone();
268    let changes = vec![encode_change(change, arena, None)];
269    JsonSchema {
270        changes,
271        schema_version: SCHEMA_VERSION,
272        peers: None,
273        start_version: f,
274    }
275}
276
277fn encode_changes(
278    diff_changes: &[Either<BlockChangeRef, Change>],
279    arena: &SharedArena,
280    mut peer_register: Option<&mut ValueRegister<PeerID>>,
281) -> Vec<json::JsonChange> {
282    let mut changes = Vec::with_capacity(diff_changes.len());
283    for change in diff_changes.iter() {
284        let change: &Change = match change {
285            Either::Left(c) => c,
286            Either::Right(c) => c,
287        };
288        let c = encode_change(
289            ChangeRef::from_change(change),
290            arena,
291            peer_register.as_deref_mut(),
292        );
293        changes.push(c);
294    }
295    changes
296}
297
298fn encode_change(
299    change: ChangeRef<'_, Op>,
300    arena: &SharedArena,
301    mut peer_register: Option<&mut ValueRegister<PeerID>>,
302) -> JsonChange {
303    let mut ops = Vec::with_capacity(change.ops.len());
304    for Op {
305        counter,
306        container,
307        content,
308    } in change.ops.iter()
309    {
310        let mut container = arena.get_container_id(*container).unwrap();
311        if container.is_normal() {
312            container = register_container_id(container, peer_register.as_deref_mut());
313        }
314        let op = match container.container_type() {
315            ContainerType::List => match content {
316                InnerContent::List(list) => JsonOpContent::List(match list {
317                    InnerListOp::Insert { slice, pos } => {
318                        let mut values =
319                            arena.get_values(slice.0.start as usize..slice.0.end as usize);
320                        values.iter_mut().for_each(|x| {
321                            if let LoroValue::Container(id) = x {
322                                if id.is_normal() {
323                                    *id = register_container_id(
324                                        id.clone(),
325                                        peer_register.as_deref_mut(),
326                                    );
327                                }
328                            }
329                        });
330                        json::ListOp::Insert {
331                            pos: *pos as u32,
332                            value: values,
333                        }
334                    }
335                    InnerListOp::Delete(DeleteSpanWithId {
336                        id_start,
337                        span: DeleteSpan { pos, signed_len },
338                    }) => json::ListOp::Delete {
339                        pos: *pos as i32,
340                        len: *signed_len as i32,
341                        start_id: register_id(id_start, peer_register.as_deref_mut()),
342                    },
343                    _ => unreachable!(),
344                }),
345                _ => unreachable!(),
346            },
347            ContainerType::MovableList => match content {
348                InnerContent::List(list) => JsonOpContent::MovableList(match list {
349                    InnerListOp::Insert { slice, pos } => {
350                        let mut values =
351                            arena.get_values(slice.0.start as usize..slice.0.end as usize);
352                        values.iter_mut().for_each(|x| {
353                            if let LoroValue::Container(id) = x {
354                                if id.is_normal() {
355                                    *id = register_container_id(
356                                        id.clone(),
357                                        peer_register.as_deref_mut(),
358                                    );
359                                }
360                            }
361                        });
362                        json::MovableListOp::Insert {
363                            pos: *pos as u32,
364                            value: values,
365                        }
366                    }
367                    InnerListOp::Delete(DeleteSpanWithId {
368                        id_start,
369                        span: DeleteSpan { pos, signed_len },
370                    }) => json::MovableListOp::Delete {
371                        pos: *pos as i32,
372                        len: *signed_len as i32,
373                        start_id: register_id(id_start, peer_register.as_deref_mut()),
374                    },
375                    InnerListOp::Move {
376                        from,
377                        elem_id: from_id,
378                        to,
379                    } => json::MovableListOp::Move {
380                        from: *from,
381                        to: *to,
382                        elem_id: register_idlp(from_id, peer_register.as_deref_mut()),
383                    },
384                    InnerListOp::Set { elem_id, value } => {
385                        let value = if let LoroValue::Container(id) = value {
386                            if id.is_normal() {
387                                LoroValue::Container(register_container_id(
388                                    id.clone(),
389                                    peer_register.as_deref_mut(),
390                                ))
391                            } else {
392                                value.clone()
393                            }
394                        } else {
395                            value.clone()
396                        };
397                        json::MovableListOp::Set {
398                            elem_id: register_idlp(elem_id, peer_register.as_deref_mut()),
399                            value,
400                        }
401                    }
402                    _ => unreachable!(),
403                }),
404                _ => unreachable!(),
405            },
406            ContainerType::Text => match content {
407                InnerContent::List(list) => JsonOpContent::Text(match list {
408                    InnerListOp::InsertText {
409                        slice,
410                        unicode_start: _,
411                        unicode_len: _,
412                        pos,
413                    } => {
414                        let text = String::from_utf8(slice.as_bytes().to_vec()).unwrap();
415                        json::TextOp::Insert { pos: *pos, text }
416                    }
417                    InnerListOp::Delete(DeleteSpanWithId {
418                        id_start,
419                        span: DeleteSpan { pos, signed_len },
420                    }) => json::TextOp::Delete {
421                        pos: *pos as i32,
422                        len: *signed_len as i32,
423                        start_id: register_id(id_start, peer_register.as_deref_mut()),
424                    },
425                    InnerListOp::StyleStart {
426                        start,
427                        end,
428                        key,
429                        value,
430                        info,
431                    } => json::TextOp::Mark {
432                        start: *start,
433                        end: *end,
434                        style_key: key.to_string(),
435                        style_value: value.clone(),
436                        info: info.to_byte(),
437                    },
438                    InnerListOp::StyleEnd => json::TextOp::MarkEnd,
439                    _ => unreachable!(),
440                }),
441                _ => unreachable!(),
442            },
443            ContainerType::Map => match content {
444                InnerContent::Map(MapSet { key, value }) => {
445                    JsonOpContent::Map(if let Some(v) = value {
446                        let value = if let LoroValue::Container(id) = v {
447                            if id.is_normal() {
448                                LoroValue::Container(register_container_id(
449                                    id.clone(),
450                                    peer_register.as_deref_mut(),
451                                ))
452                            } else {
453                                v.clone()
454                            }
455                        } else {
456                            v.clone()
457                        };
458                        json::MapOp::Insert {
459                            key: key.to_string(),
460                            value,
461                        }
462                    } else {
463                        json::MapOp::Delete {
464                            key: key.to_string(),
465                        }
466                    })
467                }
468
469                _ => unreachable!(),
470            },
471
472            ContainerType::Tree => match content {
473                InnerContent::Tree(op) => JsonOpContent::Tree(match &**op {
474                    TreeOp::Create {
475                        target,
476                        parent,
477                        position,
478                    } => json::TreeOp::Create {
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::Move {
484                        target,
485                        parent,
486                        position,
487                    } => json::TreeOp::Move {
488                        target: register_tree_id(target, peer_register.as_deref_mut()),
489                        parent: parent.map(|p| register_tree_id(&p, peer_register.as_deref_mut())),
490                        fractional_index: position.clone(),
491                    },
492                    TreeOp::Delete { target } => json::TreeOp::Delete {
493                        target: register_tree_id(target, peer_register.as_deref_mut()),
494                    },
495                }),
496                _ => unreachable!(),
497            },
498            ContainerType::Unknown(_) => {
499                let InnerContent::Future(FutureInnerContent::Unknown { prop, value }) = content
500                else {
501                    unreachable!();
502                };
503                JsonOpContent::Future(json::FutureOpWrapper {
504                    prop: *prop,
505                    value: json::FutureOp::Unknown((**value).clone()),
506                })
507            }
508            #[cfg(feature = "counter")]
509            ContainerType::Counter => {
510                let InnerContent::Future(f) = content else {
511                    unreachable!()
512                };
513                match f {
514                    FutureInnerContent::Counter(x) => {
515                        JsonOpContent::Future(json::FutureOpWrapper {
516                            prop: 0,
517                            value: json::FutureOp::Counter(super::OwnedValue::F64(*x)),
518                        })
519                    }
520                    _ => unreachable!(),
521                }
522            }
523        };
524        ops.push(json::JsonOp {
525            counter: *counter,
526            container,
527            content: op,
528        });
529    }
530    let c = json::JsonChange {
531        id: register_id(change.id, peer_register.as_deref_mut()),
532        ops,
533        deps: change
534            .deps
535            .iter()
536            .map(|id| register_id(&id, peer_register.as_deref_mut()))
537            .collect(),
538        lamport: *change.lamport,
539        timestamp: *change.timestamp,
540        msg: change.commit_msg.as_deref().map(|x| x.to_string()),
541    };
542    c
543}
544
545fn decode_changes(json: JsonSchema, arena: &SharedArena) -> LoroResult<Vec<Change>> {
546    let JsonSchema { peers, changes, .. } = json;
547    let mut ans = Vec::with_capacity(changes.len());
548    for json::JsonChange {
549        id,
550        timestamp,
551        deps,
552        lamport,
553        msg,
554        ops: json_ops,
555    } in changes
556    {
557        let id = convert_id(&id, &peers);
558        let mut ops: RleVec<[Op; 1]> = RleVec::new();
559        for op in json_ops {
560            ops.push(decode_op(op, arena, &peers)?);
561        }
562
563        let change = Change {
564            id,
565            timestamp,
566            deps: Frontiers::from_iter(deps.into_iter().map(|id| convert_id(&id, &peers))),
567            lamport,
568            ops,
569            commit_msg: msg.map(|x| x.into()),
570        };
571        ans.push(change);
572    }
573    Ok(ans)
574}
575
576fn decode_op(op: json::JsonOp, arena: &SharedArena, peers: &Option<Vec<PeerID>>) -> LoroResult<Op> {
577    let json::JsonOp {
578        counter,
579        container,
580        content,
581    } = op;
582    let container = convert_container_id(container, peers);
583    let idx = arena.register_container(&container);
584    let content = match container.container_type() {
585        ContainerType::Text => match content {
586            JsonOpContent::Text(text) => match text {
587                json::TextOp::Insert { pos, text } => {
588                    let (slice, result) = arena.alloc_str_with_slice(&text);
589                    InnerContent::List(InnerListOp::InsertText {
590                        slice,
591                        unicode_start: result.start as u32,
592                        unicode_len: (result.end - result.start) as u32,
593                        pos,
594                    })
595                }
596                json::TextOp::Delete {
597                    pos,
598                    len,
599                    start_id: id_start,
600                } => {
601                    let id_start = convert_id(&id_start, peers);
602                    InnerContent::List(InnerListOp::Delete(DeleteSpanWithId {
603                        id_start,
604                        span: DeleteSpan {
605                            pos: pos as isize,
606                            signed_len: len as isize,
607                        },
608                    }))
609                }
610                json::TextOp::Mark {
611                    start,
612                    end,
613                    style_key,
614                    style_value,
615                    info,
616                } => InnerContent::List(InnerListOp::StyleStart {
617                    start,
618                    end,
619                    key: style_key.into(),
620                    value: style_value,
621                    info: TextStyleInfoFlag::from_byte(info),
622                }),
623                json::TextOp::MarkEnd => InnerContent::List(InnerListOp::StyleEnd),
624            },
625            _ => unreachable!(),
626        },
627        ContainerType::List => match content {
628            JsonOpContent::List(list) => match list {
629                json::ListOp::Insert {
630                    pos,
631                    value: mut values,
632                } => {
633                    values.iter_mut().for_each(|v| {
634                        if let LoroValue::Container(id) = v {
635                            if id.is_normal() {
636                                *id = convert_container_id(id.clone(), peers);
637                            }
638                        }
639                    });
640                    let range = arena.alloc_values(values.iter().cloned());
641                    InnerContent::List(InnerListOp::Insert {
642                        slice: SliceRange::new(range.start as u32..range.end as u32),
643                        pos: pos as usize,
644                    })
645                }
646                json::ListOp::Delete { pos, len, start_id } => {
647                    InnerContent::List(InnerListOp::Delete(DeleteSpanWithId {
648                        id_start: convert_id(&start_id, peers),
649                        span: DeleteSpan {
650                            pos: pos as isize,
651                            signed_len: len as isize,
652                        },
653                    }))
654                }
655            },
656            _ => unreachable!(),
657        },
658        ContainerType::MovableList => match content {
659            JsonOpContent::MovableList(list) => match list {
660                json::MovableListOp::Insert {
661                    pos,
662                    value: mut values,
663                } => {
664                    values.iter_mut().for_each(|v| {
665                        if let LoroValue::Container(id) = v {
666                            if id.is_normal() {
667                                *id = convert_container_id(id.clone(), peers);
668                            }
669                        }
670                    });
671                    let range = arena.alloc_values(values.iter().cloned());
672                    InnerContent::List(InnerListOp::Insert {
673                        slice: SliceRange::new(range.start as u32..range.end as u32),
674                        pos: pos as usize,
675                    })
676                }
677                json::MovableListOp::Delete { pos, len, start_id } => {
678                    InnerContent::List(InnerListOp::Delete(DeleteSpanWithId {
679                        id_start: convert_id(&start_id, peers),
680                        span: DeleteSpan {
681                            pos: pos as isize,
682                            signed_len: len as isize,
683                        },
684                    }))
685                }
686                json::MovableListOp::Move {
687                    from,
688                    elem_id: from_id,
689                    to,
690                } => {
691                    let from_id = convert_idlp(&from_id, peers);
692                    InnerContent::List(InnerListOp::Move {
693                        from,
694                        elem_id: from_id,
695                        to,
696                    })
697                }
698                json::MovableListOp::Set { elem_id, mut value } => {
699                    let elem_id = convert_idlp(&elem_id, peers);
700                    if let LoroValue::Container(id) = &mut value {
701                        *id = convert_container_id(id.clone(), peers);
702                    }
703                    InnerContent::List(InnerListOp::Set { elem_id, value })
704                }
705            },
706            _ => unreachable!(),
707        },
708        ContainerType::Map => match content {
709            JsonOpContent::Map(map) => match map {
710                json::MapOp::Insert { key, mut value } => {
711                    if let LoroValue::Container(id) = &mut value {
712                        *id = convert_container_id(id.clone(), peers);
713                    }
714                    InnerContent::Map(MapSet {
715                        key: key.into(),
716                        value: Some(value),
717                    })
718                }
719                json::MapOp::Delete { key } => InnerContent::Map(MapSet {
720                    key: key.into(),
721                    value: None,
722                }),
723            },
724            _ => unreachable!(),
725        },
726        ContainerType::Tree => match content {
727            JsonOpContent::Tree(tree) => match tree {
728                json::TreeOp::Create {
729                    target,
730                    parent,
731                    fractional_index,
732                } => InnerContent::Tree(Arc::new(TreeOp::Create {
733                    target: convert_tree_id(&target, peers),
734                    parent: parent.map(|p| convert_tree_id(&p, peers)),
735                    position: fractional_index,
736                })),
737                json::TreeOp::Move {
738                    target,
739                    parent,
740                    fractional_index,
741                } => InnerContent::Tree(Arc::new(TreeOp::Move {
742                    target: convert_tree_id(&target, peers),
743                    parent: parent.map(|p| convert_tree_id(&p, peers)),
744                    position: fractional_index,
745                })),
746                json::TreeOp::Delete { target } => InnerContent::Tree(Arc::new(TreeOp::Delete {
747                    target: convert_tree_id(&target, peers),
748                })),
749            },
750            _ => unreachable!(),
751        },
752        ContainerType::Unknown(_) => match content {
753            JsonOpContent::Future(json::FutureOpWrapper {
754                prop,
755                value: json::FutureOp::Unknown(value),
756            }) => InnerContent::Future(FutureInnerContent::Unknown {
757                prop,
758                value: Box::new(value),
759            }),
760            _ => unreachable!(),
761        },
762        #[cfg(feature = "counter")]
763        ContainerType::Counter => {
764            let JsonOpContent::Future(json::FutureOpWrapper { prop: _, value }) = content else {
765                unreachable!()
766            };
767            use crate::encoding::OwnedValue;
768            match value {
769                json::FutureOp::Counter(OwnedValue::F64(c))
770                | json::FutureOp::Unknown(OwnedValue::F64(c)) => {
771                    InnerContent::Future(FutureInnerContent::Counter(c))
772                }
773                json::FutureOp::Counter(OwnedValue::I64(c))
774                | json::FutureOp::Unknown(OwnedValue::I64(c)) => {
775                    InnerContent::Future(FutureInnerContent::Counter(c as f64))
776                }
777                _ => unreachable!(),
778            }
779        } // Note: The Future Type need try to parse Op from the unknown content
780    };
781    Ok(Op {
782        counter,
783        container: idx,
784        content,
785    })
786}
787
788impl TryFrom<&str> for JsonSchema {
789    type Error = serde_json::Error;
790
791    fn try_from(value: &str) -> Result<Self, Self::Error> {
792        serde_json::from_str(value)
793    }
794}
795
796impl TryFrom<&String> for JsonSchema {
797    type Error = serde_json::Error;
798
799    fn try_from(value: &String) -> Result<Self, Self::Error> {
800        serde_json::from_str(value)
801    }
802}
803
804impl TryFrom<String> for JsonSchema {
805    type Error = serde_json::Error;
806
807    fn try_from(value: String) -> Result<Self, Self::Error> {
808        serde_json::from_str(&value)
809    }
810}
811
812pub mod json {
813    use crate::{
814        encoding::OwnedValue,
815        version::{Frontiers, VersionRange},
816    };
817    use fractional_index::FractionalIndex;
818    use loro_common::{ContainerID, Counter, IdLp, Lamport, LoroValue, PeerID, TreeID, ID};
819    use serde::{Deserialize, Serialize};
820    use std::ops::Range;
821
822    use super::{get_peer_from_peers, redact_value};
823
824    #[derive(Debug, Clone, Serialize, Deserialize)]
825    pub struct JsonSchema {
826        pub schema_version: u8,
827        #[serde(with = "self::serde_impl::frontiers")]
828        pub start_version: Frontiers,
829        #[serde(with = "self::serde_impl::peer_id")]
830        pub peers: Option<Vec<PeerID>>,
831        pub changes: Vec<JsonChange>,
832    }
833
834    #[derive(Debug, Clone, Serialize, Deserialize)]
835    pub struct JsonChange {
836        #[serde(with = "self::serde_impl::id")]
837        pub id: ID,
838        pub timestamp: i64,
839        #[serde(with = "self::serde_impl::deps")]
840        pub deps: Vec<ID>,
841        pub lamport: Lamport,
842        pub msg: Option<String>,
843        pub ops: Vec<JsonOp>,
844    }
845
846    impl JsonChange {
847        pub fn op_len(&self) -> usize {
848            let last_op = self.ops.last().unwrap();
849            (last_op.counter - self.id.counter) as usize + last_op.content.op_len()
850        }
851    }
852
853    #[derive(Debug, Clone)]
854    pub struct JsonOp {
855        pub content: JsonOpContent,
856        pub container: ContainerID,
857        pub counter: i32,
858    }
859
860    #[derive(Debug, Clone, Serialize, Deserialize)]
861    #[serde(untagged)]
862    pub enum JsonOpContent {
863        List(ListOp),
864        MovableList(MovableListOp),
865        Map(MapOp),
866        Text(TextOp),
867        Tree(TreeOp),
868        // #[serde(with = "self::serde_impl::future_op")]
869        Future(FutureOpWrapper),
870    }
871
872    impl JsonOpContent {
873        pub fn op_len(&self) -> usize {
874            match self {
875                JsonOpContent::List(list_op) => list_op.op_len(),
876                JsonOpContent::MovableList(movable_list_op) => movable_list_op.op_len(),
877                JsonOpContent::Map(..) => 1,
878                JsonOpContent::Text(text_op) => text_op.op_len(),
879                JsonOpContent::Tree(..) => 1,
880                JsonOpContent::Future(..) => 1,
881            }
882        }
883    }
884
885    #[derive(Debug, Clone, Serialize, Deserialize)]
886    pub struct FutureOpWrapper {
887        #[serde(flatten)]
888        pub value: FutureOp,
889        pub prop: i32,
890    }
891
892    #[derive(Debug, Clone, Serialize, Deserialize)]
893    #[serde(tag = "type", rename_all = "snake_case")]
894    pub enum ListOp {
895        Insert {
896            pos: u32,
897            value: Vec<LoroValue>,
898        },
899        Delete {
900            pos: i32,
901            len: i32,
902            #[serde(with = "self::serde_impl::id")]
903            start_id: ID,
904        },
905    }
906
907    impl ListOp {
908        fn op_len(&self) -> usize {
909            match self {
910                ListOp::Insert { value: values, .. } => values.len(),
911                ListOp::Delete { len, .. } => (*len).unsigned_abs() as usize,
912            }
913        }
914    }
915
916    #[derive(Debug, Clone, Serialize, Deserialize)]
917    #[serde(tag = "type", rename_all = "snake_case")]
918    pub enum MovableListOp {
919        Insert {
920            pos: u32,
921            value: Vec<LoroValue>,
922        },
923        Delete {
924            pos: i32,
925            len: i32,
926            #[serde(with = "self::serde_impl::id")]
927            start_id: ID,
928        },
929        Move {
930            from: u32,
931            to: u32,
932            #[serde(with = "self::serde_impl::idlp")]
933            elem_id: IdLp,
934        },
935        Set {
936            #[serde(with = "self::serde_impl::idlp")]
937            elem_id: IdLp,
938            value: LoroValue,
939        },
940    }
941
942    impl MovableListOp {
943        fn op_len(&self) -> usize {
944            match self {
945                MovableListOp::Insert { value: values, .. } => values.len(),
946                MovableListOp::Delete { len, .. } => (*len).unsigned_abs() as usize,
947                MovableListOp::Move { .. } => 1,
948                MovableListOp::Set { .. } => 1,
949            }
950        }
951    }
952
953    #[derive(Debug, Clone, Serialize, Deserialize)]
954    #[serde(tag = "type", rename_all = "snake_case")]
955    pub enum MapOp {
956        Insert { key: String, value: LoroValue },
957        Delete { key: String },
958    }
959
960    #[derive(Debug, Clone, Serialize, Deserialize)]
961    #[serde(tag = "type", rename_all = "snake_case")]
962    pub enum TextOp {
963        Insert {
964            pos: u32,
965            text: String,
966        },
967        Delete {
968            pos: i32,
969            len: i32,
970            #[serde(with = "self::serde_impl::id")]
971            start_id: ID,
972        },
973        Mark {
974            start: u32,
975            end: u32,
976            style_key: String,
977            style_value: LoroValue,
978            info: u8,
979        },
980        MarkEnd,
981    }
982
983    impl TextOp {
984        fn op_len(&self) -> usize {
985            match self {
986                TextOp::Insert { text, .. } => text.chars().count(),
987                TextOp::Delete { len, .. } => len.unsigned_abs() as usize,
988                TextOp::Mark { .. } => 1,
989                TextOp::MarkEnd => 1,
990            }
991        }
992    }
993
994    #[derive(Debug, Clone, Serialize, Deserialize)]
995    #[serde(tag = "type", rename_all = "snake_case")]
996    pub enum TreeOp {
997        Create {
998            #[serde(with = "self::serde_impl::tree_id")]
999            target: TreeID,
1000            #[serde(with = "self::serde_impl::option_tree_id")]
1001            parent: Option<TreeID>,
1002            #[serde(default, with = "self::serde_impl::fractional_index")]
1003            fractional_index: FractionalIndex,
1004        },
1005        Move {
1006            #[serde(with = "self::serde_impl::tree_id")]
1007            target: TreeID,
1008            #[serde(with = "self::serde_impl::option_tree_id")]
1009            parent: Option<TreeID>,
1010            #[serde(default, with = "self::serde_impl::fractional_index")]
1011            fractional_index: FractionalIndex,
1012        },
1013        Delete {
1014            #[serde(with = "self::serde_impl::tree_id")]
1015            target: TreeID,
1016        },
1017    }
1018
1019    #[derive(Debug, Clone, Serialize, Deserialize)]
1020    #[serde(tag = "type", rename_all = "snake_case")]
1021    pub enum FutureOp {
1022        #[cfg(feature = "counter")]
1023        Counter(OwnedValue),
1024        Unknown(OwnedValue),
1025    }
1026
1027    mod serde_impl {
1028
1029        use loro_common::{ContainerID, ContainerType};
1030        use serde::{
1031            de::{MapAccess, Visitor},
1032            ser::SerializeStruct,
1033            Deserialize, Deserializer, Serialize, Serializer,
1034        };
1035
1036        #[allow(unused_imports)]
1037        use crate::encoding::OwnedValue;
1038
1039        impl Serialize for super::JsonOp {
1040            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1041            where
1042                S: Serializer,
1043            {
1044                let mut s = serializer.serialize_struct("Op", 3)?;
1045                s.serialize_field("container", &self.container.to_string())?;
1046                s.serialize_field("content", &self.content)?;
1047                s.serialize_field("counter", &self.counter)?;
1048                s.end()
1049            }
1050        }
1051
1052        impl<'de> Deserialize<'de> for super::JsonOp {
1053            fn deserialize<D>(deserializer: D) -> Result<super::JsonOp, D::Error>
1054            where
1055                D: Deserializer<'de>,
1056            {
1057                struct __Visitor;
1058
1059                impl<'de> Visitor<'de> for __Visitor {
1060                    type Value = super::JsonOp;
1061                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1062                        formatter.write_str("struct Op")
1063                    }
1064
1065                    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1066                    where
1067                        A: MapAccess<'de>,
1068                    {
1069                        let (_key, container) = map.next_entry::<String, String>()?.unwrap();
1070                        let is_unknown = container.ends_with(')');
1071                        let container = ContainerID::try_from(container.as_str())
1072                            .map_err(|_| serde::de::Error::custom("invalid container id"))?;
1073                        let op = if is_unknown {
1074                            let (_key, op) =
1075                                map.next_entry::<String, super::FutureOpWrapper>()?.unwrap();
1076                            super::JsonOpContent::Future(op)
1077                        } else {
1078                            match container.container_type() {
1079                                ContainerType::List => {
1080                                    let (_key, op) =
1081                                        map.next_entry::<String, super::ListOp>()?.unwrap();
1082                                    super::JsonOpContent::List(op)
1083                                }
1084                                ContainerType::MovableList => {
1085                                    let (_key, op) =
1086                                        map.next_entry::<String, super::MovableListOp>()?.unwrap();
1087                                    super::JsonOpContent::MovableList(op)
1088                                }
1089                                ContainerType::Map => {
1090                                    let (_key, op) =
1091                                        map.next_entry::<String, super::MapOp>()?.unwrap();
1092                                    super::JsonOpContent::Map(op)
1093                                }
1094                                ContainerType::Text => {
1095                                    let (_key, op) =
1096                                        map.next_entry::<String, super::TextOp>()?.unwrap();
1097                                    super::JsonOpContent::Text(op)
1098                                }
1099                                ContainerType::Tree => {
1100                                    let (_key, op) =
1101                                        map.next_entry::<String, super::TreeOp>()?.unwrap();
1102                                    super::JsonOpContent::Tree(op)
1103                                }
1104                                #[cfg(feature = "counter")]
1105                                ContainerType::Counter => {
1106                                    let (_key, value) =
1107                                        map.next_entry::<String, OwnedValue>()?.unwrap();
1108                                    super::JsonOpContent::Future(super::FutureOpWrapper {
1109                                        prop: 0,
1110                                        value: super::FutureOp::Counter(value),
1111                                    })
1112                                }
1113                                _ => unreachable!(),
1114                            }
1115                        };
1116                        let (_, counter) = map.next_entry::<String, i32>()?.unwrap();
1117                        Ok(super::JsonOp {
1118                            container,
1119                            content: op,
1120                            counter,
1121                        })
1122                    }
1123                }
1124                const FIELDS: &[&str] = &["container", "content", "counter"];
1125                deserializer.deserialize_struct("JsonOp", FIELDS, __Visitor)
1126            }
1127        }
1128
1129        pub mod id {
1130            use loro_common::ID;
1131            use serde::{Deserialize, Deserializer, Serializer};
1132
1133            pub fn serialize<S>(id: &ID, s: S) -> Result<S::Ok, S::Error>
1134            where
1135                S: Serializer,
1136            {
1137                s.serialize_str(&id.to_string())
1138            }
1139
1140            pub fn deserialize<'de, 'a, D>(d: D) -> Result<ID, D::Error>
1141            where
1142                D: Deserializer<'de>,
1143            {
1144                // NOTE: https://github.com/serde-rs/serde/issues/2467    we use String here
1145                let str: String = Deserialize::deserialize(d)?;
1146                let id: ID = ID::try_from(str.as_str()).unwrap();
1147                Ok(id)
1148            }
1149        }
1150
1151        pub mod frontiers {
1152            use loro_common::ID;
1153            use serde::{ser::SerializeMap, Deserializer, Serializer};
1154
1155            use crate::version::Frontiers;
1156
1157            pub fn serialize<S>(f: &Frontiers, s: S) -> Result<S::Ok, S::Error>
1158            where
1159                S: Serializer,
1160            {
1161                let mut map = s.serialize_map(Some(f.len()))?;
1162                for id in f.iter() {
1163                    map.serialize_entry(&id.peer.to_string(), &id.counter)?;
1164                }
1165                map.end()
1166            }
1167
1168            pub fn deserialize<'de, 'a, D>(d: D) -> Result<Frontiers, D::Error>
1169            where
1170                D: Deserializer<'de>,
1171            {
1172                struct __Visitor;
1173                impl<'de> serde::de::Visitor<'de> for __Visitor {
1174                    type Value = Frontiers;
1175                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1176                        formatter.write_str("a Frontiers")
1177                    }
1178
1179                    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1180                    where
1181                        A: serde::de::MapAccess<'de>,
1182                    {
1183                        let mut f = Frontiers::default();
1184                        while let Some((k, v)) = map.next_entry::<String, i32>()? {
1185                            f.push(ID::new(k.parse().unwrap(), v))
1186                        }
1187                        Ok(f)
1188                    }
1189                }
1190                d.deserialize_map(__Visitor)
1191            }
1192        }
1193
1194        pub mod deps {
1195            use loro_common::ID;
1196            use serde::{Deserialize, Deserializer, Serializer};
1197
1198            pub fn serialize<S>(deps: &[ID], s: S) -> Result<S::Ok, S::Error>
1199            where
1200                S: Serializer,
1201            {
1202                s.collect_seq(deps.iter().map(|x| x.to_string()))
1203            }
1204
1205            pub fn deserialize<'de, 'a, D>(d: D) -> Result<Vec<ID>, D::Error>
1206            where
1207                D: Deserializer<'de>,
1208            {
1209                let deps: Vec<String> = Deserialize::deserialize(d)?;
1210                Ok(deps
1211                    .into_iter()
1212                    .map(|x| ID::try_from(x.as_str()).unwrap())
1213                    .collect())
1214            }
1215        }
1216
1217        pub mod peer_id {
1218            use loro_common::PeerID;
1219            use serde::{Deserialize, Deserializer, Serializer};
1220
1221            pub fn serialize<S>(peers: &Option<Vec<PeerID>>, s: S) -> Result<S::Ok, S::Error>
1222            where
1223                S: Serializer,
1224            {
1225                match peers {
1226                    Some(peers) => s.collect_seq(peers.iter().map(|x| x.to_string())),
1227                    None => s.serialize_none(),
1228                }
1229            }
1230
1231            pub fn deserialize<'de, 'a, D>(d: D) -> Result<Option<Vec<PeerID>>, D::Error>
1232            where
1233                D: Deserializer<'de>,
1234            {
1235                let peers: Option<Vec<String>> = Deserialize::deserialize(d)?;
1236                Ok(peers.map(|x| x.into_iter().map(|x| x.parse().unwrap()).collect()))
1237            }
1238        }
1239
1240        pub mod idlp {
1241            use loro_common::IdLp;
1242            use serde::{Deserialize, Deserializer, Serializer};
1243
1244            pub fn serialize<S>(idlp: &IdLp, s: S) -> Result<S::Ok, S::Error>
1245            where
1246                S: Serializer,
1247            {
1248                s.serialize_str(&idlp.to_string())
1249            }
1250
1251            pub fn deserialize<'de, 'a, D>(d: D) -> Result<IdLp, D::Error>
1252            where
1253                D: Deserializer<'de>,
1254            {
1255                let str: String = Deserialize::deserialize(d)?;
1256                let id: IdLp = IdLp::try_from(str.as_str()).unwrap();
1257                Ok(id)
1258            }
1259        }
1260
1261        pub mod tree_id {
1262            use loro_common::TreeID;
1263            use serde::{Deserialize, Deserializer, Serializer};
1264
1265            pub fn serialize<S>(id: &TreeID, s: S) -> Result<S::Ok, S::Error>
1266            where
1267                S: Serializer,
1268            {
1269                s.serialize_str(&id.to_string())
1270            }
1271
1272            pub fn deserialize<'de, 'a, D>(d: D) -> Result<TreeID, D::Error>
1273            where
1274                D: Deserializer<'de>,
1275            {
1276                let str: String = Deserialize::deserialize(d)?;
1277                let id: TreeID = TreeID::try_from(str.as_str()).unwrap();
1278                Ok(id)
1279            }
1280        }
1281
1282        pub mod option_tree_id {
1283            use loro_common::TreeID;
1284            use serde::{Deserialize, Deserializer, Serializer};
1285
1286            pub fn serialize<S>(id: &Option<TreeID>, s: S) -> Result<S::Ok, S::Error>
1287            where
1288                S: Serializer,
1289            {
1290                match id {
1291                    Some(id) => s.serialize_str(&id.to_string()),
1292                    None => s.serialize_none(),
1293                }
1294            }
1295
1296            pub fn deserialize<'de, 'a, D>(d: D) -> Result<Option<TreeID>, D::Error>
1297            where
1298                D: Deserializer<'de>,
1299            {
1300                let str: Option<String> = Deserialize::deserialize(d)?;
1301                match str {
1302                    Some(str) => {
1303                        let id: TreeID = TreeID::try_from(str.as_str()).unwrap();
1304                        Ok(Some(id))
1305                    }
1306                    None => Ok(None),
1307                }
1308            }
1309        }
1310
1311        pub mod fractional_index {
1312            use fractional_index::FractionalIndex;
1313            use serde::{Deserialize, Deserializer, Serializer};
1314
1315            pub fn serialize<S>(fi: &FractionalIndex, s: S) -> Result<S::Ok, S::Error>
1316            where
1317                S: Serializer,
1318            {
1319                s.serialize_str(&fi.to_string())
1320            }
1321
1322            pub fn deserialize<'de, 'a, D>(d: D) -> Result<FractionalIndex, D::Error>
1323            where
1324                D: Deserializer<'de>,
1325            {
1326                let str: String = Deserialize::deserialize(d)?;
1327                let fi = FractionalIndex::from_hex_string(str);
1328                Ok(fi)
1329            }
1330        }
1331    }
1332
1333    #[derive(thiserror::Error, Debug, PartialEq, Eq)]
1334    pub enum RedactError {
1335        #[error("unknown operation type")]
1336        UnknownOperationType,
1337    }
1338
1339    /// Redacts sensitive content within the specified range by replacing it with default values.
1340    ///
1341    /// This method applies the following redaction rules:
1342    ///
1343    /// - Preserves delete and move operations without changes
1344    /// - Replaces text insertion content with the Unicode replacement character (U+FFFD)
1345    /// - Substitutes list and map insert values with `LoroValue::Null`
1346    /// - Maintains child container creation operations
1347    /// - Replaces text mark values with `LoroValue::Null`
1348    /// - Preserves map insertion and text annotation keys
1349    /// - Resets counter operations to zero
1350    /// - Leaves unknown operation types (from future Loro versions) unchanged
1351    ///
1352    /// This approach ensures sensitive data removal while preserving the document's overall
1353    /// structure. Redacted documents maintain seamless collaboration capabilities with both
1354    /// redacted and non-redacted versions.
1355    pub fn redact(json: &mut JsonSchema, range: VersionRange) -> Result<(), RedactError> {
1356        let peers = json.peers.clone();
1357        let mut errors = Vec::new();
1358        for change in json.changes.iter_mut() {
1359            let real_peer = get_peer_from_peers(&peers, change.id.peer);
1360            let real_id = ID::new(real_peer, change.id.counter);
1361            if !range.has_overlap_with(real_id.to_span(change.op_len())) {
1362                continue;
1363            }
1364
1365            let redact_range = range.get(&real_peer).copied().unwrap();
1366            for op in change.ops.iter_mut() {
1367                if op.counter >= redact_range.1 {
1368                    break;
1369                }
1370
1371                let len = op.content.op_len() as Counter;
1372                if op.counter + len <= redact_range.0 {
1373                    continue;
1374                }
1375
1376                let result = redact_op(
1377                    &mut op.content,
1378                    (redact_range.0 - op.counter).max(0).min(len)
1379                        ..(redact_range.1 - op.counter).max(0).min(len),
1380                );
1381                match result {
1382                    Ok(()) => {}
1383                    Err(e) => errors.push(e),
1384                }
1385            }
1386        }
1387
1388        if errors.is_empty() {
1389            Ok(())
1390        } else {
1391            Err(errors.pop().unwrap())
1392        }
1393    }
1394
1395    fn redact_op(op: &mut JsonOpContent, range: Range<Counter>) -> Result<(), RedactError> {
1396        match op {
1397            JsonOpContent::List(list_op) => {
1398                match list_op {
1399                    ListOp::Insert { value: values, .. } => {
1400                        for i in range {
1401                            redact_value(&mut values[i as usize]);
1402                        }
1403                    }
1404                    ListOp::Delete { .. } => {
1405                        // Delete op won't be changed
1406                    }
1407                }
1408            }
1409            JsonOpContent::MovableList(movable_list_op) => {
1410                match movable_list_op {
1411                    MovableListOp::Insert { value: values, .. } => {
1412                        for i in range {
1413                            redact_value(&mut values[i as usize]);
1414                        }
1415                    }
1416                    MovableListOp::Delete { .. } | MovableListOp::Move { .. } => {
1417                        // Delete and move ops won't be changed
1418                    }
1419                    MovableListOp::Set { value, .. } => {
1420                        assert!(range.start == 0 && range.len() == 1);
1421                        redact_value(value);
1422                    }
1423                }
1424            }
1425            JsonOpContent::Map(map_op) => {
1426                match map_op {
1427                    MapOp::Insert { value, .. } => {
1428                        assert!(range.start == 0 && range.len() == 1);
1429                        redact_value(value);
1430                    }
1431                    MapOp::Delete { .. } => {
1432                        // Delete op won't be changed
1433                    }
1434                }
1435            }
1436            JsonOpContent::Text(text_op) => {
1437                match text_op {
1438                    TextOp::Insert { text, .. } => {
1439                        let mut chars = vec![];
1440                        for (i, c) in text.chars().enumerate() {
1441                            if i < range.start as usize || i >= range.end as usize {
1442                                chars.push(c);
1443                            } else {
1444                                chars.push("� ".chars().next().unwrap());
1445                            }
1446                        }
1447                        *text = chars.into_iter().collect();
1448                    }
1449                    TextOp::Delete { .. } => {
1450                        // Delete op won't be changed
1451                    }
1452                    TextOp::Mark { style_value, .. } => {
1453                        assert!(range.start == 0 && range.len() == 1);
1454                        *style_value = LoroValue::Null;
1455                    }
1456                    TextOp::MarkEnd => {
1457                        // MarkEnd won't be changed
1458                    }
1459                }
1460            }
1461            JsonOpContent::Tree(..) => {
1462                // Creation of child container won't be changed
1463            }
1464            JsonOpContent::Future(future_op_wrapper) => match &mut future_op_wrapper.value {
1465                #[cfg(feature = "counter")]
1466                FutureOp::Counter(owned_value) => {
1467                    *owned_value = OwnedValue::I64(0);
1468                }
1469                FutureOp::Unknown(..) => {
1470                    return Err(RedactError::UnknownOperationType);
1471                }
1472            },
1473        }
1474
1475        Ok(())
1476    }
1477}
1478
1479fn redact_value(v: &mut LoroValue) {
1480    match v {
1481        LoroValue::Container(_) => {}
1482        _ => *v = LoroValue::Null,
1483    }
1484}
1485
1486#[cfg(test)]
1487mod tests {
1488    use crate::{LoroDoc, VersionVector};
1489
1490    #[test]
1491    fn json_range_version() {
1492        let doc = LoroDoc::new_auto_commit();
1493        doc.set_peer_id(0).unwrap();
1494        let list = doc.get_list("list");
1495        list.insert(0, "a").unwrap();
1496        list.insert(0, "b").unwrap();
1497        list.insert(0, "c").unwrap();
1498        let json = doc.export_json_updates(
1499            &VersionVector::from_iter(vec![(0, 1)]),
1500            &VersionVector::from_iter(vec![(0, 2)]),
1501            true,
1502        );
1503        assert_eq!(json.changes[0].ops.len(), 1);
1504        let json = doc.export_json_updates(
1505            &VersionVector::from_iter(vec![(0, 0)]),
1506            &VersionVector::from_iter(vec![(0, 2)]),
1507            true,
1508        );
1509        assert_eq!(json.changes[0].ops.len(), 2);
1510    }
1511}