coven_protocol/remote_object/
pending_release.rs1use super::*;
2use crate::blob::locator::{BlobLocator, StoredBlobRef};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum PendingCandidateRelease {
9 Retained(RemoteObjectRecord),
10 DeleteProtocol(ExactObjectRef),
11 DeleteBlob(StoredBlobRef),
12}
13
14impl RemoteObjectRecord {
15 pub fn release_pending_candidate(
21 mut self,
22 candidate: &StoreBatchCommitRef,
23 ) -> Result<PendingCandidateRelease, RemoteObjectRecordError> {
24 self.validate()?;
25 let retain = match &mut self {
26 Self::CandidateCommit(record) => {
27 if record.identity != *candidate {
28 return Err(RemoteObjectRecordError::CandidateOwnerMismatch);
29 }
30 match record.state {
31 CandidateCommitState::Prepared | CandidateCommitState::UploadedVerified => {
32 false
33 }
34 CandidateCommitState::CleanupPending { .. }
35 | CandidateCommitState::AbsentVerified { .. } => {
36 return Err(RemoteObjectRecordError::InvalidCleanupTransition);
37 }
38 }
39 }
40 Self::CandidateExclusive(record) => {
41 if !matches!(
42 record.identity.domain,
43 CandidateExclusiveObjectDomain::StorePackage { .. }
44 | CandidateExclusiveObjectDomain::CirclePackage { .. }
45 ) {
46 return Err(RemoteObjectRecordError::DomainMismatch);
47 }
48 match &mut record.state {
49 CandidateObjectState::Prepared { ownership }
50 | CandidateObjectState::UploadedVerified { ownership } => {
51 release_pending_owner(ownership, candidate)?
52 }
53 CandidateObjectState::CleanupPending { .. }
54 | CandidateObjectState::AbsentVerified { .. } => {
55 return Err(RemoteObjectRecordError::InvalidCleanupTransition);
56 }
57 }
58 }
59 Self::RetainedAuthority(record) => {
60 let RetainedAuthorityObjectDomain::Commit { reference } = &record.identity.domain
61 else {
62 return Err(RemoteObjectRecordError::DomainMismatch);
63 };
64 if reference != candidate {
65 return Err(RemoteObjectRecordError::CandidateOwnerMismatch);
66 }
67 match &record.state {
68 RetainedAuthorityObjectState::UploadedVerified { ownership }
69 if ownership.activated.contains(candidate) =>
70 {
71 true
72 }
73 _ => return Err(RemoteObjectRecordError::InvalidActivation),
74 }
75 }
76 Self::SharedLiveSet(record) => {
77 if !matches!(
78 record.identity.domain,
79 SharedLiveSetObjectDomain::StoredBlob
80 | SharedLiveSetObjectDomain::StorePackage { .. }
81 | SharedLiveSetObjectDomain::CirclePackage { .. }
82 ) {
83 return Err(RemoteObjectRecordError::DomainMismatch);
84 }
85 match &mut record.state {
86 OwnedObjectState::Prepared { ownership } => {
87 release_pending_owner(ownership, candidate)?
88 }
89 OwnedObjectState::UploadedVerified { ownership } => {
90 if !ownership.pending.remove(candidate)
91 && !ownership
92 .activated
93 .contains(&SharedObjectOwner::StoreCommit(candidate.clone()))
94 {
95 return Err(RemoteObjectRecordError::CandidateOwnerMismatch);
96 }
97 !ownership.pending.is_empty() || !ownership.activated.is_empty()
98 }
99 OwnedObjectState::RetirementPending { .. } => {
100 return Err(RemoteObjectRecordError::InvalidCleanupTransition);
101 }
102 }
103 }
104 };
105 if retain {
106 self.validate()?;
107 return Ok(PendingCandidateRelease::Retained(self));
108 }
109 if let Self::SharedLiveSet(record) = &self {
110 if matches!(
111 record.identity.domain,
112 SharedLiveSetObjectDomain::StoredBlob
113 ) {
114 let bytes = record
115 .payloads
116 .carried_locator_bytes()
117 .ok_or(RemoteObjectRecordError::PayloadPlacement)?;
118 return Ok(PendingCandidateRelease::DeleteBlob(StoredBlobRef::new(
119 BlobLocator::parse(bytes)?,
120 record.identity.object.clone(),
121 )?));
122 }
123 }
124 Ok(PendingCandidateRelease::DeleteProtocol(
125 self.object().clone(),
126 ))
127 }
128}
129
130fn release_pending_owner(
131 ownership: &mut PendingCandidateOwnership,
132 candidate: &StoreBatchCommitRef,
133) -> Result<bool, RemoteObjectRecordError> {
134 if !ownership.pending.remove(candidate) {
135 return Err(RemoteObjectRecordError::CandidateOwnerMismatch);
136 }
137 Ok(!ownership.pending.is_empty())
138}