use serde_json::Value;
use crate::core::StoreError;
pub trait Upcaster: Send + Sync + std::fmt::Debug {
fn current_version(&self, kind: &str) -> u16;
fn upcast(&self, kind: &str, version: u16, payload: Value) -> Result<Value, StoreError>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Identity;
impl Upcaster for Identity {
fn current_version(&self, _kind: &str) -> u16 {
1
}
fn upcast(&self, kind: &str, version: u16, payload: Value) -> Result<Value, StoreError> {
match version {
1 => Ok(payload),
_ => Err(StoreError::UnknownRecordVersion {
kind: kind.to_owned(),
version,
reads: 1,
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn a_journal_from_an_older_cut_is_refused_rather_than_misread() {
let err = Identity
.upcast("RunAdmitted", 0, json!({ "agent": "a", "input": null }))
.unwrap_err();
assert!(
matches!(
err,
StoreError::UnknownRecordVersion {
version: 0,
reads: 1,
..
}
),
"a version this build does not read is not damage: {err:?}"
);
let text = err.to_string();
assert!(
text.contains("deploy readers before writers"),
"the refusal must say what to do about it: {text}"
);
}
#[test]
fn future_versions_are_refused_not_guessed() {
let err = Identity.upcast("EffectDone", 7, json!({})).unwrap_err();
assert!(matches!(
err,
StoreError::UnknownRecordVersion { version: 7, .. }
));
}
#[test]
fn current_version_passes_through() {
let v = Identity.current_version("EffectDone");
assert_eq!(
Identity.upcast("EffectDone", v, json!({"a": 1})).unwrap(),
json!({"a": 1})
);
}
#[test]
fn upcasting_is_pure() {
let v = Identity.current_version("StepStarted");
let a = Identity
.upcast("StepStarted", v, json!({"skill": "x"}))
.unwrap();
let b = Identity
.upcast("StepStarted", v, json!({"skill": "x"}))
.unwrap();
assert_eq!(a, b);
}
}