mod lease_blob_options;
pub mod operations;
mod source_content_md5;
pub use source_content_md5::*;
mod blob_block_type;
mod blob_block_with_size;
mod block_list;
mod block_list_type;
mod block_with_size_list;
mod page_range_list;
pub use blob_block_type::BlobBlockType;
pub use blob_block_with_size::BlobBlockWithSize;
pub use block_list::BlockList;
pub use block_list_type::BlockListType;
pub use block_with_size_list::BlockWithSizeList;
pub use lease_blob_options::{LeaseBlobOptions, LEASE_BLOB_OPTIONS_DEFAULT};
pub use page_range_list::PageRangeList;
use crate::options::{AccessTier, Snapshot, Tags, SNAPSHOT};
use azure_core::{
content_type, date,
headers::{self, Headers},
parsing::from_azure_time,
Etag, LeaseDuration, LeaseState, LeaseStatus,
};
use azure_storage::{ConsistencyCRC64, ConsistencyMD5, CopyId, CopyProgress};
use serde::{Deserialize, Deserializer};
use serde_json::Value;
use std::collections::HashMap;
use time::OffsetDateTime;
#[cfg(feature = "azurite_workaround")]
fn get_creation_time(h: &Headers) -> azure_core::Result<Option<OffsetDateTime>> {
if let Some(creation_time) = h.get_optional_str(&headers::CREATION_TIME) {
let creation_time =
date::parse_rfc1123(creation_time).unwrap_or_else(|_| OffsetDateTime::now_utc());
Ok(Some(creation_time))
} else {
Ok(None)
}
}
create_enum!(
BlobType,
(BlockBlob, "BlockBlob"),
(PageBlob, "PageBlob"),
(AppendBlob, "AppendBlob")
);
create_enum!(
CopyStatus,
(Pending, "pending"),
(Success, "success"),
(Aborted, "aborted"),
(Failed, "failed")
);
create_enum!(RehydratePriority, (High, "High"), (Standard, "Standard"));
create_enum!(PageWriteType, (Update, "update"), (Clear, "clear"));
fn deserialize_crc64_optional<'de, D>(deserializer: D) -> Result<Option<ConsistencyCRC64>, D::Error>
where
D: Deserializer<'de>,
{
let s: Option<String> = Option::deserialize(deserializer)?;
s.filter(|s| !s.is_empty())
.map(ConsistencyCRC64::decode)
.transpose()
.map_err(serde::de::Error::custom)
}
fn deserialize_md5_optional<'de, D>(deserializer: D) -> Result<Option<ConsistencyMD5>, D::Error>
where
D: Deserializer<'de>,
{
let s: Option<String> = Option::deserialize(deserializer)?;
s.filter(|s| !s.is_empty())
.map(ConsistencyMD5::decode)
.transpose()
.map_err(serde::de::Error::custom)
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Blob {
pub name: String,
pub snapshot: Option<Snapshot>,
pub version_id: Option<String>,
pub is_current_version: Option<bool>,
pub deleted: Option<bool>,
pub properties: BlobProperties,
pub metadata: Option<HashMap<String, String>>,
pub tags: Option<Tags>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct BlobProperties {
#[cfg(not(feature = "azurite_workaround"))]
#[serde(with = "azure_core::date::rfc1123", rename = "Creation-Time")]
pub creation_time: OffsetDateTime,
#[cfg(feature = "azurite_workaround")]
#[serde(
default,
with = "azure_core::date::rfc1123::option",
rename = "Creation-Time"
)]
pub creation_time: Option<OffsetDateTime>,
#[serde(with = "azure_core::date::rfc1123", rename = "Last-Modified")]
pub last_modified: OffsetDateTime,
#[serde(default, with = "azure_core::date::rfc1123::option")]
pub last_access_time: Option<OffsetDateTime>,
pub etag: Etag,
#[serde(rename = "Content-Length")]
pub content_length: u64,
#[serde(rename = "Content-Type")]
pub content_type: String,
#[serde(rename = "Content-Encoding")]
pub content_encoding: Option<String>,
#[serde(rename = "Content-Language")]
pub content_language: Option<String>,
#[serde(rename = "Content-Disposition")]
pub content_disposition: Option<String>,
#[serde(
default,
deserialize_with = "deserialize_md5_optional",
rename = "Content-MD5"
)]
pub content_md5: Option<ConsistencyMD5>,
#[serde(
default,
deserialize_with = "deserialize_crc64_optional",
rename = "Content-CRC64"
)]
pub content_crc64: Option<ConsistencyCRC64>,
#[serde(rename = "Cache-Control")]
pub cache_control: Option<String>,
#[serde(rename = "x-ms-blob-sequence-number")]
pub blob_sequence_number: Option<u64>,
pub blob_type: BlobType,
pub access_tier: Option<AccessTier>,
#[serde(default, with = "azure_core::date::rfc1123::option")]
pub access_tier_change_time: Option<OffsetDateTime>,
pub lease_status: Option<LeaseStatus>,
pub lease_state: Option<LeaseState>,
pub lease_duration: Option<LeaseDuration>,
pub copy_id: Option<CopyId>,
pub copy_status: Option<CopyStatus>,
pub copy_source: Option<String>,
pub copy_progress: Option<CopyProgress>,
#[serde(default, with = "azure_core::date::rfc1123::option")]
pub copy_completion_time: Option<OffsetDateTime>,
pub copy_status_description: Option<String>,
#[serde(default)]
pub server_encrypted: bool,
pub customer_provided_key_sha256: Option<String>,
pub encryption_scope: Option<String>,
pub incremental_copy: Option<bool>,
pub access_tier_inferred: Option<bool>,
#[serde(default, with = "azure_core::date::rfc1123::option")]
pub deleted_time: Option<OffsetDateTime>,
pub remaining_retention_days: Option<u32>,
pub tag_count: Option<u32>,
pub rehydrate_priority: Option<RehydratePriority>,
#[serde(
default,
with = "azure_core::date::rfc1123::option",
rename = "Expiry-Time"
)]
pub expiry_time: Option<OffsetDateTime>,
pub blob_committed_block_count: Option<u64>,
pub resource_type: Option<String>,
#[serde(flatten)]
extra: HashMap<String, Value>, }
impl Blob {
pub(crate) fn from_headers<BN: Into<String>>(
blob_name: BN,
h: &Headers,
) -> azure_core::Result<Blob> {
#[cfg(not(feature = "azurite_workaround"))]
let creation_time = {
let creation_time = h.get_str(&headers::CREATION_TIME)?;
date::parse_rfc1123(creation_time)?
};
#[cfg(feature = "azurite_workaround")]
let creation_time = get_creation_time(h)?;
let content_type = h
.get_optional_str(&headers::CONTENT_TYPE)
.unwrap_or(content_type::APPLICATION_OCTET_STREAM.as_str())
.to_string();
let content_length = h.get_as(&headers::CONTENT_LENGTH)?;
let last_modified = from_azure_time(h.get_str(&headers::LAST_MODIFIED)?)?;
let etag = h.get_as(&headers::ETAG)?;
let blob_sequence_number = h.get_optional_as(&headers::BLOB_SEQUENCE_NUMBER)?;
let blob_type = h.get_as(&headers::BLOB_TYPE)?;
let access_tier = h.get_optional_as(&headers::BLOB_ACCESS_TIER)?;
let content_encoding = h.get_optional_string(&headers::CONTENT_ENCODING);
let content_language = h.get_optional_string(&headers::CONTENT_LANGUAGE);
let content_md5 = h.get_optional_as(&headers::CONTENT_MD5)?;
let content_crc64 = h.get_optional_as(&azure_storage::headers::CONTENT_CRC64)?;
let cache_control = h.get_optional_string(&headers::CACHE_CONTROL);
let content_disposition = h.get_optional_string(&headers::CONTENT_DISPOSITION);
let lease_status = h.get_optional_as(&headers::LEASE_STATUS)?;
let lease_state = h.get_optional_as(&headers::LEASE_STATE)?;
let lease_duration = h.get_optional_as(&headers::LEASE_DURATION)?;
let copy_id = h.get_optional_as(&azure_storage::headers::COPY_ID)?;
let copy_status = h.get_optional_as(&headers::COPY_STATUS)?;
let copy_source = h.get_optional_string(&headers::COPY_SOURCE);
let copy_progress = h.get_optional_as(&headers::COPY_PROGRESS)?;
let copy_completion_time: Option<OffsetDateTime> = h
.get_optional_str(&headers::COPY_COMPLETION_TIME)
.and_then(|cct| date::parse_rfc1123(cct).ok());
let copy_status_description = h.get_optional_string(&headers::COPY_STATUS_DESCRIPTION);
let server_encrypted = h.get_as(&headers::SERVER_ENCRYPTED)?;
let blob_committed_block_count = h.get_optional_as(&headers::BLOB_COMMITTED_BLOCK_COUNT)?;
let mut metadata = HashMap::new();
for (name, value) in h.iter() {
let name = name.as_str();
if let Some(name) = name.strip_prefix(headers::META_PREFIX.as_str()) {
metadata.insert(name.to_string(), value.as_str().to_string());
}
}
let metadata = if metadata.is_empty() {
None
} else {
Some(metadata)
};
let tags = h.get_optional_as(&headers::TAGS)?;
let snapshot = h.get_optional_as(&SNAPSHOT)?;
Ok(Blob {
name: blob_name.into(),
snapshot,
deleted: None, is_current_version: None, version_id: None, properties: BlobProperties {
creation_time,
last_modified,
last_access_time: None, etag,
content_length,
content_type,
content_encoding,
content_language,
content_md5,
content_crc64,
cache_control,
content_disposition,
blob_sequence_number,
blob_type,
access_tier,
lease_status,
lease_state,
lease_duration,
copy_id,
copy_status,
copy_source,
copy_progress,
copy_completion_time,
copy_status_description,
incremental_copy: None, server_encrypted,
customer_provided_key_sha256: None, encryption_scope: None, access_tier_inferred: None, access_tier_change_time: None, deleted_time: None, remaining_retention_days: None, tag_count: None, rehydrate_priority: None, expiry_time: None,
resource_type: None,
blob_committed_block_count,
extra: HashMap::new(),
},
metadata,
tags,
})
}
}
pub(crate) fn copy_status_from_headers(headers: &Headers) -> azure_core::Result<CopyStatus> {
headers.get_as(&headers::COPY_STATUS)
}