Preserve exchange allocations during atomic replacement and reserve generated aliases
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use crate::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeSet;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Clone, Error, Serialize, Deserialize)]
|
||||
@@ -135,7 +136,9 @@ impl Session {
|
||||
}
|
||||
pub fn execute(&mut self, command: Command) -> Result<Option<Id>, ModelError> {
|
||||
let mut candidate = self.project.clone();
|
||||
let selected = apply_command(&mut candidate, &command, 0)?;
|
||||
let mut pending_deletions = BTreeSet::new();
|
||||
let selected = apply_command(&mut candidate, &command, 0, &mut pending_deletions)?;
|
||||
crate::references::finish_deletions(&mut candidate, &pending_deletions);
|
||||
validate_candidate(&candidate)?;
|
||||
if candidate != self.project {
|
||||
self.past
|
||||
@@ -161,9 +164,11 @@ pub fn validate_candidate(p: &Project) -> Result<(), ModelError> {
|
||||
}
|
||||
pub fn execute_batch(project: &Project, commands: &[Command]) -> Result<Project, ModelError> {
|
||||
let mut candidate = project.clone();
|
||||
let mut pending_deletions = BTreeSet::new();
|
||||
for command in commands {
|
||||
apply_command(&mut candidate, command, 0)?;
|
||||
apply_command(&mut candidate, command, 0, &mut pending_deletions)?;
|
||||
}
|
||||
crate::references::finish_deletions(&mut candidate, &pending_deletions);
|
||||
validate_candidate(&candidate)?;
|
||||
Ok(candidate)
|
||||
}
|
||||
@@ -224,7 +229,12 @@ fn attach(p: &mut Project, id: Id) {
|
||||
}
|
||||
}
|
||||
}
|
||||
fn apply_command(p: &mut Project, c: &Command, depth: usize) -> Result<Option<Id>, ModelError> {
|
||||
fn apply_command(
|
||||
p: &mut Project,
|
||||
c: &Command,
|
||||
depth: usize,
|
||||
pending_deletions: &mut BTreeSet<Id>,
|
||||
) -> Result<Option<Id>, ModelError> {
|
||||
if depth > 64 {
|
||||
return Err(ModelError(
|
||||
"Command batch nesting exceeds 64 levels.".into(),
|
||||
@@ -233,12 +243,13 @@ fn apply_command(p: &mut Project, c: &Command, depth: usize) -> Result<Option<Id
|
||||
let selected = match c {
|
||||
Command::Replace(value) => {
|
||||
*p = value.clone();
|
||||
pending_deletions.clear();
|
||||
None
|
||||
}
|
||||
Command::Batch(commands) => {
|
||||
let mut selected = None;
|
||||
for command in commands {
|
||||
let id = apply_command(p, command, depth + 1)?;
|
||||
let id = apply_command(p, command, depth + 1, pending_deletions)?;
|
||||
if id.is_some() {
|
||||
selected = id;
|
||||
}
|
||||
@@ -318,6 +329,9 @@ fn apply_command(p: &mut Project, c: &Command, depth: usize) -> Result<Option<Id
|
||||
v.id = *old;
|
||||
p.relations.insert(*old, v);
|
||||
reidentify(p, *old, value.id)?;
|
||||
if pending_deletions.remove(old) {
|
||||
pending_deletions.insert(value.id);
|
||||
}
|
||||
Some(value.id)
|
||||
}
|
||||
Command::CreateDiagram(value) => {
|
||||
@@ -438,6 +452,9 @@ fn apply_command(p: &mut Project, c: &Command, depth: usize) -> Result<Option<Id
|
||||
}
|
||||
Command::Reidentify(old, new) => {
|
||||
reidentify(p, *old, *new)?;
|
||||
if pending_deletions.remove(old) {
|
||||
pending_deletions.insert(*new);
|
||||
}
|
||||
Some(*new)
|
||||
}
|
||||
Command::Rename(id, value) => {
|
||||
@@ -469,7 +486,7 @@ fn apply_command(p: &mut Project, c: &Command, depth: usize) -> Result<Option<Id
|
||||
items: vec![],
|
||||
},
|
||||
)?;
|
||||
return apply_command(p, &Command::Batch(commands), depth + 1);
|
||||
return apply_command(p, &Command::Batch(commands), depth + 1, pending_deletions);
|
||||
}
|
||||
Some(p.relate(*kind, name, *source, *target))
|
||||
}
|
||||
@@ -480,7 +497,7 @@ fn apply_command(p: &mut Project, c: &Command, depth: usize) -> Result<Option<Id
|
||||
if !p.contains(*id) {
|
||||
return Err(missing());
|
||||
}
|
||||
crate::references::delete_cascade(p, *id);
|
||||
crate::references::delete_cascade_deferred(p, *id, pending_deletions);
|
||||
None
|
||||
}
|
||||
Command::AddStep(owner, exchange) => {
|
||||
@@ -519,7 +536,7 @@ fn apply_command(p: &mut Project, c: &Command, depth: usize) -> Result<Option<Id
|
||||
.and_then(Element::steps)
|
||||
.and_then(|s| s.get(*index))
|
||||
.ok_or_else(|| ModelError("Step index is out of range.".into()))?;
|
||||
crate::references::delete_cascade(p, id);
|
||||
crate::references::delete_cascade_deferred(p, id, pending_deletions);
|
||||
Some(*owner)
|
||||
}
|
||||
Command::MoveStep(owner, from, to) => {
|
||||
|
||||
Reference in New Issue
Block a user