///|
priv struct Transaction[T] {
before : T
label : String
}
///|
pub fn[T] History::transaction_depth(self : History[T]) -> Int {
self.transactions.length()
}
///|
/// Nested transactions preview state, but only an outer commit creates history.
pub fn[T] History::begin(
self : History[T],
label : String,
) -> Result[Unit, String] {
if self.transactions.length() >= 64 {
return Err("transaction nesting limit is 64")
}
self.merge_allowed = false
self.transactions.push({ before: (self.copy)(self.live), label, })
Ok(())
}
///|
/// Returns whether the frame changed state. Empty/net-zero commits preserve redo.
pub fn[T] History::commit(self : History[T]) -> Result[Bool, String] {
let n = self.transactions.length()
if n == 0 {
return Err("no active transaction")
}
let frame = self.transactions[n - 1]
let changed = !(self.same)(frame.before, self.live)
if n == 1 && changed && self.next_id == 2147483647 {
return Err("revision identifier space exhausted")
}
ignore(self.transactions.pop())
if n > 1 {
return Ok(changed)
}
let after = (self.copy)(self.live)
self.live = (self.copy)(frame.before)
self.record(after, frame.label)
}
///|
/// Cancel only the innermost frame; committed timeline and future stay intact.
pub fn[T] History::rollback(self : History[T]) -> Result[Bool, String] {
match self.transactions.pop() {
None => Err("no active transaction")
Some(frame) => {
let changed = !(self.same)(self.live, frame.before)
self.live = (self.copy)(frame.before)
Ok(changed)
}
}
}