1use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct CompiledArtifact {
9 pub root_id: u64,
11 pub view: SerializedView,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct SerializedView {
18 pub view_type: String,
20 pub props: serde_json::Value,
22 pub children: Vec<SerializedView>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub enum RuntimePatch {
29 ReplaceView {
31 node_id: u64,
33 new_view: SerializedView,
35 },
36 UpdateState {
38 node_id: u64,
40 field: String,
42 value: serde_json::Value,
44 },
45 Batch(Vec<RuntimePatch>),
47}
48
49pub struct PatchEngine {
51 previous_view: Option<SerializedView>,
52}
53
54impl PatchEngine {
55 pub fn new() -> Self {
57 Self {
58 previous_view: None,
59 }
60 }
61
62 pub fn generate_patch(&mut self, artifact: CompiledArtifact) -> RuntimePatch {
64 let mut patches = Vec::new();
65
66 if let Some(prev) = &self.previous_view {
67 self.diff_recursive(artifact.root_id, prev, &artifact.view, &mut patches);
68 } else {
69 patches.push(RuntimePatch::ReplaceView {
71 node_id: artifact.root_id,
72 new_view: artifact.view.clone(),
73 });
74 }
75
76 self.previous_view = Some(artifact.view);
77
78 if patches.len() == 1 {
79 patches.remove(0)
80 } else {
81 RuntimePatch::Batch(patches)
82 }
83 }
84
85 fn diff_recursive(&self, node_id: u64, old: &SerializedView, new: &SerializedView, patches: &mut Vec<RuntimePatch>) {
86 if old.view_type != new.view_type {
88 patches.push(RuntimePatch::ReplaceView {
89 node_id,
90 new_view: new.clone(),
91 });
92 return;
93 }
94
95 if old.props != new.props || old.children.len() != new.children.len() {
98 patches.push(RuntimePatch::ReplaceView {
99 node_id,
100 new_view: new.clone(),
101 });
102 return;
103 }
104
105 for (i, (old_child, new_child)) in old.children.iter().zip(new.children.iter()).enumerate() {
108 let child_id = node_id * 100 + (i as u64 + 1);
111 self.diff_recursive(child_id, old_child, new_child, patches);
112 }
113 }
114}