1use crate::{error::*, value::intermediate::Intermediate};
2use petgraph::{algo::astar, Graph};
3use serde::{de::DeserializeOwned, Deserialize, Serialize};
4
5#[derive(Debug, Default, Copy, Clone, PartialEq)]
7pub enum DiffOptimizationHint {
8 #[default]
10 Default,
11 SizeSource,
13 SizeTarget,
15 SizeValue(
17 usize,
19 ),
20 SizePercentage(
22 f64,
24 ),
25}
26
27#[derive(Debug, Default, Copy, Clone, PartialEq)]
29pub struct DiffOptions {
30 pub optimization_hint: DiffOptimizationHint,
32}
33
34impl DiffOptions {
35 pub fn optimization_hint(mut self, hint: DiffOptimizationHint) -> Self {
36 self.optimization_hint = hint;
37 self
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
43pub enum Change {
44 Same,
46 Removed,
48 Changed(
50 Intermediate,
52 ),
53 Added(
55 Intermediate,
57 ),
58 PartialChange(
60 Box<Change>,
62 ),
63 PartialSeq(
65 Vec<(usize, Change)>,
67 ),
68 PartialMap(
70 Vec<(Intermediate, Change)>,
72 ),
73 PartialStruct(
75 Vec<(String, Change)>,
77 ),
78}
79
80impl Change {
81 pub fn same() -> Self {
82 Self::Same
83 }
84
85 pub fn removed() -> Self {
86 Self::Removed
87 }
88
89 pub fn changed(value: impl Into<Intermediate>) -> Self {
90 Self::Changed(value.into())
91 }
92
93 pub fn added(value: impl Into<Intermediate>) -> Self {
94 Self::Added(value.into())
95 }
96
97 pub fn partial_change(change: Self) -> Self {
98 Self::PartialChange(Box::new(change))
99 }
100
101 pub fn partial_seq() -> Self {
102 Self::PartialSeq(vec![])
103 }
104
105 pub fn partial_map() -> Self {
106 Self::PartialMap(vec![])
107 }
108
109 pub fn partial_struct() -> Self {
110 Self::PartialStruct(vec![])
111 }
112
113 pub fn partial_seq_item(mut self, index: usize, change: Self) -> Self {
114 if let Self::PartialSeq(v) = &mut self {
115 v.push((index, change));
116 }
117 self
118 }
119
120 pub fn partial_map_item(mut self, key: impl Into<Intermediate>, change: Self) -> Self {
121 if let Self::PartialMap(v) = &mut self {
122 let key = key.into();
123 if let Some(item) = v.iter_mut().find(|(k, _)| k == &key) {
124 item.1 = change;
125 } else {
126 v.push((key, change));
127 }
128 }
129 self
130 }
131
132 pub fn partial_struct_item(mut self, name: impl ToString, change: Self) -> Self {
133 if let Self::PartialStruct(v) = &mut self {
134 let name = name.to_string();
135 if let Some(item) = v.iter_mut().find(|(n, _)| n == &name) {
136 item.1 = change;
137 } else {
138 v.push((name, change));
139 }
140 }
141 self
142 }
143
144 pub fn is_same(&self) -> bool {
145 matches!(self, Self::Same)
146 }
147
148 fn optimize(
149 self,
150 source: &Intermediate,
151 target: &Intermediate,
152 hint: DiffOptimizationHint,
153 ) -> Self {
154 match hint {
155 DiffOptimizationHint::Default => self,
156 DiffOptimizationHint::SizeSource => {
157 if self.total_bytesize() > source.total_bytesize() {
158 Self::Changed(target.to_owned())
159 } else {
160 self
161 }
162 }
163 DiffOptimizationHint::SizeTarget => {
164 if self.total_bytesize() > target.total_bytesize() {
165 Self::Changed(target.to_owned())
166 } else {
167 self
168 }
169 }
170 DiffOptimizationHint::SizeValue(threshold) => {
171 if self.total_bytesize() > threshold {
172 Self::Changed(target.to_owned())
173 } else {
174 self
175 }
176 }
177 DiffOptimizationHint::SizePercentage(threshold) => {
178 if self.total_bytesize()
179 > (threshold.clamp(0.0, 1.0) * source.total_bytesize() as f64) as _
180 {
181 Self::Changed(target.to_owned())
182 } else {
183 self
184 }
185 }
186 }
187 }
188
189 pub fn difference(prev: &Intermediate, next: &Intermediate, options: &DiffOptions) -> Self {
190 if prev == next {
191 Self::Same
192 } else {
193 match (prev, next) {
194 (Intermediate::Option(Some(prev)), Intermediate::Option(Some(next))) => {
195 Self::PartialChange(Box::new(Self::difference(prev, next, options)))
196 }
197 (Intermediate::NewTypeStruct(prev), Intermediate::NewTypeStruct(next)) => {
198 Self::PartialChange(Box::new(Self::difference(prev, next, options)))
199 }
200 (
201 Intermediate::NewTypeVariant(prev_name, prev_value),
202 Intermediate::NewTypeVariant(next_name, next_value),
203 ) => {
204 if prev_name != next_name {
205 Self::Changed(next.to_owned())
206 } else {
207 Self::PartialChange(Box::new(Self::difference(
208 prev_value, next_value, options,
209 )))
210 }
211 }
212 (Intermediate::Seq(prev), Intermediate::Seq(next))
213 | (Intermediate::Tuple(prev), Intermediate::Tuple(next))
214 | (Intermediate::TupleStruct(prev), Intermediate::TupleStruct(next)) => {
215 Self::PartialSeq(Self::sequence_difference(prev, next, options))
216 }
217 (Intermediate::Map(prev), Intermediate::Map(next)) => {
218 let mut result = vec![];
219 for (nk, nv) in next {
220 if !prev.iter().any(|(pk, _)| pk == nk) {
221 result.push((nk.to_owned(), Self::Added(nv.to_owned())));
222 }
223 }
224 for (pk, _) in prev {
225 if !next.iter().any(|(nk, _)| pk == nk) {
226 result.push((pk.to_owned(), Self::Removed));
227 }
228 }
229 for (pk, pv) in prev {
230 if let Some((_, nv)) = next
231 .iter()
232 .find(|(nk, _)| pk == nk)
233 .filter(|(_, nv)| pv != nv)
234 {
235 let diff = Self::difference(pv, nv, options);
236 if !diff.is_same() {
237 result.push((pk.to_owned(), diff));
238 }
239 }
240 }
241 Self::PartialMap(result)
242 }
243 (Intermediate::Struct(prev), Intermediate::Struct(next))
244 | (Intermediate::StructVariant(_, prev), Intermediate::StructVariant(_, next)) => {
245 let mut result = vec![];
246 for (nk, nv) in next {
247 if !prev.iter().any(|(pk, _)| pk == nk) {
248 result.push((nk.to_owned(), Self::Added(nv.to_owned())));
249 }
250 }
251 for (pk, _) in prev {
252 if !next.iter().any(|(nk, _)| pk == nk) {
253 result.push((pk.to_owned(), Self::Removed));
254 }
255 }
256 for (pk, pv) in prev {
257 if let Some((_, nv)) = next
258 .iter()
259 .find(|(nk, _)| pk == nk)
260 .filter(|(_, nv)| pv != nv)
261 {
262 let diff = Self::difference(pv, nv, options);
263 if !diff.is_same() {
264 result.push((pk.to_owned(), diff));
265 }
266 }
267 }
268 Self::PartialStruct(result)
269 }
270 _ => Self::Changed(next.to_owned()),
271 }
272 }
273 .optimize(prev, next, options.optimization_hint)
274 }
275
276 pub fn sequence_difference(
277 prev: &[Intermediate],
278 next: &[Intermediate],
279 options: &DiffOptions,
280 ) -> Vec<(usize, Self)> {
281 if prev.is_empty() && next.is_empty() {
282 return vec![];
283 } else if prev.is_empty() {
284 return next
285 .iter()
286 .enumerate()
287 .map(|(i, v)| (i, Self::Added(v.to_owned())))
288 .collect();
289 } else if next.is_empty() {
290 return (0..prev.len()).map(|_| (0, Self::Removed)).collect();
291 }
292
293 type Location = (usize, usize);
295
296 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
297 enum Diff {
298 Unchanged,
299 Changed,
300 Added,
301 Removed,
302 }
303
304 impl Diff {
305 fn cost(&self) -> usize {
306 match self {
307 Self::Added => 8,
308 Self::Removed => 9,
309 Self::Unchanged => 10,
310 Self::Changed => 11,
311 }
312 }
313 }
314
315 let cols = prev.len() + 1;
316 let rows = next.len() + 1;
317 let mut graph = Graph::<Location, Diff>::with_capacity(
318 cols * rows,
319 (cols - 1) * rows + (rows - 1) * cols + (cols - 1) * (rows - 1),
320 );
321 let mut nodes = Vec::with_capacity(cols * rows);
322 for row in 0..rows {
323 for col in 0..cols {
324 nodes.push(graph.add_node((col, row)));
325 }
326 }
327 let get_node = |col, row| nodes[row * cols + col];
328 for row in 0..rows {
329 for col in 0..(cols - 1) {
330 graph.add_edge(get_node(col, row), get_node(col + 1, row), Diff::Removed);
331 }
332 }
333 for col in 0..cols {
334 for row in 0..(rows - 1) {
335 graph.add_edge(get_node(col, row), get_node(col, row + 1), Diff::Added);
336 }
337 }
338 for (col, prev) in prev.iter().enumerate().take(cols - 1) {
339 for (row, next) in next.iter().enumerate().take(rows - 1) {
340 if prev == next {
341 graph.add_edge(
342 get_node(col, row),
343 get_node(col + 1, row + 1),
344 Diff::Unchanged,
345 );
346 } else {
347 graph.add_edge(
348 get_node(col, row),
349 get_node(col + 1, row + 1),
350 Diff::Changed,
351 );
352 }
353 }
354 }
355 let finish = *nodes.last().unwrap();
356 astar(
357 &graph,
358 *nodes.first().unwrap(),
359 |n| n == finish,
360 |e| e.weight().cost(),
361 |_| 0,
362 )
363 .map(|(_, path)| {
364 let mut pos = 0;
365 path.windows(2)
366 .filter_map(|chunk| {
367 let diff = graph
368 .find_edge(chunk[0], chunk[1])
369 .and_then(|e| graph.edge_weight(e))?;
370 let old_pos = pos;
371 match diff {
372 Diff::Unchanged => {
373 pos += 1;
374 None
375 }
376 Diff::Changed => {
377 pos += 1;
378 let (prev_pos, _) = graph.node_weight(chunk[0])?;
379 let prev = &prev[*prev_pos];
380 let next = &next[old_pos];
381 let diff = Self::difference(prev, next, options).optimize(
382 prev,
383 next,
384 options.optimization_hint,
385 );
386 Some((old_pos, diff))
387 }
388 Diff::Removed => Some((old_pos, Self::Removed)),
389 Diff::Added => {
390 pos += 1;
391 Some((old_pos, Self::Added(next[old_pos].to_owned())))
392 }
393 }
394 })
395 .collect()
396 })
397 .unwrap_or_default()
398 }
399
400 pub fn patch(&self, value: &Intermediate) -> Result<Option<Intermediate>> {
401 match self {
402 Self::Same => Ok(Some(value.to_owned())),
403 Self::Removed => Ok(None),
404 Self::Changed(v) => Ok(Some(v.to_owned())),
405 Self::Added(_) => Err(Error::CannotAdd(value.to_owned())),
406 Self::PartialChange(change) => {
407 let result = match value {
408 Intermediate::Option(Some(v)) => match change.patch(v)? {
409 Some(v) => Intermediate::Option(Some(Box::new(v))),
410 _ => return Err(Error::NotPartial(value.to_owned())),
411 },
412 Intermediate::NewTypeStruct(v) => match change.patch(v)? {
413 Some(v) => Intermediate::NewTypeStruct(Box::new(v)),
414 _ => return Err(Error::NotPartial(value.to_owned())),
415 },
416 Intermediate::NewTypeVariant(n, v) => match change.patch(v)? {
417 Some(v) => Intermediate::NewTypeVariant(n.to_owned(), Box::new(v)),
418 _ => return Err(Error::NotPartial(value.to_owned())),
419 },
420 _ => return Err(Error::NotPartial(value.to_owned())),
421 };
422 Ok(Some(result))
423 }
424 Self::PartialSeq(changes) => {
425 fn implement(
426 v: &[Intermediate],
427 changes: &[(usize, Change)],
428 ) -> Result<Vec<Intermediate>> {
429 let mut result = v.to_owned();
430 for (index, change) in changes {
431 match change {
432 Change::Removed => {
433 result.remove(*index);
434 }
435 Change::Changed(v) => {
436 if let Some(item) = result.get_mut(*index) {
437 *item = v.to_owned();
438 }
439 }
440 Change::Added(v) => result.insert(*index, v.to_owned()),
441 change => {
442 if let Some(item) = result.get_mut(*index) {
443 if let Some(patched) = change.patch(item)? {
444 *item = patched;
445 }
446 }
447 }
448 }
449 }
450 Ok(result)
451 }
452
453 match value {
454 Intermediate::Seq(v) => Ok(Some(Intermediate::Seq(implement(v, changes)?))),
455 Intermediate::Tuple(v) => Ok(Some(Intermediate::Tuple(implement(v, changes)?))),
456 Intermediate::TupleStruct(v) => {
457 Ok(Some(Intermediate::TupleStruct(implement(v, changes)?)))
458 }
459 _ => Err(Error::NotSeq(value.to_owned())),
460 }
461 }
462 Self::PartialMap(changes) => match value {
463 Intermediate::Map(v) => {
464 let mut result = v.to_owned();
465 for (key, change) in changes {
466 match change {
467 Self::Removed => {
468 if let Some(index) = result.iter().position(|(k, _)| k == key) {
469 result.remove(index);
470 }
471 }
472 Self::Changed(v) => {
473 if let Some(index) = result.iter().position(|(k, _)| k == key) {
474 if let Some(item) = result.get_mut(index) {
475 item.1 = v.to_owned();
476 }
477 }
478 }
479 Self::Added(v) => {
480 if let Some(item) = result.iter_mut().find(|(k, _)| k == key) {
481 item.1 = v.to_owned();
482 } else {
483 result.push((key.to_owned(), v.to_owned()))
484 }
485 }
486 change => {
487 if let Some(item) = result.iter_mut().find(|(k, _)| k == key) {
488 if let Some(patched) = change.patch(&item.1)? {
489 item.1 = patched;
490 }
491 }
492 }
493 }
494 }
495 Ok(Some(Intermediate::Map(result)))
496 }
497 _ => Err(Error::NotMap(value.to_owned())),
498 },
499 Self::PartialStruct(changes) => {
500 fn implement(
501 v: &[(String, Intermediate)],
502 changes: &[(String, Change)],
503 ) -> Result<Vec<(String, Intermediate)>> {
504 let mut result = v.to_owned();
505 for (key, change) in changes {
506 match change {
507 Change::Removed => {
508 if let Some(index) = result.iter().position(|(k, _)| k == key) {
509 result.remove(index);
510 }
511 }
512 Change::Changed(v) => {
513 if let Some(index) = result.iter().position(|(k, _)| k == key) {
514 if let Some(item) = result.get_mut(index) {
515 item.1 = v.to_owned();
516 }
517 }
518 }
519 Change::Added(v) => {
520 if let Some(item) = result.iter_mut().find(|(k, _)| k == key) {
521 item.1 = v.to_owned();
522 } else {
523 result.push((key.to_owned(), v.to_owned()))
524 }
525 }
526 change => {
527 if let Some(item) = result.iter_mut().find(|(k, _)| k == key) {
528 if let Some(patched) = change.patch(&item.1)? {
529 item.1 = patched;
530 }
531 }
532 }
533 }
534 }
535 Ok(result)
536 }
537
538 match value {
539 Intermediate::Struct(v) => {
540 Ok(Some(Intermediate::Struct(implement(v, changes)?)))
541 }
542 Intermediate::StructVariant(n, v) => Ok(Some(Intermediate::StructVariant(
543 n.to_owned(),
544 implement(v, changes)?,
545 ))),
546 _ => Err(Error::NotMap(value.to_owned())),
547 }
548 }
549 }
550 }
551
552 pub fn data_difference<P, N>(prev: &P, next: &N, options: &DiffOptions) -> Result<Self>
553 where
554 P: Serialize,
555 N: Serialize,
556 {
557 let prev = crate::to_intermediate(prev)?;
558 let next = crate::to_intermediate(next)?;
559 Ok(Self::difference(&prev, &next, options))
560 }
561
562 pub fn data_patch<T>(&self, data: &T) -> Result<Option<T>>
563 where
564 T: Serialize + DeserializeOwned,
565 {
566 let serialized = crate::to_intermediate(data)?;
567 let patched = match self.patch(&serialized)? {
568 Some(patched) => patched,
569 None => return Ok(None),
570 };
571 Ok(Some(crate::from_intermediate::<T>(&patched)?))
572 }
573
574 pub fn total_bytesize(&self) -> usize {
575 fn string_bytesize(v: &str) -> usize {
576 std::mem::size_of_val(v.as_bytes())
577 }
578
579 std::mem::size_of_val(self)
580 + match self {
581 Self::Changed(v) | Self::Added(v) => v.total_bytesize(),
582 Self::PartialChange(v) => v.total_bytesize(),
583 Self::PartialSeq(v) => v
584 .iter()
585 .map(|(i, v)| std::mem::size_of_val(i) + v.total_bytesize())
586 .sum(),
587 Self::PartialMap(v) => v
588 .iter()
589 .map(|(k, v)| k.total_bytesize() + v.total_bytesize())
590 .sum(),
591 Self::PartialStruct(v) => v
592 .iter()
593 .map(|(k, v)| string_bytesize(k) + v.total_bytesize())
594 .sum(),
595 _ => 0,
596 }
597 }
598}