Oregami
Repositories/oxedyne/ore

oxedyne/ore/oracle/src/replica.rs

4.3 KiB, 1 run

created by r2848102244:93, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! A simulated replica: the frontend that turns index-based editing intent
2//! into content-anchored operations.
3//!
4//! The oracle keeps this deliberately slow, re-rendering before every
5//! operation, because the point is to exercise the structure and not the
6//! frontend.
7
8use crate::doc::{
9 Bind,
10 Doc,
11 Mode,
12 Render,
13};
14use crate::id::{
15 after,
16 before,
17 Anchor,
18 ContentId,
19 ContentRange,
20 OpId,
21};
22use crate::op::Op;
23
24use oxedyne_fe2o3_core::prelude::*;
25
26/// Coalesces a run of content ids into the fewest content ranges.
27pub fn coalesce(ids: &[ContentId]) -> Outcome<Vec<ContentRange>> {
28 let mut out: Vec<ContentRange> = Vec::new();
29 for cid in ids {
30 match out.last_mut() {
31 Some(r) if r.op == cid.op && r.to == cid.off => r.to += 1,
32 _ => out.push(res!(
33 ContentRange::new(cid.op, cid.off, cid.off + 1))),
34 }
35 }
36 Ok(out)
37}
38
39/// One replica of the document.
40pub struct Replica {
41 /// Replica number, the second component of every op id it mints.
42 pub id: u32,
43 /// The operation set.
44 pub doc: Doc,
45}
46
47impl Replica {
48 /// Creates a replica with the design note's own anchor binding.
49 pub fn new(id: u32, mode: Mode) -> Self {
50 Self { id, doc: Doc::new(mode) }
51 }
52
53 /// Creates a replica with an explicit anchor binding rule.
54 pub fn with_bind(id: u32, mode: Mode, bind: Bind) -> Self {
55 Self { id, doc: Doc::with_bind(mode, bind) }
56 }
57
58 /// Renders the replica's view.
59 pub fn view(&self) -> Outcome<Render> {
60 self.doc.render()
61 }
62
63 /// The rendered bytes.
64 pub fn text(&self) -> Outcome<String> {
65 Ok(res!(self.view()).text())
66 }
67
68 /// Mints the next operation id, with a Lamport counter.
69 pub fn next_id(&self) -> OpId {
70 OpId::new(self.doc.max_counter() + 1, self.id)
71 }
72
73 /// Receives an operation from another replica.
74 pub fn recv(&mut self, op: Op) {
75 self.doc.apply(op);
76 }
77
78 /// The anchors bracketing a gap at the given index of a rendered view.
79 pub fn gap(prov: &[ContentId], index: usize) -> (Anchor, Anchor) {
80 let l = if index > 0 { after(prov[index - 1]) } else { None };
81 let r = if index < prov.len() { before(prov[index]) } else { None };
82 (l, r)
83 }
84
85 /// Inserts bytes at a rendered index.
86 pub fn insert(&mut self, index: usize, bytes: &[u8]) -> Outcome<Op> {
87 let v = res!(self.view());
88 if index > v.prov.len() {
89 return Err(err!("Insert index {} beyond length {}.",
90 index, v.prov.len(); Invalid, Input));
91 }
92 let (left, right) = Self::gap(&v.prov, index);
93 let op = Op::Splice {
94 id: self.next_id(),
95 left,
96 right,
97 remove: Vec::new(),
98 insert: bytes.to_vec(),
99 };
100 self.doc.apply(op.clone());
101 Ok(op)
102 }
103
104 /// Deletes a run at a rendered index.
105 pub fn delete(&mut self, index: usize, len: usize) -> Outcome<Op> {
106 let v = res!(self.view());
107 if index + len > v.prov.len() {
108 return Err(err!("Delete {}..{} beyond length {}.",
109 index, index + len, v.prov.len(); Invalid, Input));
110 }
111 let op = Op::Splice {
112 id: self.next_id(),
113 left: None,
114 right: None,
115 remove: res!(coalesce(&v.prov[index..index + len])),
116 insert: Vec::new(),
117 };
118 self.doc.apply(op.clone());
119 Ok(op)
120 }
121
122 /// Replaces a run at a rendered index, in one splice.
123 pub fn replace(&mut self, index: usize, len: usize, bytes: &[u8]) -> Outcome<Op> {
124 let v = res!(self.view());
125 if index + len > v.prov.len() {
126 return Err(err!("Replace {}..{} beyond length {}.",
127 index, index + len, v.prov.len(); Invalid, Input));
128 }
129 let (left, right) = Self::gap(&v.prov, index);
130 let op = Op::Splice {
131 id: self.next_id(),
132 left,
133 right,
134 remove: res!(coalesce(&v.prov[index..index + len])),
135 insert: bytes.to_vec(),
136 };
137 self.doc.apply(op.clone());
138 Ok(op)
139 }
140
141 /// Moves a rendered run to a rendered index. Both are read from the
142 /// replica's current view, as a real frontend would.
143 pub fn move_range(&mut self, index: usize, len: usize, dest: usize) -> Outcome<Op> {
144 let v = res!(self.view());
145 if index + len > v.prov.len() {
146 return Err(err!("Move source {}..{} beyond length {}.",
147 index, index + len, v.prov.len(); Invalid, Input));
148 }
149 if dest > v.prov.len() {
150 return Err(err!("Move destination {} beyond length {}.",
151 dest, v.prov.len(); Invalid, Input));
152 }
153 let (left, right) = Self::gap(&v.prov, dest);
154 let op = Op::Move {
155 id: self.next_id(),
156 src: res!(coalesce(&v.prov[index..index + len])),
157 left,
158 right,
159 };
160 self.doc.apply(op.clone());
161 Ok(op)
162 }
163}