use serde::{Deserialize, Serialize};
pub const CONTRACT_API_VERSION: &str = "zebflow.com/v1";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ContractKind {
ProjectConfiguration,
Pipeline,
DependencyLock,
NodeDefinition,
NodeBundle,
FileRef,
ZebFsAcl,
DatabaseSchema,
ProjectBundle,
HubPackage,
HubRepositoryIndex,
RweLibraryManifest,
MapPublishManifest,
InvocationRecord,
RweSource,
Credential,
OfficeTopology,
}
impl ContractKind {
pub const fn as_str(self) -> &'static str {
match self {
Self::ProjectConfiguration => "ProjectConfiguration",
Self::Pipeline => "Pipeline",
Self::DependencyLock => "DependencyLock",
Self::NodeDefinition => "NodeDefinition",
Self::NodeBundle => "NodeBundle",
Self::FileRef => "FileRef",
Self::ZebFsAcl => "ZebFsAcl",
Self::DatabaseSchema => "DatabaseSchema",
Self::ProjectBundle => "ProjectBundle",
Self::HubPackage => "HubPackage",
Self::HubRepositoryIndex => "HubRepositoryIndex",
Self::RweLibraryManifest => "RweLibraryManifest",
Self::MapPublishManifest => "MapPublishManifest",
Self::InvocationRecord => "InvocationRecord",
Self::RweSource => "RweSource",
Self::Credential => "Credential",
Self::OfficeTopology => "OfficeTopology",
}
}
pub fn parse(value: &str) -> Option<Self> {
ALL_CONTRACT_DESCRIPTORS
.iter()
.find(|descriptor| descriptor.kind.as_str() == value)
.map(|descriptor| descriptor.kind)
}
}
impl std::fmt::Display for ContractKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy)]
pub struct ContractDescriptor {
pub kind: ContractKind,
pub owner: &'static str,
pub boundary: &'static str,
pub representation: ContractRepresentation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContractRepresentation {
Envelope,
InlinePayload,
DatabaseRecord,
SourceFile,
}
const ALL_CONTRACT_DESCRIPTORS: &[ContractDescriptor] = &[
envelope(ContractKind::ProjectConfiguration, "platform", "persisted"),
envelope(ContractKind::Pipeline, "pipeline", "persisted"),
envelope(ContractKind::DependencyLock, "platform", "persisted"),
envelope(ContractKind::NodeDefinition, "pipeline", "normalized"),
envelope(ContractKind::NodeBundle, "platform", "transferred"),
inline(ContractKind::FileRef, "pipeline", "payload"),
envelope(ContractKind::ZebFsAcl, "zebfs", "persisted"),
envelope(ContractKind::DatabaseSchema, "platform", "persisted"),
envelope(ContractKind::ProjectBundle, "platform", "transferred"),
envelope(ContractKind::HubPackage, "platform", "transferred"),
envelope(ContractKind::HubRepositoryIndex, "platform", "transferred"),
envelope(ContractKind::RweLibraryManifest, "rwe", "persisted"),
envelope(ContractKind::MapPublishManifest, "mapserver", "persisted"),
database_record(ContractKind::InvocationRecord, "pipeline", "persisted"),
source_file(ContractKind::RweSource, "rwe", "persisted"),
database_record(ContractKind::Credential, "platform", "persisted"),
envelope(ContractKind::OfficeTopology, "execution", "persisted"),
];
const fn descriptor(
kind: ContractKind,
owner: &'static str,
boundary: &'static str,
representation: ContractRepresentation,
) -> ContractDescriptor {
ContractDescriptor {
kind,
owner,
boundary,
representation,
}
}
const fn envelope(
kind: ContractKind,
owner: &'static str,
boundary: &'static str,
) -> ContractDescriptor {
descriptor(kind, owner, boundary, ContractRepresentation::Envelope)
}
const fn inline(
kind: ContractKind,
owner: &'static str,
boundary: &'static str,
) -> ContractDescriptor {
descriptor(kind, owner, boundary, ContractRepresentation::InlinePayload)
}
const fn database_record(
kind: ContractKind,
owner: &'static str,
boundary: &'static str,
) -> ContractDescriptor {
descriptor(
kind,
owner,
boundary,
ContractRepresentation::DatabaseRecord,
)
}
const fn source_file(
kind: ContractKind,
owner: &'static str,
boundary: &'static str,
) -> ContractDescriptor {
descriptor(kind, owner, boundary, ContractRepresentation::SourceFile)
}
pub fn contract_descriptor(kind: ContractKind) -> &'static ContractDescriptor {
ALL_CONTRACT_DESCRIPTORS
.iter()
.find(|descriptor| descriptor.kind == kind)
.expect("every ContractKind must be registered")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_kind_has_one_registry_entry_and_roundtrips() {
let mut kinds = std::collections::HashSet::new();
for descriptor in ALL_CONTRACT_DESCRIPTORS {
assert!(kinds.insert(descriptor.kind));
assert_eq!(
ContractKind::parse(descriptor.kind.as_str()),
Some(descriptor.kind)
);
assert!(!descriptor.owner.is_empty());
assert!(!descriptor.boundary.is_empty());
}
assert_eq!(kinds.len(), 17);
}
}