use std::fmt;
use rpki::ca::idexchange::MyHandle;
use rpki::repository::x509::Time;
use serde::{Deserialize, Serialize};
use crate::api;
use crate::api::history::{CommandHistoryRecord, CommandSummary};
use crate::commons::actor::Actor;
use super::store::{AggregateStoreError, Storable};
pub trait Aggregate: Storable + 'static {
type InitCommand: InitCommand<
StorableDetails = Self::StorableCommandDetails,
>;
type Command: Command<StorableDetails = Self::StorableCommandDetails>;
type StorableCommandDetails: WithStorableDetails;
type InitEvent: InitEvent;
type Event: Event;
type Error: std::error::Error + Send + Sync + From<AggregateStoreError>;
fn init(handle: &MyHandle, event: Self::InitEvent) -> Self;
fn process_init_command(
command: Self::InitCommand,
) -> Result<Self::InitEvent, Self::Error>;
fn process_command(
&self,
command: Self::Command,
) -> Result<Vec<Self::Event>, Self::Error>;
fn version(&self) -> u64;
fn increment_version(&mut self);
fn apply(&mut self, event: Self::Event);
fn apply_command(&mut self, command: StoredCommand<Self>) {
self.increment_version();
if let Some(events) = command.into_events() {
for event in events {
self.apply(event);
}
}
}
}
pub trait InitEvent:
fmt::Display + Eq + PartialEq + Send + Sync + Storable + 'static
{
}
pub trait Event:
fmt::Display + Eq + PartialEq + Send + Sync + Storable + 'static
{
}
pub trait WithStorableDetails: Storable {
fn summary(&self) -> CommandSummary;
fn make_init() -> Self;
}
pub trait InitCommand: Clone {
type StorableDetails: WithStorableDetails;
fn handle(&self) -> &MyHandle;
fn actor(&self) -> &str;
fn store(&self) -> Self::StorableDetails;
}
#[derive(Clone, Debug)]
pub struct SentInitCommand<I> {
handle: MyHandle,
actor: String,
details: I,
}
impl<I> SentInitCommand<I> {
pub fn new(id: MyHandle, details: I, actor: &Actor) -> Self {
SentInitCommand {
handle: id,
details,
actor: actor.to_string(),
}
}
pub fn details(&self) -> &I {
&self.details
}
pub fn into_details(self) -> I {
self.details
}
}
impl<I: InitCommandDetails> InitCommand for SentInitCommand<I> {
type StorableDetails = I::StorableDetails;
fn handle(&self) -> &MyHandle {
&self.handle
}
fn actor(&self) -> &str {
&self.actor
}
fn store(&self) -> Self::StorableDetails {
self.details.store()
}
}
impl<I> fmt::Display for SentInitCommand<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "initialise '{}'", self.handle)
}
}
pub trait InitCommandDetails: Clone {
type StorableDetails: WithStorableDetails;
fn store(&self) -> Self::StorableDetails;
}
pub trait Command: Clone {
type StorableDetails: WithStorableDetails;
fn handle(&self) -> &MyHandle;
fn version(&self) -> Option<u64>;
fn actor(&self) -> &str;
fn store(&self) -> Self::StorableDetails;
}
#[derive(Clone)]
pub struct SentCommand<C> {
handle: MyHandle,
version: Option<u64>,
actor: String,
details: C,
}
impl<C> SentCommand<C> {
pub fn new(
id: MyHandle,
version: Option<u64>,
details: C,
actor: &Actor,
) -> Self {
SentCommand {
handle: id,
version,
details,
actor: actor.audit_name(),
}
}
pub fn into_details(self) -> C {
self.details
}
}
impl<C: CommandDetails> Command for SentCommand<C> {
type StorableDetails = C::StorableDetails;
fn handle(&self) -> &MyHandle {
&self.handle
}
fn version(&self) -> Option<u64> {
self.version
}
fn store(&self) -> Self::StorableDetails {
self.details.store()
}
fn actor(&self) -> &str {
&self.actor
}
}
impl<C: fmt::Display> fmt::Display for SentCommand<C> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let version_string = self
.version
.as_ref()
.map(u64::to_string)
.unwrap_or_else(|| "any".to_string());
write!(
f,
"id '{}' version '{}' details '{}'",
self.handle, version_string, self.details
)
}
}
pub trait CommandDetails: Clone {
type Event: Event;
type StorableDetails: WithStorableDetails;
fn store(&self) -> Self::StorableDetails;
}
pub struct StoredCommandBuilder<A: Aggregate> {
actor: String,
time: Time,
handle: MyHandle,
version: u64,
details: A::StorableCommandDetails,
}
impl<A: Aggregate> StoredCommandBuilder<A> {
pub fn new(
actor: String,
time: Time,
handle: MyHandle,
version: u64,
details: A::StorableCommandDetails,
) -> Self {
StoredCommandBuilder {
actor,
time,
handle,
version,
details,
}
}
pub fn finish_with_init_event(
self,
init_event: A::InitEvent,
) -> StoredCommand<A> {
self.finish_with_effect(StoredEffect::Init { init: init_event })
}
pub fn finish_with_events(
self,
events: Vec<A::Event>,
) -> StoredCommand<A> {
self.finish_with_effect(StoredEffect::Success { events })
}
pub fn finish_with_error(
self,
error: impl fmt::Display,
) -> StoredCommand<A> {
self.finish_with_effect(
StoredEffect::Error { msg: error.to_string() }
)
}
fn finish_with_effect(
self,
effect: StoredEffect<A::Event, A::InitEvent>,
) -> StoredCommand<A> {
StoredCommand::new(
self.actor,
self.time,
self.handle,
self.version,
self.details,
effect,
)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(bound(deserialize = "A::Event: Event"))]
pub struct StoredCommand<A: Aggregate> {
actor: String,
time: Time,
handle: MyHandle,
version: u64,
#[serde(deserialize_with = "A::StorableCommandDetails::deserialize")]
details: A::StorableCommandDetails,
effect: StoredEffect<A::Event, A::InitEvent>,
}
impl<A: Aggregate> StoredCommand<A> {
fn new(
actor: String,
time: Time,
handle: MyHandle,
version: u64,
details: A::StorableCommandDetails,
effect: StoredEffect<A::Event, A::InitEvent>,
) -> Self {
StoredCommand {
actor,
time,
handle,
version,
details,
effect,
}
}
pub fn builder(
actor: String,
time: Time,
handle: MyHandle,
version: u64,
details: A::StorableCommandDetails,
) -> StoredCommandBuilder<A> {
StoredCommandBuilder::new(actor, time, handle, version, details)
}
pub fn actor(&self) -> &str {
&self.actor
}
pub fn time(&self) -> Time {
self.time
}
pub fn handle(&self) -> &MyHandle {
&self.handle
}
pub fn version(&self) -> u64 {
self.version
}
pub fn details(&self) -> &A::StorableCommandDetails {
&self.details
}
pub fn effect(&self) -> &StoredEffect<A::Event, A::InitEvent> {
&self.effect
}
pub fn events(&self) -> Option<&Vec<A::Event>> {
match &self.effect {
StoredEffect::Error { .. } | StoredEffect::Init { .. } => None,
StoredEffect::Success { events } => Some(events),
}
}
pub fn into_events(self) -> Option<Vec<A::Event>> {
match self.effect {
StoredEffect::Error { .. } | StoredEffect::Init { .. } => None,
StoredEffect::Success { events } => Some(events),
}
}
pub fn into_init(self) -> Option<A::InitEvent> {
match self.effect {
StoredEffect::Init { init } => Some(init),
_ => None,
}
}
pub fn to_history_details(&self) -> api::history::CommandDetails
where
<A as Aggregate>::StorableCommandDetails: fmt::Display
{
api::history::CommandDetails {
actor: self.actor.clone(),
time: self.time,
handle: self.handle.clone(),
version: self.version,
msg: self.details.to_string(),
details: to_json_value(&self.details),
effect: self.effect.to_history_details(),
}
}
pub fn into_history_record(self) -> CommandHistoryRecord {
CommandHistoryRecord {
actor: self.actor,
timestamp: self.time.timestamp_millis(),
handle: self.handle,
version: self.version,
summary: self.details.summary(),
effect: self.effect.into(),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(
rename_all = "snake_case",
tag = "result",
bound(deserialize = "E: Event")
)]
pub enum StoredEffect<E: Event, I: InitEvent> {
Error { msg: String },
Success { events: Vec<E> },
Init { init: I },
}
impl<E: Event, I: InitEvent> StoredEffect<E, I> {
fn to_history_details(&self) -> api::history::CommandEffect {
match self {
Self::Error { msg } => {
api::history::CommandEffect::Error { msg: msg.clone() }
}
Self::Success { events } => {
api::history::CommandEffect::Success {
events: events.iter().map(|ev| {
api::history::CommandEffectEvent {
msg: ev.to_string(),
details: to_json_value(ev)
}
}).collect(),
}
}
Self::Init { init } => {
api::history::CommandEffect::Init {
init: api::history::CommandEffectEvent {
msg: init.to_string(),
details: to_json_value(init),
}
}
}
}
}
}
pub trait PreSaveEventListener<A: Aggregate>: Send + Sync + 'static {
fn listen(&self, agg: &A, events: &[A::Event]) -> Result<(), A::Error>;
}
pub trait PostSaveEventListener<A: Aggregate>: Send + Sync + 'static {
fn listen(&self, agg: &A, events: &[A::Event]);
}
fn to_json_value<T: serde::Serialize>(value: T) -> serde_json::Value {
serde_json::to_value(value).unwrap_or_else(|err| {
serde_json::json!({ "serialization_error": err.to_string() })
})
}