///|
/// A named alternative history forked from a journal boundary.
pub struct ReplayBranch[P] {
name : String
base_sequence : Int
journal : Journal[P]
}
///|
/// Forks a branch from the first `sequence` events of a source journal.
///
/// Returns `None` when the source is invalid or the boundary is out of range.
pub fn[P] ReplayBranch::fork(
name : String,
source : Journal[P],
sequence : Int,
payload_fingerprint : (P) -> String,
) -> ReplayBranch[P]? {
if sequence < 0 || sequence > source.length() {
return None
}
match source.validate(payload_fingerprint) {
Invalid(_) => None
Valid(_, _) =>
Some({ name, base_sequence: sequence, journal: source.prefix(sequence) })
}
}
///|
/// Returns the branch name.
pub fn[P] ReplayBranch::name(self : ReplayBranch[P]) -> String {
self.name
}
///|
/// Returns the source sequence where the branch was created.
pub fn[P] ReplayBranch::base_sequence(self : ReplayBranch[P]) -> Int {
self.base_sequence
}
///|
/// Returns a copy of the branch journal.
pub fn[P] ReplayBranch::journal(self : ReplayBranch[P]) -> Journal[P] {
Journal::from_events(self.journal.events())
}
///|
/// Appends one event to the alternative history.
pub fn[P] ReplayBranch::append(
self : ReplayBranch[P],
kind : String,
payload : P,
correlation_id : String,
payload_fingerprint : (P) -> String,
) -> JournalEvent[P] {
self.journal.append(kind, payload, correlation_id, payload_fingerprint)
}
///|
/// Locates the first structural difference between two event histories.
pub fn[P] compare_histories(
left : Journal[P],
right : Journal[P],
payload_fingerprint : (P) -> String,
) -> Divergence? {
match left.validate(payload_fingerprint) {
Invalid(_) =>
return Some({
sequence: 0,
kind: EventMismatch,
left_hash: left.tail_hash(),
right_hash: right.tail_hash(),
message: "left journal is invalid",
})
Valid(_, _) => ()
}
match right.validate(payload_fingerprint) {
Invalid(_) =>
return Some({
sequence: 0,
kind: EventMismatch,
left_hash: left.tail_hash(),
right_hash: right.tail_hash(),
message: "right journal is invalid",
})
Valid(_, _) => ()
}
let common = if left.length() < right.length() {
left.length()
} else {
right.length()
}
for index = 0; index < common; index = index + 1 {
let left_event = match left.get(index) {
Some(event) => event
None => abort("validated left event disappeared")
}
let right_event = match right.get(index) {
Some(event) => event
None => abort("validated right event disappeared")
}
if left_event.hash != right_event.hash {
return Some({
sequence: index + 1,
kind: EventMismatch,
left_hash: left_event.hash,
right_hash: right_event.hash,
message: "event histories diverge",
})
}
}
if left.length() != right.length() {
return Some({
sequence: common + 1,
kind: LengthMismatch,
left_hash: left.tail_hash(),
right_hash: right.tail_hash(),
message: "event histories have different lengths",
})
}
None
}