1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Transactional coordinate editing over immutable snapshots.
use super::{CoordinateStore, Structure, StructureData, validate};
use crate::diagnostic::{Code, Diagnostic};
use crate::index::ModelIndex;
use crate::{ExecutionContext, MemoryBudgetError};
/// A private coordinate copy that becomes a new structure only on commit.
///
/// The original structure remains immutable and every view into it stays valid.
/// Dropping an editor discards the transaction.
#[derive(Debug)]
pub struct CoordinateEditor {
base: Structure,
coords: CoordinateStore,
}
impl Structure {
/// Starts a scoped coordinate edit under the shared execution account.
///
/// Coordinate storage is copied for the new snapshot; topology and atom
/// annotations remain untouched. Call [`CoordinateEditor::commit`] to
/// validate and publish the new snapshot.
/// Shared topology remains borrowed. Direct coordinate frames detach under
/// the supplied account, and their reservations follow the copied backing
/// buffers through committed snapshots and clones.
///
/// # Errors
///
/// Returns before publishing an editor when all coordinate copies cannot fit.
pub fn edit_coordinates(
&self,
context: &ExecutionContext,
) -> Result<CoordinateEditor, MemoryBudgetError> {
let mut coords = self.data().coords.clone();
coords.make_unique_in(context)?;
Ok(CoordinateEditor {
base: self.clone(),
coords,
})
}
}
impl CoordinateEditor {
/// Mutable positions of one dense model.
///
/// Ragged models are independent structures and must be edited through the
/// corresponding value returned by [`Structure::ragged_models`].
pub fn positions_mut(&mut self, model: ModelIndex) -> Option<&mut [[f32; 3]]> {
match &mut self.coords {
CoordinateStore::Single(block) if model.get() == 0 => Some(block.as_mut_slice()),
CoordinateStore::Dense { frames } => frames
.get_mut(model.as_usize())
.map(crate::coords::CoordinateBlock::as_mut_slice),
CoordinateStore::Single(_) | CoordinateStore::Ragged { .. } => None,
}
}
/// Mutable positions of one dense model, with a diagnostic on failure.
///
/// This is the fallible form for callers that must preserve molframe's
/// diagnostic contract across an FFI boundary.
///
/// # Errors
///
/// Returns `E6003` when `model` is not a dense frame of this structure.
pub fn try_positions_mut(&mut self, model: ModelIndex) -> Result<&mut [[f32; 3]], Diagnostic> {
self.positions_mut(model)
.ok_or_else(|| Diagnostic::new(Code::E6003))
}
/// Validates the transaction and returns a new immutable snapshot.
///
/// # Errors
///
/// Returns every violated structural invariant. The original snapshot is
/// unaffected whether the commit succeeds or fails.
pub fn commit(self) -> Result<Structure, Vec<crate::diagnostic::Diagnostic>> {
let mut data: StructureData = self.base.data().clone();
data.coords = self.coords;
finish(data)
}
/// Builds a validated snapshot that shares the editor's coordinate buffer.
///
/// Nothing is copied, so the snapshot and the editor see the same
/// coordinates: use it only when the editor is not written to afterwards.
/// A caller that keeps a writable view of the buffer must use
/// [`Self::snapshot_detached`].
///
/// # Errors
///
/// Returns every violated structural invariant.
pub fn snapshot(&self) -> Result<Structure, Vec<crate::diagnostic::Diagnostic>> {
let mut data: StructureData = self.base.data().clone();
data.coords = self.coords.clone();
finish(data)
}
}
impl CoordinateEditor {
/// Builds a validated snapshot with a coordinate buffer of its own.
///
/// The copy is charged to `context`'s memory budget before anything is
/// allocated. Writing to the editor, or to a foreign view of its buffer,
/// afterwards cannot change the published structure.
///
/// # Errors
///
/// Returns a memory diagnostic when the copy does not fit the budget, or
/// every violated structural invariant.
pub fn snapshot_detached(
&self,
context: &ExecutionContext,
) -> Result<Structure, Vec<crate::diagnostic::Diagnostic>> {
let mut coords = self.coords.clone();
coords
.make_unique_in(context)
.map_err(|error| vec![crate::diagnostic::Diagnostic::from(error)])?;
let mut data: StructureData = self.base.data().clone();
data.coords = coords;
finish(data)
}
}
fn finish(mut data: StructureData) -> Result<Structure, Vec<crate::diagnostic::Diagnostic>> {
let Some(generation) = data.generation.next() else {
return Err(vec![
Diagnostic::new(Code::E6003).with_context("coordinate_generation", "exhausted"),
]);
};
data.generation = generation;
let findings = validate(&data);
if findings.is_empty() {
Ok(Structure::new(data))
} else {
Err(findings)
}
}
#[cfg(test)]
#[path = "edit_tests.rs"]
mod tests;