azure_storage_blobs/blob/operations/
get_blob.rsuse crate::prelude::*;
use azure_core::{
error::Error, headers::*, prelude::*, Pageable, RequestId, Response as AzureResponse,
ResponseBody,
};
use time::OffsetDateTime;
const DEFAULT_CHUNK_SIZE: u64 = 0x1000 * 0x1000;
operation! {
#[stream]
GetBlob,
client: BlobClient,
?range: Range,
?blob_versioning: BlobVersioning,
?lease_id: LeaseId,
?chunk_size: u64,
?encryption_key: CPKInfo,
?if_modified_since: IfModifiedSinceCondition,
?if_match: IfMatchCondition,
?if_tags: IfTags,
}
impl GetBlobBuilder {
pub fn into_stream(self) -> Pageable<GetBlobResponse, Error> {
let make_request = move |continuation: Option<Range>| {
let this = self.clone();
let mut ctx = self.context.clone();
async move {
let mut url = this.client.url()?;
let range = match continuation {
Some(range) => range,
None => initial_range(
this.chunk_size.unwrap_or(DEFAULT_CHUNK_SIZE),
this.range.clone(),
),
};
this.blob_versioning.append_to_url_query(&mut url);
let mut headers = Headers::new();
for (name, value) in range.as_headers() {
headers.insert(name, value);
}
headers.add(this.lease_id);
headers.add(this.encryption_key.as_ref());
headers.add(this.if_modified_since);
headers.add(this.if_match.clone());
headers.add(this.if_tags.clone());
let mut request =
BlobClient::finalize_request(url, azure_core::Method::Get, headers, None)?;
let response = this.client.send(&mut ctx, &mut request).await?;
GetBlobResponse::try_from(this, response)
}
};
Pageable::new(make_request)
}
}
#[derive(Debug)]
pub struct GetBlobResponse {
pub request_id: RequestId,
pub blob: Blob,
pub data: ResponseBody,
pub date: OffsetDateTime,
pub content_range: Option<Range>,
pub remaining_range: Option<Range>,
}
impl GetBlobResponse {
fn try_from(request: GetBlobBuilder, response: AzureResponse) -> azure_core::Result<Self> {
let headers = response.headers();
let request_id = request_id_from_headers(headers)?;
let date = date_from_headers(headers)?;
let content_range = headers.get_optional_as(&CONTENT_RANGE)?;
let remaining_range = remaining_range(
request.chunk_size.unwrap_or(DEFAULT_CHUNK_SIZE),
request.range,
content_range,
);
let blob = Blob::from_headers(request.client.blob_name(), headers)?;
let data = response.into_body();
Ok(Self {
request_id,
blob,
data,
date,
content_range: content_range.map(|cr| Range::new(cr.start(), cr.end())),
remaining_range,
})
}
}
impl Continuable for GetBlobResponse {
type Continuation = Range;
fn continuation(&self) -> Option<Self::Continuation> {
self.remaining_range.clone()
}
}
fn initial_range(chunk_size: u64, request_range: Option<Range>) -> Range {
match request_range {
Some(Range::Range(x)) => {
let len = std::cmp::min(x.end - x.start, chunk_size);
(x.start..x.start + len).into()
}
Some(Range::RangeFrom(x)) => (x.start..x.start + chunk_size).into(),
None => Range::new(0, chunk_size),
}
}
fn remaining_range(
chunk_size: u64,
base_range: Option<Range>,
content_range: Option<ContentRange>,
) -> Option<Range> {
let content_range = content_range?;
if content_range.end() + 1 >= content_range.total_length() {
return None;
}
let requested_range = base_range.unwrap_or_else(|| Range::new(0, content_range.total_length()));
let after = content_range.end() + 1;
let remaining_size = match requested_range {
Range::Range(x) => {
if after >= x.end {
return None;
}
x.end - after
}
Range::RangeFrom(_) => after,
};
let size = std::cmp::min(remaining_size, chunk_size);
Some(Range::new(after, after + size))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initial_range() -> azure_core::Result<()> {
let result = initial_range(3, Some(Range::new(0, 10)));
let expected = Range::new(0, 3);
assert_eq!(result, expected);
let result = initial_range(3, Some(Range::new(3, 10)));
let expected = Range::new(3, 6);
assert_eq!(result, expected);
let result = initial_range(3, None);
let expected = Range::new(0, 3);
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_remaining_range() -> azure_core::Result<()> {
let result = remaining_range(3, None, None);
assert!(result.is_none());
let result = remaining_range(3, Some(Range::new(0, 10)), None);
assert!(result.is_none());
let result = remaining_range(
3,
Some(Range::new(0, 10)),
Some(ContentRange::new(0, 3, 10)),
);
assert_eq!(result, Some(Range::new(4, 7)));
let result = remaining_range(
3,
Some(Range::new(0, 10)),
Some(ContentRange::new(0, 10, 10)),
);
assert!(result.is_none());
let result = remaining_range(3, None, Some(ContentRange::new(0, 10, 10)));
assert!(result.is_none());
let result = remaining_range(3, None, Some(ContentRange::new(0, 10, 20)));
assert_eq!(result, Some(Range::new(11, 14)));
let result = remaining_range(
20,
Some(Range::new(5, 15)),
Some(ContentRange::new(5, 14, 20)),
);
assert_eq!(result, None);
Ok(())
}
}