mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-24 18:28:11 +08:00
WIP prototype migrations
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{AttributesWrite, CrdtError, Delta, Rev, TimeStamp};
|
||||
use crate::{AttributesWrite, CrdtError, Delta, RegistryDelta, Rev, TimeStamp};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct History {
|
||||
@@ -178,3 +178,36 @@ impl History {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Recompute every delta's content-addressed `Rev` after delta payloads were rewritten, walking in
|
||||
/// topological order and remapping parent links (including `Merge` extra parents) along the way.
|
||||
/// Returns the old → new mapping for remapping cursors (`head`, redo stack, broadcast tip).
|
||||
pub fn rehash_deltas(deltas: &mut [Delta]) -> HashMap<Rev, Rev> {
|
||||
fn remap_merge_parents(kind: &mut RegistryDelta, mapping: &HashMap<Rev, Rev>) {
|
||||
if let RegistryDelta::Merge { extra_parents } = kind {
|
||||
for parent in extra_parents.iter_mut() {
|
||||
if let Some(new) = mapping.get(parent) {
|
||||
*parent = *new;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut mapping: HashMap<Rev, Rev> = HashMap::new();
|
||||
|
||||
for delta in deltas.iter_mut() {
|
||||
if let Some(parent) = delta.parent.as_mut()
|
||||
&& let Some(new) = mapping.get(parent)
|
||||
{
|
||||
*parent = *new;
|
||||
}
|
||||
remap_merge_parents(&mut delta.kind, &mapping);
|
||||
remap_merge_parents(&mut delta.reverse, &mapping);
|
||||
|
||||
let new_id = delta.recomputed_id();
|
||||
mapping.insert(delta.id, new_id);
|
||||
delta.id = new_id;
|
||||
}
|
||||
|
||||
mapping
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ pub mod to_runtime;
|
||||
pub use attributes::*;
|
||||
pub use crdt::*;
|
||||
pub use document::*;
|
||||
pub use history::History;
|
||||
pub use history::{History, rehash_deltas};
|
||||
pub use ids::*;
|
||||
pub use model::*;
|
||||
pub use registry::*;
|
||||
|
||||
@@ -11,6 +11,15 @@ pub struct Node {
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new(implementation: Implementation, inputs: Vec<InputSlot>, attributes: Attributes, network: NetworkId) -> Self {
|
||||
Self {
|
||||
implementation,
|
||||
inputs,
|
||||
attributes,
|
||||
network,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn implementation(&self) -> &Implementation {
|
||||
&self.implementation
|
||||
}
|
||||
@@ -24,6 +33,21 @@ impl Node {
|
||||
self.network
|
||||
}
|
||||
|
||||
// Mutable access for edits on a registry clone that is diffed back into deltas;
|
||||
// live-session edits go through the delta paths instead.
|
||||
pub fn implementation_mut(&mut self) -> &mut Implementation {
|
||||
&mut self.implementation
|
||||
}
|
||||
pub fn inputs_mut(&mut self) -> &mut Vec<InputSlot> {
|
||||
&mut self.inputs
|
||||
}
|
||||
pub fn attributes_mut(&mut self) -> &mut Attributes {
|
||||
&mut self.attributes
|
||||
}
|
||||
pub fn set_network(&mut self, network: NetworkId) {
|
||||
self.network = network;
|
||||
}
|
||||
|
||||
/// True if both nodes agree on every value-bearing field, ignoring slot/attribute timestamps.
|
||||
pub fn value_equal(&self, other: &Self) -> bool {
|
||||
if self.implementation != other.implementation || self.network != other.network {
|
||||
|
||||
@@ -45,7 +45,7 @@ impl<'de> Deserialize<'de> for ResourceEntry {
|
||||
}
|
||||
|
||||
let Raw { mut sources, hash, hash_timestamp } = Raw::deserialize(deserializer)?;
|
||||
sources.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
sources.sort_by_key(|(key, _)| *key);
|
||||
sources.dedup_by(|(later_key, later_value), (kept_key, kept_value)| {
|
||||
// `dedup_by` keeps the first of each run; sorting is stable, so resolve duplicates by LWW.
|
||||
if later_key != kept_key {
|
||||
|
||||
@@ -92,6 +92,27 @@ impl Session {
|
||||
Ok(conversion.network_ids)
|
||||
}
|
||||
|
||||
/// Diff the working registry against `target` and stage the difference as hot ops, so bulk
|
||||
/// registry mutations commit through the ordinary hot-op path without hand-built deltas.
|
||||
pub fn stage_registry_replace(&mut self, target: &Registry) -> Result<Vec<HotOp>, CrdtError> {
|
||||
let ops = crate::delta::compute_deltas(&self.document.working_registry, target);
|
||||
self.stage_ops(ops)
|
||||
}
|
||||
|
||||
/// Retire every pending hot op as one gesture. Convenience over [`Session::retire`] for callers
|
||||
/// that don't track hot-op timestamps themselves.
|
||||
pub fn retire_all(&mut self) -> Result<Vec<Rev>, CrdtError> {
|
||||
match self.document.hot_log.iter().map(|hot_op| hot_op.timestamp).max() {
|
||||
Some(up_to) => self.retire(up_to),
|
||||
None => Ok(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Mint a fresh peer-scoped `NodeId`. Forwards to [`Document::next_node_id`].
|
||||
pub fn next_node_id(&mut self) -> NodeId {
|
||||
self.document.next_node_id()
|
||||
}
|
||||
|
||||
/// Register a content-addressed resource as a single `DataSource::Embedded` source resolved to
|
||||
/// `hash`, staged as one `AddResource` hot op. The caller owns `id` allocation, persists the
|
||||
/// returned hot frame, retires, and persists the bytes into its byte store separately.
|
||||
|
||||
Reference in New Issue
Block a user