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 }