use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompiledArtifact {
pub root_id: u64,
pub view: SerializedView,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerializedView {
pub view_type: String,
pub props: serde_json::Value,
pub children: Vec<SerializedView>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RuntimePatch {
ReplaceView {
node_id: u64,
new_view: SerializedView,
},
UpdateState {
node_id: u64,
field: String,
value: serde_json::Value,
},
Batch(Vec<RuntimePatch>),
}
pub struct PatchEngine {
previous_view: Option<SerializedView>,
}
impl PatchEngine {
pub fn new() -> Self {
Self {
previous_view: None,
}
}
pub fn generate_patch(&mut self, artifact: CompiledArtifact) -> RuntimePatch {
let mut patches = Vec::new();
if let Some(prev) = &self.previous_view {
self.diff_recursive(artifact.root_id, prev, &artifact.view, &mut patches);
} else {
patches.push(RuntimePatch::ReplaceView {
node_id: artifact.root_id,
new_view: artifact.view.clone(),
});
}
self.previous_view = Some(artifact.view);
if patches.len() == 1 {
patches.remove(0)
} else {
RuntimePatch::Batch(patches)
}
}
fn diff_recursive(&self, node_id: u64, old: &SerializedView, new: &SerializedView, patches: &mut Vec<RuntimePatch>) {
if old.view_type != new.view_type {
patches.push(RuntimePatch::ReplaceView {
node_id,
new_view: new.clone(),
});
return;
}
if old.props != new.props || old.children.len() != new.children.len() {
patches.push(RuntimePatch::ReplaceView {
node_id,
new_view: new.clone(),
});
return;
}
for (i, (old_child, new_child)) in old.children.iter().zip(new.children.iter()).enumerate() {
let child_id = node_id * 100 + (i as u64 + 1);
self.diff_recursive(child_id, old_child, new_child, patches);
}
}
}