///|
fn text(value : String) -> String {
value
}
///|
fn int_text(value : Int) -> String {
"\{value}"
}
///|
fn amount_reducer(
state : Int,
event : @replay.JournalEvent[String],
) -> @replay.Transition[Int] {
match event.payload {
"add-5" => @replay.Transition::Accepted(state + 5)
"subtract-4" => @replay.Transition::Accepted(state - 4)
"double" => @replay.Transition::Accepted(state * 2)
"cancel" => @replay.Transition::Accepted(0)
_ => @replay.Transition::Rejected("unknown command")
}
}
///|
fn upgraded_reducer(
state : Int,
event : @replay.JournalEvent[String],
) -> @replay.Transition[Int] {
match event.payload {
"double" => @replay.Transition::Accepted(state * 3)
_ => amount_reducer(state, event)
}
}
///|
fn state_json(value : Int) -> String {
"\{value}"
}
///|
fn payload_json(value : String) -> String {
"\"\{@replay.json_escape(value)}\""
}
///|
fn main {
let journal : @replay.Journal[String] = @replay.Journal::new()
ignore(journal.append("Balance", "add-5", "demo-1", text))
ignore(journal.append("Balance", "double", "demo-1", text))
ignore(journal.append("Balance", "subtract-4", "demo-1", text))
println("journal=" + @replay.journal_to_json(journal, payload_json))
let full = @replay.replay(journal, 0, text, int_text, amount_reducer)
println("full_replay=" + @replay.replay_report_to_json(full, state_json))
println(
"deterministic=\{@replay.verify_determinism(journal, 0, text, int_text, amount_reducer)}",
)
match @replay.build_replay_evidence(journal, full, text) {
Some(tree) => {
println("evidence=" + @replay.replay_evidence_to_json(tree))
match @replay.replay_inclusion_proof(journal, full, tree, 2) {
Some(proof) =>
println(
"proof={\"sequence\":\{proof.sequence},\"depth\":\{proof.siblings.length()},\"verified\":\{@replay.verify_replay_inclusion(proof)}}",
)
None => println("proof=unavailable")
}
}
None => println("evidence=unavailable")
}
let prefix_report = @replay.replay(
journal.prefix(2),
0,
text,
int_text,
amount_reducer,
)
match @replay.checkpoint_from_report(prefix_report) {
Some(checkpoint) => {
let resumed = @replay.replay_from_checkpoint(
journal, checkpoint, text, int_text, amount_reducer,
)
println(
"checkpoint_replay=" +
@replay.replay_report_to_json(resumed, state_json),
)
}
None => println("checkpoint_replay=unavailable")
}
match @replay.ReplayBranch::fork("cancel-path", journal, 2, text) {
Some(branch) => {
ignore(branch.append("Balance", "cancel", "demo-1", text))
match @replay.compare_histories(journal, branch.journal(), text) {
Some(divergence) =>
println(
"branch_divergence=sequence:\{divergence.sequence},kind:\{@replay.divergence_kind_name(divergence.kind)}",
)
None => println("branch_divergence=none")
}
}
None => println("branch=invalid")
}
let migration = @replay.compare_reducer_versions(
journal, 0, text, int_text, amount_reducer, upgraded_reducer,
)
match migration.first_divergence {
Some(divergence) =>
println(
"migration=changed_at:\{divergence.sequence},old:\{migration.old_report.final_state},new:\{migration.new_report.final_state}",
)
None => println("migration=compatible")
}
let failing : @replay.Journal[String] = @replay.Journal::new()
ignore(failing.append("Balance", "add-5", "demo-2", text))
ignore(failing.append("Balance", "subtract-4", "demo-2", text))
ignore(failing.append("Balance", "subtract-4", "demo-2", text))
let invariant = fn(value : Int) -> String? {
if value < 0 {
Some("balance became negative")
} else {
None
}
}
match
@replay.find_first_invariant_failure(
failing, 0, text, int_text, amount_reducer, invariant,
) {
@replay.InvariantResult::Violated(sequence, _, reason) =>
println("invariant=failed_at:\{sequence},reason:\{reason}")
_ => println("invariant=holds")
}
}