use crate::generated::models::{
BlobServiceClientFindBlobsByTagsOptions, BlobServiceClientGetAccountInfoOptions,
BlobServiceClientGetAccountInfoResult, BlobServiceClientGetPropertiesOptions,
BlobServiceClientGetStatisticsOptions, BlobServiceClientGetUserDelegationKeyOptions,
BlobServiceClientListContainersSegmentOptions, BlobServiceClientSetPropertiesOptions,
BlobServiceProperties, FilterBlobSegment, KeyInfo, ListContainersSegmentResponse,
StorageServiceStats, UserDelegationKey,
};
use azure_core::{
credentials::TokenCredential,
error::CheckSuccessOptions,
fmt::SafeDebug,
http::{
pager::{PagerResult, PagerState},
policies::{auth::BearerTokenAuthorizationPolicy, Policy},
ClientOptions, Method, NoFormat, Pager, Pipeline, PipelineSendOptions, RawResponse,
Request, RequestContent, Response, Url, UrlExt, XmlFormat,
},
tracing, xml, Result,
};
use std::sync::Arc;
#[tracing::client]
pub struct BlobServiceClient {
pub(crate) endpoint: Url,
pub(crate) pipeline: Pipeline,
pub(crate) version: String,
}
#[derive(Clone, SafeDebug)]
pub struct BlobServiceClientOptions {
pub client_options: ClientOptions,
pub version: String,
}
impl BlobServiceClient {
#[tracing::new("Storage.Blob.Service")]
pub fn new(
endpoint: &str,
credential: Arc<dyn TokenCredential>,
options: Option<BlobServiceClientOptions>,
) -> Result<Self> {
let options = options.unwrap_or_default();
let endpoint = Url::parse(endpoint)?;
if !endpoint.scheme().starts_with("http") {
return Err(azure_core::Error::with_message(
azure_core::error::ErrorKind::Other,
format!("{endpoint} must use http(s)"),
));
}
let auth_policy: Arc<dyn Policy> = Arc::new(BearerTokenAuthorizationPolicy::new(
credential,
vec!["https://storage.azure.com/.default"],
));
Ok(Self {
endpoint,
version: options.version,
pipeline: Pipeline::new(
option_env!("CARGO_PKG_NAME"),
option_env!("CARGO_PKG_VERSION"),
options.client_options,
Vec::default(),
vec![auth_policy],
None,
),
})
}
pub fn endpoint(&self) -> &Url {
&self.endpoint
}
#[tracing::function("Storage.Blob.Service.findBlobsByTags")]
pub async fn find_blobs_by_tags(
&self,
filter_expression: &str,
options: Option<BlobServiceClientFindBlobsByTagsOptions<'_>>,
) -> Result<Response<FilterBlobSegment, XmlFormat>> {
let options = options.unwrap_or_default();
let ctx = options.method_options.context.to_borrowed();
let mut url = self.endpoint.clone();
let mut query_builder = url.query_builder();
query_builder.append_pair("comp", "blobs");
if let Some(include) = options.include.as_ref() {
query_builder.set_pair(
"include",
include
.iter()
.map(|i| i.to_string())
.collect::<Vec<String>>()
.join(","),
);
}
if let Some(marker) = options.marker.as_ref() {
query_builder.set_pair("marker", marker);
}
if let Some(maxresults) = options.maxresults {
query_builder.set_pair("maxresults", maxresults.to_string());
}
if let Some(timeout) = options.timeout {
query_builder.set_pair("timeout", timeout.to_string());
}
query_builder.set_pair("where", filter_expression);
query_builder.build();
let mut request = Request::new(url, Method::Get);
request.insert_header("accept", "application/xml");
request.insert_header("content-type", "application/xml");
request.insert_header("x-ms-version", &self.version);
let rsp = self
.pipeline
.send(
&ctx,
&mut request,
Some(PipelineSendOptions {
check_success: CheckSuccessOptions {
success_codes: &[200],
},
..Default::default()
}),
)
.await?;
Ok(rsp.into())
}
#[tracing::function("Storage.Blob.Service.getAccountInfo")]
pub async fn get_account_info(
&self,
options: Option<BlobServiceClientGetAccountInfoOptions<'_>>,
) -> Result<Response<BlobServiceClientGetAccountInfoResult, NoFormat>> {
let options = options.unwrap_or_default();
let ctx = options.method_options.context.to_borrowed();
let mut url = self.endpoint.clone();
let mut query_builder = url.query_builder();
query_builder
.append_pair("comp", "properties")
.append_pair("restype", "account");
if let Some(timeout) = options.timeout {
query_builder.set_pair("timeout", timeout.to_string());
}
query_builder.build();
let mut request = Request::new(url, Method::Get);
request.insert_header("content-type", "application/xml");
request.insert_header("x-ms-version", &self.version);
let rsp = self
.pipeline
.send(
&ctx,
&mut request,
Some(PipelineSendOptions {
check_success: CheckSuccessOptions {
success_codes: &[200],
},
..Default::default()
}),
)
.await?;
Ok(rsp.into())
}
#[tracing::function("Storage.Blob.Service.getProperties")]
pub async fn get_properties(
&self,
options: Option<BlobServiceClientGetPropertiesOptions<'_>>,
) -> Result<Response<BlobServiceProperties, XmlFormat>> {
let options = options.unwrap_or_default();
let ctx = options.method_options.context.to_borrowed();
let mut url = self.endpoint.clone();
let mut query_builder = url.query_builder();
query_builder
.append_pair("comp", "properties")
.append_pair("restype", "service");
if let Some(timeout) = options.timeout {
query_builder.set_pair("timeout", timeout.to_string());
}
query_builder.build();
let mut request = Request::new(url, Method::Get);
request.insert_header("accept", "application/xml");
request.insert_header("content-type", "application/xml");
request.insert_header("x-ms-version", &self.version);
let rsp = self
.pipeline
.send(
&ctx,
&mut request,
Some(PipelineSendOptions {
check_success: CheckSuccessOptions {
success_codes: &[200],
},
..Default::default()
}),
)
.await?;
Ok(rsp.into())
}
#[tracing::function("Storage.Blob.Service.getStatistics")]
pub async fn get_statistics(
&self,
options: Option<BlobServiceClientGetStatisticsOptions<'_>>,
) -> Result<Response<StorageServiceStats, XmlFormat>> {
let options = options.unwrap_or_default();
let ctx = options.method_options.context.to_borrowed();
let mut url = self.endpoint.clone();
let mut query_builder = url.query_builder();
query_builder
.append_pair("comp", "stats")
.append_pair("restype", "service");
if let Some(timeout) = options.timeout {
query_builder.set_pair("timeout", timeout.to_string());
}
query_builder.build();
let mut request = Request::new(url, Method::Get);
request.insert_header("accept", "application/xml");
request.insert_header("content-type", "application/xml");
request.insert_header("x-ms-version", &self.version);
let rsp = self
.pipeline
.send(
&ctx,
&mut request,
Some(PipelineSendOptions {
check_success: CheckSuccessOptions {
success_codes: &[200],
},
..Default::default()
}),
)
.await?;
Ok(rsp.into())
}
#[tracing::function("Storage.Blob.Service.getUserDelegationKey")]
pub async fn get_user_delegation_key(
&self,
key_info: RequestContent<KeyInfo, XmlFormat>,
options: Option<BlobServiceClientGetUserDelegationKeyOptions<'_>>,
) -> Result<Response<UserDelegationKey, XmlFormat>> {
let options = options.unwrap_or_default();
let ctx = options.method_options.context.to_borrowed();
let mut url = self.endpoint.clone();
let mut query_builder = url.query_builder();
query_builder
.append_pair("comp", "userdelegationkey")
.append_pair("restype", "service");
if let Some(timeout) = options.timeout {
query_builder.set_pair("timeout", timeout.to_string());
}
query_builder.build();
let mut request = Request::new(url, Method::Post);
request.insert_header("accept", "application/xml");
request.insert_header("content-type", "application/xml");
request.insert_header("x-ms-version", &self.version);
request.set_body(key_info);
let rsp = self
.pipeline
.send(
&ctx,
&mut request,
Some(PipelineSendOptions {
check_success: CheckSuccessOptions {
success_codes: &[200],
},
..Default::default()
}),
)
.await?;
Ok(rsp.into())
}
#[tracing::function("Storage.Blob.Service.listContainersSegment")]
pub fn list_containers_segment(
&self,
options: Option<BlobServiceClientListContainersSegmentOptions<'_>>,
) -> Result<Pager<ListContainersSegmentResponse, XmlFormat, String>> {
let options = options.unwrap_or_default().into_owned();
let pipeline = self.pipeline.clone();
let mut first_url = self.endpoint.clone();
let mut query_builder = first_url.query_builder();
query_builder.append_pair("comp", "list");
if let Some(include) = options.include.as_ref() {
query_builder.set_pair(
"include",
include
.iter()
.map(|i| i.to_string())
.collect::<Vec<String>>()
.join(","),
);
}
if let Some(marker) = options.marker.as_ref() {
query_builder.set_pair("marker", marker);
}
if let Some(maxresults) = options.maxresults {
query_builder.set_pair("maxresults", maxresults.to_string());
}
if let Some(prefix) = options.prefix.as_ref() {
query_builder.set_pair("prefix", prefix);
}
if let Some(timeout) = options.timeout {
query_builder.set_pair("timeout", timeout.to_string());
}
query_builder.build();
let version = self.version.clone();
Ok(Pager::new(
move |marker: PagerState<String>, pager_options| {
let mut url = first_url.clone();
if let PagerState::More(marker) = marker {
let mut query_builder = url.query_builder();
query_builder.set_pair("marker", &marker);
query_builder.build();
}
let mut request = Request::new(url, Method::Get);
request.insert_header("accept", "application/xml");
request.insert_header("content-type", "application/xml");
request.insert_header("x-ms-version", &version);
let pipeline = pipeline.clone();
Box::pin(async move {
let rsp = pipeline
.send(
&pager_options.context,
&mut request,
Some(PipelineSendOptions {
check_success: CheckSuccessOptions {
success_codes: &[200],
},
..Default::default()
}),
)
.await?;
let (status, headers, body) = rsp.deconstruct();
let res: ListContainersSegmentResponse = xml::from_xml(&body)?;
let rsp = RawResponse::from_bytes(status, headers, body).into();
Ok(match res.next_marker {
Some(next_marker) if !next_marker.is_empty() => PagerResult::More {
response: rsp,
continuation: next_marker,
},
_ => PagerResult::Done { response: rsp },
})
})
},
Some(options.method_options),
))
}
#[tracing::function("Storage.Blob.Service.setProperties")]
pub async fn set_properties(
&self,
storage_service_properties: RequestContent<BlobServiceProperties, XmlFormat>,
options: Option<BlobServiceClientSetPropertiesOptions<'_>>,
) -> Result<Response<(), NoFormat>> {
let options = options.unwrap_or_default();
let ctx = options.method_options.context.to_borrowed();
let mut url = self.endpoint.clone();
let mut query_builder = url.query_builder();
query_builder
.append_pair("comp", "properties")
.append_pair("restype", "service");
if let Some(timeout) = options.timeout {
query_builder.set_pair("timeout", timeout.to_string());
}
query_builder.build();
let mut request = Request::new(url, Method::Put);
request.insert_header("content-type", "application/xml");
request.insert_header("x-ms-version", &self.version);
request.set_body(storage_service_properties);
let rsp = self
.pipeline
.send(
&ctx,
&mut request,
Some(PipelineSendOptions {
check_success: CheckSuccessOptions {
success_codes: &[202],
},
..Default::default()
}),
)
.await?;
Ok(rsp.into())
}
}
impl Default for BlobServiceClientOptions {
fn default() -> Self {
Self {
client_options: ClientOptions::default(),
version: String::from("2025-11-05"),
}
}
}