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