rhi

Coordinated trade for connected markets
git clone https://radroots.dev/git/rhi.git
Log | Files | Refs | README | LICENSE

state_repository.rs (17379B)


      1 //! Sealed typed capability topology for RHI-owned state repositories.
      2 
      3 use core::fmt;
      4 
      5 use crate::RhiStateHost;
      6 
      7 /// Exact version of the RHI state-repository topology contract.
      8 pub const RHI_STATE_REPOSITORY_CONTRACT_VERSION: u32 = 1;
      9 
     10 /// Number of distinct typed repository capabilities in the v1 topology.
     11 pub const RHI_STATE_REPOSITORY_COUNT: usize = 21;
     12 
     13 /// Closed mutation class for one governed repository.
     14 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
     15 pub enum RhiStateRepositoryWriteClass {
     16     AppendOnly,
     17     CompareAndSwap,
     18     Immutable,
     19 }
     20 
     21 impl RhiStateRepositoryWriteClass {
     22     /// Returns the exact machine-contract spelling.
     23     #[must_use]
     24     pub const fn code(self) -> &'static str {
     25         match self {
     26             Self::AppendOnly => "append_only",
     27             Self::CompareAndSwap => "compare_and_swap",
     28             Self::Immutable => "immutable",
     29         }
     30     }
     31 }
     32 
     33 /// Closed inventory of RHI repository responsibilities.
     34 #[repr(u8)]
     35 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
     36 pub enum RhiStateRepositoryKind {
     37     Source,
     38     SourceCursor,
     39     SourceCompletion,
     40     SignedEvent,
     41     Mutation,
     42     Provenance,
     43     DirtyTrade,
     44     ReconciliationJob,
     45     ReconciliationAttempt,
     46     EvidenceManifest,
     47     Projection,
     48     Report,
     49     Supersession,
     50     SignedAttestationEvent,
     51     PublicationOutbox,
     52     PublicationTarget,
     53     PublicationAttempt,
     54     DesiredPresence,
     55     PresenceOutbox,
     56     PresenceTarget,
     57     PresenceAttempt,
     58 }
     59 
     60 impl RhiStateRepositoryKind {
     61     /// Returns the exact machine-contract spelling.
     62     #[must_use]
     63     pub const fn code(self) -> &'static str {
     64         descriptor(self).code
     65     }
     66 
     67     /// Returns the sole governed backing-table identity.
     68     #[must_use]
     69     pub const fn backing_table(self) -> &'static str {
     70         descriptor(self).backing_table
     71     }
     72 
     73     /// Returns the closed mutation class.
     74     #[must_use]
     75     pub const fn write_class(self) -> RhiStateRepositoryWriteClass {
     76         descriptor(self).write_class
     77     }
     78 }
     79 
     80 /// Immutable description of one repository capability.
     81 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
     82 pub struct RhiStateRepositoryDescriptor {
     83     kind: RhiStateRepositoryKind,
     84     code: &'static str,
     85     backing_table: &'static str,
     86     write_class: RhiStateRepositoryWriteClass,
     87 }
     88 
     89 impl RhiStateRepositoryDescriptor {
     90     const fn new(
     91         kind: RhiStateRepositoryKind,
     92         code: &'static str,
     93         backing_table: &'static str,
     94         write_class: RhiStateRepositoryWriteClass,
     95     ) -> Self {
     96         Self {
     97             kind,
     98             code,
     99             backing_table,
    100             write_class,
    101         }
    102     }
    103 
    104     /// Returns the closed repository kind.
    105     #[must_use]
    106     pub const fn kind(self) -> RhiStateRepositoryKind {
    107         self.kind
    108     }
    109 
    110     /// Returns the exact machine-contract spelling.
    111     #[must_use]
    112     pub const fn code(self) -> &'static str {
    113         self.code
    114     }
    115 
    116     /// Returns the exact governed backing-table identity.
    117     #[must_use]
    118     pub const fn backing_table(self) -> &'static str {
    119         self.backing_table
    120     }
    121 
    122     /// Returns the repository mutation class.
    123     #[must_use]
    124     pub const fn write_class(self) -> RhiStateRepositoryWriteClass {
    125         self.write_class
    126     }
    127 }
    128 
    129 impl fmt::Debug for RhiStateRepositoryDescriptor {
    130     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    131         formatter
    132             .debug_struct("RhiStateRepositoryDescriptor")
    133             .field("kind", &self.kind)
    134             .field("write_class", &self.write_class)
    135             .finish()
    136     }
    137 }
    138 
    139 use RhiStateRepositoryKind as Kind;
    140 use RhiStateRepositoryWriteClass as Write;
    141 
    142 const DESCRIPTORS: [RhiStateRepositoryDescriptor; RHI_STATE_REPOSITORY_COUNT] = [
    143     RhiStateRepositoryDescriptor::new(
    144         Kind::Source,
    145         "source",
    146         "evidence_reconciliation_sources",
    147         Write::AppendOnly,
    148     ),
    149     RhiStateRepositoryDescriptor::new(
    150         Kind::SourceCursor,
    151         "source_cursor",
    152         "relay_checkpoints",
    153         Write::CompareAndSwap,
    154     ),
    155     RhiStateRepositoryDescriptor::new(
    156         Kind::SourceCompletion,
    157         "source_completion",
    158         "evidence_reconciliation_sources",
    159         Write::AppendOnly,
    160     ),
    161     RhiStateRepositoryDescriptor::new(
    162         Kind::SignedEvent,
    163         "signed_event",
    164         "nostr_events",
    165         Write::AppendOnly,
    166     ),
    167     RhiStateRepositoryDescriptor::new(
    168         Kind::Mutation,
    169         "mutation",
    170         "trade_mutations",
    171         Write::AppendOnly,
    172     ),
    173     RhiStateRepositoryDescriptor::new(
    174         Kind::Provenance,
    175         "provenance",
    176         "relay_observations",
    177         Write::AppendOnly,
    178     ),
    179     RhiStateRepositoryDescriptor::new(
    180         Kind::DirtyTrade,
    181         "dirty_trade",
    182         "trade_dirty_generations",
    183         Write::CompareAndSwap,
    184     ),
    185     RhiStateRepositoryDescriptor::new(
    186         Kind::ReconciliationJob,
    187         "reconciliation_job",
    188         "reconciliation_jobs",
    189         Write::CompareAndSwap,
    190     ),
    191     RhiStateRepositoryDescriptor::new(
    192         Kind::ReconciliationAttempt,
    193         "reconciliation_attempt",
    194         "evidence_reconciliations",
    195         Write::AppendOnly,
    196     ),
    197     RhiStateRepositoryDescriptor::new(
    198         Kind::EvidenceManifest,
    199         "evidence_manifest",
    200         "evidence_manifests",
    201         Write::Immutable,
    202     ),
    203     RhiStateRepositoryDescriptor::new(
    204         Kind::Projection,
    205         "projection",
    206         "trade_projections",
    207         Write::Immutable,
    208     ),
    209     RhiStateRepositoryDescriptor::new(
    210         Kind::Report,
    211         "report",
    212         "attestation_reports",
    213         Write::Immutable,
    214     ),
    215     RhiStateRepositoryDescriptor::new(
    216         Kind::Supersession,
    217         "supersession",
    218         "attestation_reports",
    219         Write::AppendOnly,
    220     ),
    221     RhiStateRepositoryDescriptor::new(
    222         Kind::SignedAttestationEvent,
    223         "signed_attestation_event",
    224         "signed_attestation_events",
    225         Write::Immutable,
    226     ),
    227     RhiStateRepositoryDescriptor::new(
    228         Kind::PublicationOutbox,
    229         "publication_outbox",
    230         "publication_outbox",
    231         Write::CompareAndSwap,
    232     ),
    233     RhiStateRepositoryDescriptor::new(
    234         Kind::PublicationTarget,
    235         "publication_target",
    236         "publication_targets",
    237         Write::CompareAndSwap,
    238     ),
    239     RhiStateRepositoryDescriptor::new(
    240         Kind::PublicationAttempt,
    241         "publication_attempt",
    242         "publication_attempts",
    243         Write::AppendOnly,
    244     ),
    245     RhiStateRepositoryDescriptor::new(
    246         Kind::DesiredPresence,
    247         "desired_presence",
    248         "presence_desired_state",
    249         Write::CompareAndSwap,
    250     ),
    251     RhiStateRepositoryDescriptor::new(
    252         Kind::PresenceOutbox,
    253         "presence_outbox",
    254         "presence_outbox",
    255         Write::CompareAndSwap,
    256     ),
    257     RhiStateRepositoryDescriptor::new(
    258         Kind::PresenceTarget,
    259         "presence_target",
    260         "presence_targets",
    261         Write::CompareAndSwap,
    262     ),
    263     RhiStateRepositoryDescriptor::new(
    264         Kind::PresenceAttempt,
    265         "presence_attempt",
    266         "presence_attempts",
    267         Write::AppendOnly,
    268     ),
    269 ];
    270 
    271 const fn descriptor(kind: RhiStateRepositoryKind) -> RhiStateRepositoryDescriptor {
    272     DESCRIPTORS[kind as usize]
    273 }
    274 
    275 /// Returns the exact ordered v1 repository topology.
    276 #[must_use]
    277 pub const fn rhi_state_repository_descriptors()
    278 -> &'static [RhiStateRepositoryDescriptor; RHI_STATE_REPOSITORY_COUNT] {
    279     &DESCRIPTORS
    280 }
    281 
    282 /// Sealed repository family bound to one already-opened RHI state host.
    283 ///
    284 /// Construction is available only through [`RhiStateHost::repositories`]:
    285 ///
    286 /// ```compile_fail
    287 /// use rhi::RhiStateRepositories;
    288 ///
    289 /// let _ = RhiStateRepositories { host: todo!() };
    290 /// ```
    291 ///
    292 /// Distinct repository responsibilities cannot be interchanged:
    293 ///
    294 /// ```compile_fail
    295 /// use rhi::{RhiMutationRepository, RhiStateRepositories};
    296 ///
    297 /// fn wrong(repository: &RhiStateRepositories<'_>) {
    298 ///     let _: RhiMutationRepository<'_> = repository.sources();
    299 /// }
    300 /// ```
    301 pub struct RhiStateRepositories<'host> {
    302     host: &'host RhiStateHost,
    303 }
    304 
    305 impl<'host> RhiStateRepositories<'host> {
    306     pub(crate) const fn new(host: &'host RhiStateHost) -> Self {
    307         Self { host }
    308     }
    309 
    310     pub(crate) const fn host(&self) -> &'host RhiStateHost {
    311         self.host
    312     }
    313 
    314     /// Returns typed source-result access.
    315     #[must_use]
    316     pub const fn sources(&self) -> RhiSourceRepository<'host> {
    317         RhiSourceRepository { host: self.host }
    318     }
    319 
    320     /// Returns typed source-cursor access.
    321     #[must_use]
    322     pub const fn source_cursors(&self) -> RhiSourceCursorRepository<'host> {
    323         RhiSourceCursorRepository { host: self.host }
    324     }
    325 
    326     /// Returns typed source-completion access.
    327     #[must_use]
    328     pub const fn source_completions(&self) -> RhiSourceCompletionRepository<'host> {
    329         RhiSourceCompletionRepository { host: self.host }
    330     }
    331 
    332     /// Returns typed admitted-event access.
    333     #[must_use]
    334     pub const fn signed_events(&self) -> RhiSignedEventRepository<'host> {
    335         RhiSignedEventRepository { host: self.host }
    336     }
    337 
    338     /// Returns typed canonical-mutation access.
    339     #[must_use]
    340     pub const fn mutations(&self) -> RhiMutationRepository<'host> {
    341         RhiMutationRepository { host: self.host }
    342     }
    343 
    344     /// Returns typed provenance-observation access.
    345     #[must_use]
    346     pub const fn provenance(&self) -> RhiProvenanceRepository<'host> {
    347         RhiProvenanceRepository { host: self.host }
    348     }
    349 
    350     /// Returns typed dirty-trade generation access.
    351     #[must_use]
    352     pub const fn dirty_trades(&self) -> RhiDirtyTradeRepository<'host> {
    353         RhiDirtyTradeRepository { host: self.host }
    354     }
    355 
    356     /// Returns typed reconciliation-job access.
    357     #[must_use]
    358     pub const fn reconciliation_jobs(&self) -> RhiReconciliationJobRepository<'host> {
    359         RhiReconciliationJobRepository { host: self.host }
    360     }
    361 
    362     /// Returns typed reconciliation-attempt access.
    363     #[must_use]
    364     pub const fn reconciliation_attempts(&self) -> RhiReconciliationAttemptRepository<'host> {
    365         RhiReconciliationAttemptRepository { host: self.host }
    366     }
    367 
    368     /// Returns typed immutable evidence-manifest access.
    369     #[must_use]
    370     pub const fn evidence_manifests(&self) -> RhiEvidenceManifestRepository<'host> {
    371         RhiEvidenceManifestRepository { host: self.host }
    372     }
    373 
    374     /// Returns typed immutable projection access.
    375     #[must_use]
    376     pub const fn projections(&self) -> RhiProjectionRepository<'host> {
    377         RhiProjectionRepository { host: self.host }
    378     }
    379 
    380     /// Returns typed immutable report access.
    381     #[must_use]
    382     pub const fn reports(&self) -> RhiReportRepository<'host> {
    383         RhiReportRepository { host: self.host }
    384     }
    385 
    386     /// Returns typed append-only supersession access.
    387     #[must_use]
    388     pub const fn supersessions(&self) -> RhiSupersessionRepository<'host> {
    389         RhiSupersessionRepository { host: self.host }
    390     }
    391 
    392     /// Returns typed immutable signed-attestation-event access.
    393     #[must_use]
    394     pub const fn signed_attestation_events(&self) -> RhiSignedAttestationEventRepository<'host> {
    395         RhiSignedAttestationEventRepository { host: self.host }
    396     }
    397 
    398     /// Returns typed publication-outbox access.
    399     #[must_use]
    400     pub const fn publication_outbox(&self) -> RhiPublicationOutboxRepository<'host> {
    401         RhiPublicationOutboxRepository { host: self.host }
    402     }
    403 
    404     /// Returns typed immutable-target workflow access.
    405     #[must_use]
    406     pub const fn publication_targets(&self) -> RhiPublicationTargetRepository<'host> {
    407         RhiPublicationTargetRepository { host: self.host }
    408     }
    409 
    410     /// Returns typed append-only publication-attempt access.
    411     #[must_use]
    412     pub const fn publication_attempts(&self) -> RhiPublicationAttemptRepository<'host> {
    413         RhiPublicationAttemptRepository { host: self.host }
    414     }
    415 
    416     /// Returns typed desired-presence access.
    417     #[must_use]
    418     pub const fn desired_presence(&self) -> RhiDesiredPresenceRepository<'host> {
    419         RhiDesiredPresenceRepository { host: self.host }
    420     }
    421 
    422     /// Returns typed exact-byte presence-outbox access.
    423     #[must_use]
    424     pub const fn presence_outbox(&self) -> RhiPresenceOutboxRepository<'host> {
    425         RhiPresenceOutboxRepository { host: self.host }
    426     }
    427 
    428     /// Returns typed durable presence-target access.
    429     #[must_use]
    430     pub const fn presence_targets(&self) -> RhiPresenceTargetRepository<'host> {
    431         RhiPresenceTargetRepository { host: self.host }
    432     }
    433 
    434     /// Returns typed append-only presence-attempt access.
    435     #[must_use]
    436     pub const fn presence_attempts(&self) -> RhiPresenceAttemptRepository<'host> {
    437         RhiPresenceAttemptRepository { host: self.host }
    438     }
    439 }
    440 
    441 impl fmt::Debug for RhiStateRepositories<'_> {
    442     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    443         formatter
    444             .debug_struct("RhiStateRepositories")
    445             .field("mode", &self.host.mode())
    446             .field("state", &"[sealed]")
    447             .finish()
    448     }
    449 }
    450 
    451 macro_rules! repository_handle {
    452     ($name:ident, $kind:ident) => {
    453         #[doc = "One non-forgeable typed view of an opened RHI state host."]
    454         pub struct $name<'host> {
    455             host: &'host RhiStateHost,
    456         }
    457 
    458         impl $name<'_> {
    459             /// Returns this repository's closed kind.
    460             #[must_use]
    461             pub const fn kind(&self) -> RhiStateRepositoryKind {
    462                 Kind::$kind
    463             }
    464 
    465             /// Returns this repository's immutable descriptor.
    466             #[must_use]
    467             pub const fn descriptor(&self) -> RhiStateRepositoryDescriptor {
    468                 descriptor(self.kind())
    469             }
    470         }
    471 
    472         impl fmt::Debug for $name<'_> {
    473             fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    474                 formatter
    475                     .debug_struct(stringify!($name))
    476                     .field("mode", &self.host.mode())
    477                     .field("state", &"[sealed]")
    478                     .finish()
    479             }
    480         }
    481     };
    482 }
    483 
    484 repository_handle!(RhiSourceRepository, Source);
    485 repository_handle!(RhiSourceCursorRepository, SourceCursor);
    486 repository_handle!(RhiSourceCompletionRepository, SourceCompletion);
    487 repository_handle!(RhiSignedEventRepository, SignedEvent);
    488 repository_handle!(RhiMutationRepository, Mutation);
    489 repository_handle!(RhiProvenanceRepository, Provenance);
    490 repository_handle!(RhiDirtyTradeRepository, DirtyTrade);
    491 repository_handle!(RhiReconciliationJobRepository, ReconciliationJob);
    492 repository_handle!(RhiReconciliationAttemptRepository, ReconciliationAttempt);
    493 repository_handle!(RhiEvidenceManifestRepository, EvidenceManifest);
    494 repository_handle!(RhiProjectionRepository, Projection);
    495 repository_handle!(RhiReportRepository, Report);
    496 repository_handle!(RhiSupersessionRepository, Supersession);
    497 repository_handle!(RhiSignedAttestationEventRepository, SignedAttestationEvent);
    498 repository_handle!(RhiPublicationOutboxRepository, PublicationOutbox);
    499 repository_handle!(RhiPublicationTargetRepository, PublicationTarget);
    500 repository_handle!(RhiPublicationAttemptRepository, PublicationAttempt);
    501 repository_handle!(RhiDesiredPresenceRepository, DesiredPresence);
    502 repository_handle!(RhiPresenceOutboxRepository, PresenceOutbox);
    503 repository_handle!(RhiPresenceTargetRepository, PresenceTarget);
    504 repository_handle!(RhiPresenceAttemptRepository, PresenceAttempt);
    505 
    506 impl<'host> RhiReconciliationJobRepository<'host> {
    507     pub(crate) const fn host(&self) -> &'host RhiStateHost {
    508         self.host
    509     }
    510 }
    511 
    512 impl<'host> RhiReconciliationAttemptRepository<'host> {
    513     pub(crate) const fn host(&self) -> &'host RhiStateHost {
    514         self.host
    515     }
    516 }
    517 
    518 impl<'host> RhiPublicationOutboxRepository<'host> {
    519     pub(crate) const fn host(&self) -> &'host RhiStateHost {
    520         self.host
    521     }
    522 }
    523 
    524 impl<'host> RhiDesiredPresenceRepository<'host> {
    525     pub(crate) const fn host(&self) -> &'host RhiStateHost {
    526         self.host
    527     }
    528 }
    529 
    530 impl<'host> RhiPresenceOutboxRepository<'host> {
    531     pub(crate) const fn host(&self) -> &'host RhiStateHost {
    532         self.host
    533     }
    534 }
    535 
    536 #[cfg(test)]
    537 mod tests {
    538     use super::*;
    539 
    540     #[test]
    541     fn descriptors_are_closed_unique_and_nonempty() {
    542         assert_eq!(DESCRIPTORS.len(), RHI_STATE_REPOSITORY_COUNT);
    543         for (index, descriptor) in DESCRIPTORS.iter().enumerate() {
    544             assert_eq!(descriptor.kind() as usize, index);
    545             assert!(!descriptor.code().is_empty());
    546             assert!(!descriptor.backing_table().is_empty());
    547             assert_eq!(descriptor.kind().code(), descriptor.code());
    548             assert_eq!(
    549                 descriptor.kind().backing_table(),
    550                 descriptor.backing_table()
    551             );
    552             assert_eq!(descriptor.kind().write_class(), descriptor.write_class());
    553             assert_eq!(
    554                 descriptor.write_class().code(),
    555                 match descriptor.write_class() {
    556                     Write::AppendOnly => "append_only",
    557                     Write::CompareAndSwap => "compare_and_swap",
    558                     Write::Immutable => "immutable",
    559                 }
    560             );
    561             for other in &DESCRIPTORS[index + 1..] {
    562                 assert_ne!(descriptor.kind(), other.kind());
    563                 assert_ne!(descriptor.code(), other.code());
    564             }
    565         }
    566     }
    567 }