use std::convert::Infallible;
use crate::state::envelope::DefinitionId;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Never {}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum DecodeError {
#[error("no definition is declared for the id {id}")]
UnknownDefinition { id: DefinitionId },
#[error("{id} declares no implementation for version {version}")]
UndeclaredVersion { id: DefinitionId, version: u32 },
#[error("the payload did not decode as {id} version {version}")]
Malformed { id: DefinitionId, version: u32 },
}
pub trait Transaction<S> {
type Error;
fn check(&self, state: &S) -> Result<(), Self::Error>;
fn records(&self, _state: &S, _out: &mut super::record::Records) {}
fn apply(&self, state: &mut S);
}
pub trait TransactionCatalog<S>: Transaction<S> + super::record::CatalogCodec {
fn replay(&self, prepared: &super::record::Prepared, state: &mut S) -> Result<(), DecodeError> {
if self.identify() != (prepared.id.clone(), prepared.version) {
return Err(DecodeError::Malformed { id: prepared.id.clone(), version: prepared.version });
}
self.apply(state);
Ok(())
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` declares no inverse",
note = "a change answers the undo question where it is defined: `inverse = …` names the \
change that takes it back, `inverse = Self` says it is its own, and `irreversible` \
says it cannot be taken back — an irreversible change carries no invert body"
)]
pub trait Undoable {
type Inverse;
}
#[diagnostic::on_unimplemented(
message = "`{Self}` declares an inverse but never describes it",
label = "declared without a body",
note = "write the body beside the change: `impl harmos::state::Invert<{S}> for {Self} {{ fn \
invert(&self, before: &{S}) -> … }}`"
)]
pub trait Invert<S>: Undoable + Transaction<S> {
fn invert(&self, before: &S) -> Self::Inverse;
}
#[diagnostic::on_unimplemented(
message = "the declared inverse of `{T}` does not name `{T}` back",
note = "inverses come in pairs: when `{T}` declares `inverse = X`, `X` declares `inverse = \
{T}` — or `inverse = Self`, which is its own pair"
)]
pub trait Mutual<T> {}
#[diagnostic::on_unimplemented(
message = "the declared inverse `{Self}` is not a member of `{C}`",
note = "add `{Self}` to `{C}` as its own variant — a catalog never invents one"
)]
pub trait Catalogued<C>: Sized {
fn catalogued(self) -> C;
}
impl<S> TransactionCatalog<S> for Never {}
impl super::record::CatalogCodec for Never {
fn identify(&self) -> (DefinitionId, u32) {
match *self {}
}
fn encode<S: serde::Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
match *self {}
}
}
impl super::record::RecordCatalog for Never {
fn restore(_: &DefinitionId, _: u32, _: &serde_json::Value) -> Result<Option<Self>, serde_json::Error> {
Ok(None)
}
}
impl<S> Transaction<S> for Never {
type Error = Infallible;
fn check(&self, _state: &S) -> Result<(), Self::Error> {
match *self {}
}
fn apply(&self, _state: &mut S) {
match *self {}
}
}
impl<T> Mutual<T> for T {}
#[diagnostic::do_not_recommend]
impl<C, T: Into<C>> Catalogued<C> for T {
fn catalogued(self) -> C {
self.into()
}
}