1use chrono::{DateTime, Utc};
2use serde::{de::Error, Deserialize, Deserializer};
3
4#[derive(Debug)]
6pub enum BlobType {
7 Unspecified,
9 PageBlob,
11 BlockBlob,
13 AppendBlob,
15}
16
17impl Default for BlobType {
18 fn default() -> Self {
19 BlobType::Unspecified
20 }
21}
22
23#[derive(Debug)]
25pub enum LeaseDuration {
26 Unspecified,
28 Fixed,
30 Infinite,
32}
33
34impl Default for LeaseDuration {
35 fn default() -> Self {
36 LeaseDuration::Unspecified
37 }
38}
39
40#[derive(Debug)]
42pub enum LeaseState {
43 Unspecified,
45 Available,
47 Leased,
49 Expired,
51 Breaking,
53 Broken,
55}
56
57impl Default for LeaseState {
58 fn default() -> Self {
59 LeaseState::Unspecified
60 }
61}
62
63#[derive(Debug)]
65pub enum LeaseStatus {
66 Unspecified,
68 Locked,
70 Unlocked,
72}
73
74impl Default for LeaseStatus {
75 fn default() -> Self {
76 LeaseStatus::Unspecified
77 }
78}
79
80#[derive(Debug)]
82pub enum PremiumPageBlobTier {
83 Unknown,
85 P4,
87 P6,
89 P10,
91 P20,
93 P30,
95 P40,
97 P50,
99 P60,
101}
102
103impl Default for PremiumPageBlobTier {
104 fn default() -> Self {
105 PremiumPageBlobTier::Unknown
106 }
107}
108
109#[derive(Debug)]
111pub enum RehydrationStatus {
112 Unknown,
114 PendingToHot,
116 PendingToCool,
118}
119
120impl Default for RehydrationStatus {
121 fn default() -> Self {
122 RehydrationStatus::Unknown
123 }
124}
125
126#[derive(Debug)]
128pub enum StandardBlobTier {
129 Unknown,
131 Hot,
133 Cool,
135 Archive,
137}
138
139impl Default for StandardBlobTier {
140 fn default() -> Self {
141 StandardBlobTier::Unknown
142 }
143}
144
145#[derive(Default, Debug, serde::Deserialize)]
147#[serde(rename_all = "PascalCase")]
148pub struct Properties {
149 pub append_blob_committed_block_count: Option<i32>,
151 pub blob_tier_inferred: Option<bool>,
153 pub blob_tier_last_modified_time: Option<DateTime<Utc>>,
155 #[serde(deserialize_with = "deserialize_blob_type")]
157 pub blob_type: BlobType,
158 pub cache_control: Option<String>,
160 pub content_disposition: Option<String>,
162 pub content_encoding: Option<String>,
164 pub content_language: Option<String>,
166 #[serde(rename = "ContentMD5")]
168 pub content_md5: Option<String>,
169 pub content_type: Option<String>,
171 pub created: Option<DateTime<Utc>>,
173 pub deleted_time: Option<DateTime<Utc>>,
175 #[serde(rename = "ETag")]
177 pub etag: Option<String>,
178 pub is_incremental_copy: bool,
180 pub is_server_encrypted: bool,
182 pub last_modified: Option<DateTime<Utc>>,
184 #[serde(deserialize_with = "deserialize_lease_duration")]
186 pub lease_duration: LeaseDuration,
187 #[serde(deserialize_with = "deserialize_lease_state")]
189 pub lease_state: LeaseState,
190 #[serde(deserialize_with = "deserialize_lease_status")]
192 pub lease_status: LeaseStatus,
193 pub length: i64,
195 pub page_blob_sequence_number: Option<i64>,
197 #[serde(deserialize_with = "deserialize_page_blob_tier")]
199 pub premium_page_blob_tier: Option<PremiumPageBlobTier>,
200 #[serde(deserialize_with = "deserialize_rehydration_status")]
201 pub rehydration_status: Option<RehydrationStatus>,
203 pub remaining_days_before_permanent_delete: Option<i32>,
205 #[serde(deserialize_with = "deserialize_standard_blob_tier")]
207 pub standard_blob_tier: Option<StandardBlobTier>,
208}
209
210fn deserialize_blob_type<'a, D>(deserializer: D) -> Result<BlobType, D::Error>
211where
212 D: Deserializer<'a>,
213{
214 match u32::deserialize(deserializer)? {
215 0 => Ok(BlobType::Unspecified),
216 1 => Ok(BlobType::PageBlob),
217 2 => Ok(BlobType::BlockBlob),
218 3 => Ok(BlobType::AppendBlob),
219 _ => Err(Error::custom("unexpected blob type")),
220 }
221}
222
223fn deserialize_lease_duration<'a, D>(deserializer: D) -> Result<LeaseDuration, D::Error>
224where
225 D: Deserializer<'a>,
226{
227 match u32::deserialize(deserializer)? {
228 0 => Ok(LeaseDuration::Unspecified),
229 1 => Ok(LeaseDuration::Fixed),
230 2 => Ok(LeaseDuration::Infinite),
231 _ => Err(Error::custom("unexpected lease duration")),
232 }
233}
234
235fn deserialize_lease_state<'a, D>(deserializer: D) -> Result<LeaseState, D::Error>
236where
237 D: Deserializer<'a>,
238{
239 match u32::deserialize(deserializer)? {
240 0 => Ok(LeaseState::Unspecified),
241 1 => Ok(LeaseState::Available),
242 2 => Ok(LeaseState::Leased),
243 3 => Ok(LeaseState::Expired),
244 4 => Ok(LeaseState::Breaking),
245 5 => Ok(LeaseState::Broken),
246 _ => Err(Error::custom("unexpected lease state")),
247 }
248}
249
250fn deserialize_lease_status<'a, D>(deserializer: D) -> Result<LeaseStatus, D::Error>
251where
252 D: Deserializer<'a>,
253{
254 match u32::deserialize(deserializer)? {
255 0 => Ok(LeaseStatus::Unspecified),
256 1 => Ok(LeaseStatus::Locked),
257 2 => Ok(LeaseStatus::Unlocked),
258 _ => Err(Error::custom("unexpected lease status")),
259 }
260}
261
262fn deserialize_page_blob_tier<'a, D>(
263 deserializer: D,
264) -> Result<Option<PremiumPageBlobTier>, D::Error>
265where
266 D: Deserializer<'a>,
267{
268 match Option::<u32>::deserialize(deserializer)? {
269 Some(x) => match x {
270 0 => Ok(Some(PremiumPageBlobTier::Unknown)),
271 1 => Ok(Some(PremiumPageBlobTier::P4)),
272 2 => Ok(Some(PremiumPageBlobTier::P6)),
273 3 => Ok(Some(PremiumPageBlobTier::P10)),
274 4 => Ok(Some(PremiumPageBlobTier::P20)),
275 5 => Ok(Some(PremiumPageBlobTier::P30)),
276 6 => Ok(Some(PremiumPageBlobTier::P40)),
277 7 => Ok(Some(PremiumPageBlobTier::P50)),
278 8 => Ok(Some(PremiumPageBlobTier::P60)),
279 _ => Err(Error::custom("unexpected page blob tier")),
280 },
281 None => Ok(None),
282 }
283}
284
285fn deserialize_rehydration_status<'a, D>(
286 deserializer: D,
287) -> Result<Option<RehydrationStatus>, D::Error>
288where
289 D: Deserializer<'a>,
290{
291 match Option::<u32>::deserialize(deserializer)? {
292 Some(x) => match x {
293 0 => Ok(Some(RehydrationStatus::Unknown)),
294 1 => Ok(Some(RehydrationStatus::PendingToHot)),
295 2 => Ok(Some(RehydrationStatus::PendingToCool)),
296 _ => Err(Error::custom("unexpected rehydration status")),
297 },
298 None => Ok(None),
299 }
300}
301
302fn deserialize_standard_blob_tier<'a, D>(
303 deserializer: D,
304) -> Result<Option<StandardBlobTier>, D::Error>
305where
306 D: Deserializer<'a>,
307{
308 match Option::<u32>::deserialize(deserializer)? {
309 Some(x) => match x {
310 0 => Ok(Some(StandardBlobTier::Unknown)),
311 1 => Ok(Some(StandardBlobTier::Hot)),
312 2 => Ok(Some(StandardBlobTier::Cool)),
313 3 => Ok(Some(StandardBlobTier::Archive)),
314 _ => Err(Error::custom("unexpected blob tier")),
315 },
316 None => Ok(None),
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323 use matches::matches;
324 use serde_json::{from_value, json};
325
326 #[test]
327 fn it_deserializes_from_json() {
328 const CACHE_CONTROL: &'static str = "test-cache-control";
329 const CONTENT_DISPOSITION: &'static str = "test-content-disposition";
330 const CONTENT_ENCODING: &'static str = "test-content-encoding";
331 const CONTENT_LANGUAGE: &'static str = "test-content-language";
332 const CONTENT_LENGTH: u32 = 12345;
333 const CONTENT_MD5: &'static str = "test-md5-hash";
334 const CONTENT_TYPE: &'static str = "test-content-type";
335 const ETAG: &'static str = "test-etag";
336 const IS_SERVER_ENCRYPTED: bool = false;
337 const IS_INCREMENTAL_COPY: bool = true;
338 const BLOB_TIER_INFERRED: bool = true;
339 const APPEND_BLOCK_COUNT: i32 = 101;
340 const PAGE_BLOCK_SEQ_NUM: i64 = 54321;
341
342 let now = Utc::now();
343
344 let value = json!({
345 "CacheControl": CACHE_CONTROL,
346 "ContentDisposition": CONTENT_DISPOSITION,
347 "ContentEncoding": CONTENT_ENCODING,
348 "ContentLanguage": CONTENT_LANGUAGE,
349 "Length": CONTENT_LENGTH,
350 "ContentMD5": CONTENT_MD5,
351 "ContentType": CONTENT_TYPE,
352 "ETag": ETAG,
353 "LastModified": now.to_rfc3339(),
354 "BlobType": 1,
355 "LeaseStatus": 1,
356 "LeaseState": 0,
357 "LeaseDuration": 2,
358 "PageBlobSequenceNumber": PAGE_BLOCK_SEQ_NUM,
359 "AppendBlobCommittedBlockCount": APPEND_BLOCK_COUNT,
360 "IsServerEncrypted": IS_SERVER_ENCRYPTED,
361 "IsIncrementalCopy": IS_INCREMENTAL_COPY,
362 "StandardBlobTier": 1,
363 "RehydrationStatus": 2,
364 "PremiumPageBlobTier": 1,
365 "BlobTierInferred": BLOB_TIER_INFERRED,
366 "BlobTierLastModifiedTime": now.to_rfc3339()
367 });
368
369 let properties: Properties = from_value(value).unwrap();
370
371 assert_eq!(
372 *properties
373 .append_blob_committed_block_count
374 .as_ref()
375 .unwrap(),
376 APPEND_BLOCK_COUNT
377 );
378 assert_eq!(
379 *properties.blob_tier_inferred.as_ref().unwrap(),
380 BLOB_TIER_INFERRED
381 );
382 assert_eq!(
383 properties
384 .blob_tier_last_modified_time
385 .as_ref()
386 .unwrap()
387 .to_rfc3339(),
388 now.to_rfc3339()
389 );
390 assert!(matches!(properties.blob_type, BlobType::PageBlob));
391 assert_eq!(properties.cache_control.as_ref().unwrap(), CACHE_CONTROL);
392 assert_eq!(
393 properties.content_disposition.as_ref().unwrap(),
394 CONTENT_DISPOSITION
395 );
396 assert_eq!(
397 properties.content_encoding.as_ref().unwrap(),
398 CONTENT_ENCODING
399 );
400 assert_eq!(
401 properties.content_language.as_ref().unwrap(),
402 CONTENT_LANGUAGE
403 );
404 assert_eq!(properties.content_md5.as_ref().unwrap(), CONTENT_MD5);
405 assert_eq!(properties.content_type.as_ref().unwrap(), CONTENT_TYPE);
406 assert!(properties.created.is_none());
407 assert!(properties.deleted_time.is_none());
408 assert_eq!(properties.etag.as_ref().unwrap(), ETAG);
409 assert_eq!(properties.is_incremental_copy, IS_INCREMENTAL_COPY);
410 assert_eq!(properties.is_server_encrypted, IS_SERVER_ENCRYPTED);
411 assert_eq!(
412 properties.last_modified.as_ref().unwrap().to_rfc3339(),
413 now.to_rfc3339()
414 );
415 assert!(matches!(properties.lease_duration, LeaseDuration::Infinite));
416 assert!(matches!(properties.lease_state, LeaseState::Unspecified));
417 assert!(matches!(properties.lease_status, LeaseStatus::Locked));
418 assert_eq!(properties.length, CONTENT_LENGTH as i64);
419 assert_eq!(
420 *properties.page_blob_sequence_number.as_ref().unwrap(),
421 PAGE_BLOCK_SEQ_NUM
422 );
423 assert!(matches!(
424 properties.premium_page_blob_tier.as_ref().unwrap(),
425 PremiumPageBlobTier::P4
426 ));
427 assert!(matches!(
428 properties.rehydration_status.as_ref().unwrap(),
429 RehydrationStatus::PendingToCool
430 ));
431 assert!(properties.remaining_days_before_permanent_delete.is_none());
432 assert!(matches!(
433 properties.standard_blob_tier.as_ref().unwrap(),
434 StandardBlobTier::Hot
435 ));
436 }
437}