1use serde::{Deserialize, Serialize};
4
5use super::cid::Cid;
6use super::error::{Error, Mutation};
7use super::store::Store;
8use super::{Prolly, Tree};
9
10#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
11pub enum LogicalPatch {
12 Upsert {
13 key: Vec<u8>,
14 old: Option<Vec<u8>>,
15 new: Vec<u8>,
16 },
17 Delete {
18 key: Vec<u8>,
19 old: Vec<u8>,
20 },
21}
22
23impl LogicalPatch {
24 pub fn key(&self) -> &[u8] {
25 match self {
26 Self::Upsert { key, .. } | Self::Delete { key, .. } => key,
27 }
28 }
29}
30
31#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
32pub enum StructuralEdit {
33 Point(LogicalPatch),
34 Subtree {
35 start_exclusive: Option<Vec<u8>>,
36 end_inclusive: Vec<u8>,
37 level: u16,
38 cid: Option<Cid>,
39 logical_count: u64,
40 },
41}
42
43#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
44pub struct StructuralPatch {
45 pub base_root: Option<Cid>,
46 pub format_digest: Cid,
47 pub edits: Vec<StructuralEdit>,
48}
49
50impl StructuralPatch {
51 pub fn validate(&self) -> Result<(), Error> {
52 if let [StructuralEdit::Subtree {
53 start_exclusive: None,
54 end_inclusive,
55 level,
56 cid,
57 logical_count,
58 }] = self.edits.as_slice()
59 {
60 if end_inclusive.is_empty() {
61 let valid_root = match cid {
62 Some(_) => *logical_count == 0 && *level == 0,
63 None => *logical_count == 0 && *level == 0,
64 };
65 if !valid_root {
66 return Err(Error::InvalidStructuralPatch(
67 "invalid root subtree replacement".to_string(),
68 ));
69 }
70 return Ok(());
71 }
72 }
73
74 let mut previous_end: Option<&[u8]> = None;
75 for edit in &self.edits {
76 let (start, end) = match edit {
77 StructuralEdit::Point(point) => (None, point.key()),
78 StructuralEdit::Subtree {
79 start_exclusive,
80 end_inclusive,
81 level,
82 cid,
83 logical_count,
84 } => {
85 if *level == 0
86 || *logical_count == 0
87 || cid.is_none()
88 || start_exclusive
89 .as_ref()
90 .map(|start| start >= end_inclusive)
91 .unwrap_or(false)
92 {
93 return Err(Error::InvalidStructuralPatch(
94 "invalid subtree replacement".to_string(),
95 ));
96 }
97 (start_exclusive.as_deref(), end_inclusive.as_slice())
98 }
99 };
100 if previous_end
101 .map(|previous| previous >= end)
102 .unwrap_or(false)
103 || start
104 .zip(previous_end)
105 .map(|(start, previous)| start < previous)
106 .unwrap_or(false)
107 {
108 return Err(Error::InvalidStructuralPatch(
109 "patch edits must be strictly ordered and non-overlapping".to_string(),
110 ));
111 }
112 previous_end = Some(end);
113 }
114 Ok(())
115 }
116}
117
118impl<S: Store> Prolly<S> {
119 pub fn diff_patch(&self, base: &Tree, target: &Tree) -> Result<StructuralPatch, Error> {
127 if base.config.format != target.config.format {
128 return Err(Error::PatchBaseMismatch);
129 }
130 let edits = if base.root == target.root {
131 Vec::new()
132 } else if let Some(cid) = &target.root {
133 vec![StructuralEdit::Subtree {
134 start_exclusive: None,
135 end_inclusive: Vec::new(),
136 level: 0,
137 cid: Some(cid.clone()),
138 logical_count: 0,
139 }]
140 } else {
141 vec![StructuralEdit::Subtree {
142 start_exclusive: None,
143 end_inclusive: Vec::new(),
144 level: 0,
145 cid: None,
146 logical_count: 0,
147 }]
148 };
149 Ok(StructuralPatch {
150 base_root: base.root.clone(),
151 format_digest: self.format_digest()?,
152 edits,
153 })
154 }
155
156 pub fn apply_patch(&self, base: &Tree, patch: &StructuralPatch) -> Result<Tree, Error> {
158 patch.validate()?;
159 if patch.base_root != base.root || patch.format_digest != self.format_digest()? {
160 return Err(Error::PatchBaseMismatch);
161 }
162
163 if patch.edits.is_empty() {
164 return Ok(base.clone());
165 }
166 if let [StructuralEdit::Subtree {
167 start_exclusive: None,
168 end_inclusive,
169 level: _,
170 cid,
171 logical_count,
172 }] = patch.edits.as_slice()
173 {
174 if end_inclusive.is_empty() {
175 let target = Tree {
176 root: cid.clone(),
177 config: base.config.clone(),
178 };
179 match cid {
180 Some(cid) => {
181 let _ = self.load_arc_ready(cid)?;
182 }
183 None if *logical_count == 0 => {}
184 None => {
185 return Err(Error::InvalidStructuralPatch(
186 "empty root subtree metadata is inconsistent".to_string(),
187 ));
188 }
189 }
190 return Ok(target);
191 }
192 }
193
194 let keys = patch
195 .edits
196 .iter()
197 .filter_map(|edit| match edit {
198 StructuralEdit::Point(point) => Some(point.key()),
199 StructuralEdit::Subtree { .. } => None,
200 })
201 .collect::<Vec<_>>();
202 if keys.len() != patch.edits.len() {
203 return Err(Error::InvalidStructuralPatch(
204 "partial subtree replacement requires an imported subtree plan".to_string(),
205 ));
206 }
207 let existing = self.get_many(base, &keys)?;
208 let mut mutations = Vec::with_capacity(patch.edits.len());
209 for (edit, existing) in patch.edits.iter().zip(existing) {
210 let point = match edit {
211 StructuralEdit::Point(point) => point,
212 StructuralEdit::Subtree { .. } => {
213 return Err(Error::InvalidStructuralPatch(
214 "subtree replacement requires a verified imported subtree".to_string(),
215 ));
216 }
217 };
218 match point {
219 LogicalPatch::Upsert { key, old, new } => {
220 if existing != *old {
221 return Err(Error::PatchBaseMismatch);
222 }
223 mutations.push(Mutation::Upsert {
224 key: key.clone(),
225 val: new.clone(),
226 });
227 }
228 LogicalPatch::Delete { key, old } => {
229 if existing.as_ref() != Some(old) {
230 return Err(Error::PatchBaseMismatch);
231 }
232 mutations.push(Mutation::Delete { key: key.clone() });
233 }
234 }
235 }
236 self.batch(base, mutations)
237 }
238}