use batpak::event::{EventKind, EventPayload};
use serde::{Deserialize, Serialize};
use crate::operation::{EffectClass, OperationDescriptor};
use super::error::StoreRegisterCatalogError;
pub const SYNCBAT_REGISTER_EVENT_KIND: EventKind = EventKind::custom(0xC, 0x5B8);
pub(super) const REGISTER_SCHEMA_VERSION: u16 = 1;
pub(super) const REGISTER_ACTION_PUT: &str = "put";
pub(super) const REGISTER_ACTION_UPDATE: &str = "update";
pub(super) const REGISTER_ACTION_DELETE: &str = "delete";
pub(super) const REGISTER_ACTION_SUPERSEDE: &str = "supersede";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum RegisterOperationActionV1 {
Put,
Update,
Delete,
Supersede,
}
impl RegisterOperationActionV1 {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Put => REGISTER_ACTION_PUT,
Self::Update => REGISTER_ACTION_UPDATE,
Self::Delete => REGISTER_ACTION_DELETE,
Self::Supersede => REGISTER_ACTION_SUPERSEDE,
}
}
fn from_catalog_str(value: &str) -> Option<Self> {
match value {
REGISTER_ACTION_PUT => Some(Self::Put),
REGISTER_ACTION_UPDATE => Some(Self::Update),
REGISTER_ACTION_DELETE => Some(Self::Delete),
REGISTER_ACTION_SUPERSEDE => Some(Self::Supersede),
_ => None,
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct RegisterOperationRowV1 {
pub schema_version: u16,
pub action: String,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supersedes: Option<String>,
pub title: Option<String>,
pub effect: String,
pub input_schema_ref: String,
pub output_schema_ref: String,
pub receipt_kind: String,
}
impl EventPayload for RegisterOperationRowV1 {
const KIND: EventKind = SYNCBAT_REGISTER_EVENT_KIND;
}
impl RegisterOperationRowV1 {
#[must_use]
pub fn from_descriptor(descriptor: &OperationDescriptor) -> Self {
Self {
schema_version: REGISTER_SCHEMA_VERSION,
action: RegisterOperationActionV1::Put.as_str().to_owned(),
name: descriptor.name().to_owned(),
supersedes: None,
title: descriptor.title().map(str::to_owned),
effect: descriptor.effect.as_str().to_owned(),
input_schema_ref: descriptor.input_schema_ref().to_owned(),
output_schema_ref: descriptor.output_schema_ref().to_owned(),
receipt_kind: descriptor.receipt_kind().to_owned(),
}
}
#[must_use]
pub fn update(descriptor: &OperationDescriptor) -> Self {
let mut row = Self::from_descriptor(descriptor);
row.action = RegisterOperationActionV1::Update.as_str().to_owned();
row
}
#[must_use]
pub fn delete(name: impl Into<String>) -> Self {
Self {
schema_version: REGISTER_SCHEMA_VERSION,
action: RegisterOperationActionV1::Delete.as_str().to_owned(),
name: name.into(),
supersedes: None,
title: None,
effect: String::new(),
input_schema_ref: String::new(),
output_schema_ref: String::new(),
receipt_kind: String::new(),
}
}
#[must_use]
pub fn supersede(superseded_name: impl Into<String>, descriptor: &OperationDescriptor) -> Self {
let mut row = Self::from_descriptor(descriptor);
row.action = RegisterOperationActionV1::Supersede.as_str().to_owned();
row.supersedes = Some(superseded_name.into());
row
}
pub(super) fn action_kind(
&self,
) -> Result<RegisterOperationActionV1, StoreRegisterCatalogError> {
if self.schema_version != REGISTER_SCHEMA_VERSION {
return Err(StoreRegisterCatalogError::InvalidSchemaVersion {
version: self.schema_version,
});
}
RegisterOperationActionV1::from_catalog_str(&self.action).ok_or_else(|| {
StoreRegisterCatalogError::InvalidAction {
action: self.action.clone(),
}
})
}
pub(super) fn into_descriptor(self) -> Result<OperationDescriptor, StoreRegisterCatalogError> {
self.descriptor()
}
pub(super) fn descriptor(&self) -> Result<OperationDescriptor, StoreRegisterCatalogError> {
let effect = EffectClass::from_catalog_str(&self.effect).ok_or_else(|| {
StoreRegisterCatalogError::InvalidEffect {
effect: self.effect.clone(),
}
})?;
let mut descriptor = OperationDescriptor::owned(
self.name.clone(),
effect,
self.input_schema_ref.clone(),
self.output_schema_ref.clone(),
self.receipt_kind.clone(),
);
if let Some(title) = &self.title {
descriptor = descriptor.with_owned_title(title.clone());
}
descriptor
.validate()
.map_err(StoreRegisterCatalogError::InvalidDescriptor)?;
Ok(descriptor)
}
pub(super) fn descriptor_payload_is_empty(&self) -> bool {
self.title.is_none()
&& self.effect.is_empty()
&& self.input_schema_ref.is_empty()
&& self.output_schema_ref.is_empty()
&& self.receipt_kind.is_empty()
}
}