pub use crate::generated::clients::{BlobContainerClient, BlobContainerClientOptions};
use crate::{models::StorageErrorCode, BlobClient};
use azure_core::{
credentials::TokenCredential,
error::ErrorKind,
http::{
policies::{auth::BearerTokenAuthorizationPolicy, Policy},
Pipeline, StatusCode, Url,
},
tracing, Result,
};
use std::sync::Arc;
impl BlobContainerClient {
#[tracing::new("Storage.Blob.Container")]
pub fn new(
container_url: Url,
credential: Option<Arc<dyn TokenCredential>>,
options: Option<BlobContainerClientOptions>,
) -> Result<Self> {
if container_url.cannot_be_a_base() {
return Err(azure_core::Error::with_message(
azure_core::error::ErrorKind::Other,
format!("{container_url} is not a valid base URL"),
));
}
let mut options = options.unwrap_or_default();
super::apply_client_defaults(&mut options.client_options);
let mut per_retry_policies: Vec<Arc<dyn Policy>> = Vec::default();
if let Some(token_credential) = credential {
if !container_url.scheme().starts_with("https") {
return Err(azure_core::Error::with_message(
azure_core::error::ErrorKind::Other,
format!("{container_url} must use https"),
));
}
per_retry_policies.push(Arc::new(BearerTokenAuthorizationPolicy::new(
token_credential,
vec!["https://storage.azure.com/.default"],
)));
}
let pipeline = Pipeline::new(
option_env!("CARGO_PKG_NAME"),
option_env!("CARGO_PKG_VERSION"),
options.client_options.clone(),
Vec::default(),
per_retry_policies,
None,
);
Ok(Self {
endpoint: container_url,
version: options.version,
pipeline,
})
}
pub fn blob_client(&self, blob_name: &str) -> BlobClient {
let mut blob_url = self.url().clone();
blob_url
.path_segments_mut()
.expect("Invalid endpoint URL: Cannot append blob_name to the blob endpoint.")
.extend([blob_name]);
BlobClient {
endpoint: blob_url,
pipeline: self.pipeline.clone(),
version: self.version.clone(),
tracer: self.tracer.clone(),
}
}
pub fn url(&self) -> &Url {
&self.endpoint
}
pub async fn exists(&self) -> Result<bool> {
match self.get_properties(None).await {
Ok(_) => Ok(true),
Err(e) if e.http_status() == Some(StatusCode::NotFound) => match e.kind() {
ErrorKind::HttpResponse {
error_code: Some(error_code),
..
} if error_code == StorageErrorCode::ContainerNotFound.as_ref() => Ok(false),
_ => Err(e),
},
Err(e) => Err(e),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_url_rejects_cannot_be_a_base_url() {
let url = Url::parse("data:text/plain,hello").unwrap();
assert!(BlobContainerClient::new(url, None, None).is_err());
}
#[test]
fn from_url_accepts_http_without_credential() {
let url = Url::parse("http://127.0.0.1:10000/devstoreaccount1/container").unwrap();
let container = BlobContainerClient::new(url, None, None).unwrap();
assert_eq!(
container.blob_client("blob").url().path(),
"/devstoreaccount1/container/blob"
);
}
#[test]
fn from_url_accepts_https_custom_hostname() {
let url = Url::parse("https://cdn.contoso.com/container").unwrap();
assert!(BlobContainerClient::new(url, None, None).is_ok());
}
}