aster_forge_cloud_files_core/
cache_write.rs1use std::fmt;
4
5use crate::{ByteRange, CloudFilesCoreError, ContentCacheKey, Result, SessionGeneration};
6
7#[derive(Clone, PartialEq, Eq, Hash)]
9pub struct ContentCacheWriteOperationId(String);
10
11impl ContentCacheWriteOperationId {
12 pub fn new(value: impl Into<String>) -> Result<Self> {
18 let value = value.into();
19 if value.is_empty() {
20 return Err(CloudFilesCoreError::empty(
21 "content cache write operation id",
22 ));
23 }
24 Ok(Self(value))
25 }
26
27 #[must_use]
29 pub fn as_str(&self) -> &str {
30 &self.0
31 }
32}
33
34impl fmt::Debug for ContentCacheWriteOperationId {
35 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
36 formatter
37 .debug_struct("ContentCacheWriteOperationId")
38 .field("byte_len", &self.0.len())
39 .finish()
40 }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub enum ContentCacheWriteExtent {
46 Whole,
48 Range(ByteRange),
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct ContentCacheWriteIntent {
55 operation_id: ContentCacheWriteOperationId,
56 cache_key: ContentCacheKey,
57 expected_size: u64,
58 extent: ContentCacheWriteExtent,
59 session_generation: SessionGeneration,
60}
61
62impl ContentCacheWriteIntent {
63 pub fn new(
69 operation_id: ContentCacheWriteOperationId,
70 cache_key: ContentCacheKey,
71 expected_size: u64,
72 extent: ContentCacheWriteExtent,
73 session_generation: SessionGeneration,
74 ) -> Result<Self> {
75 if let ContentCacheWriteExtent::Range(range) = extent
76 && range.end_exclusive() > expected_size
77 {
78 return Err(CloudFilesCoreError::invalid_content_cache_write(
79 "cache write range exceeds the expected content size",
80 ));
81 }
82 Ok(Self {
83 operation_id,
84 cache_key,
85 expected_size,
86 extent,
87 session_generation,
88 })
89 }
90
91 #[must_use]
93 pub const fn operation_id(&self) -> &ContentCacheWriteOperationId {
94 &self.operation_id
95 }
96
97 #[must_use]
99 pub const fn cache_key(&self) -> &ContentCacheKey {
100 &self.cache_key
101 }
102
103 #[must_use]
105 pub const fn expected_size(&self) -> u64 {
106 self.expected_size
107 }
108
109 #[must_use]
111 pub const fn extent(&self) -> ContentCacheWriteExtent {
112 self.extent
113 }
114
115 #[must_use]
117 pub const fn session_generation(&self) -> SessionGeneration {
118 self.session_generation
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
124pub enum ContentCacheWriteState {
125 IntentPersisted,
127 PhysicalBytesCommitted,
129 CoverageCommitted,
131 Completed,
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
137pub enum ContentCacheWriteRecordTransition {
138 Applied,
140 AlreadyApplied,
142}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
146pub struct ContentCacheWriteRecord {
147 intent: ContentCacheWriteIntent,
148 state: ContentCacheWriteState,
149}
150
151impl ContentCacheWriteRecord {
152 #[must_use]
154 pub const fn persist(intent: ContentCacheWriteIntent) -> Self {
155 Self {
156 intent,
157 state: ContentCacheWriteState::IntentPersisted,
158 }
159 }
160
161 #[must_use]
163 pub const fn intent(&self) -> &ContentCacheWriteIntent {
164 &self.intent
165 }
166
167 #[must_use]
169 pub const fn state(&self) -> ContentCacheWriteState {
170 self.state
171 }
172
173 pub fn mark_physical_bytes_committed(&mut self) -> Result<ContentCacheWriteRecordTransition> {
179 match self.state {
180 ContentCacheWriteState::IntentPersisted => {
181 self.state = ContentCacheWriteState::PhysicalBytesCommitted;
182 Ok(ContentCacheWriteRecordTransition::Applied)
183 }
184 ContentCacheWriteState::PhysicalBytesCommitted
185 | ContentCacheWriteState::CoverageCommitted
186 | ContentCacheWriteState::Completed => {
187 Ok(ContentCacheWriteRecordTransition::AlreadyApplied)
188 }
189 }
190 }
191
192 pub fn mark_coverage_committed(&mut self) -> Result<ContentCacheWriteRecordTransition> {
198 match self.state {
199 ContentCacheWriteState::PhysicalBytesCommitted => {
200 self.state = ContentCacheWriteState::CoverageCommitted;
201 Ok(ContentCacheWriteRecordTransition::Applied)
202 }
203 ContentCacheWriteState::CoverageCommitted | ContentCacheWriteState::Completed => {
204 Ok(ContentCacheWriteRecordTransition::AlreadyApplied)
205 }
206 ContentCacheWriteState::IntentPersisted => {
207 Err(CloudFilesCoreError::invalid_content_cache_write(
208 "coverage commit requires observable physical bytes",
209 ))
210 }
211 }
212 }
213
214 pub fn complete(&mut self) -> Result<ContentCacheWriteRecordTransition> {
220 match self.state {
221 ContentCacheWriteState::CoverageCommitted => {
222 self.state = ContentCacheWriteState::Completed;
223 Ok(ContentCacheWriteRecordTransition::Applied)
224 }
225 ContentCacheWriteState::Completed => {
226 Ok(ContentCacheWriteRecordTransition::AlreadyApplied)
227 }
228 _ => Err(CloudFilesCoreError::invalid_content_cache_write(
229 "cache write completion requires committed coverage",
230 )),
231 }
232 }
233}