Skip to main content

azure_storage_blob/clients/
blob_service_client.rs

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4pub use crate::generated::clients::{BlobServiceClient, BlobServiceClientOptions};
5
6use crate::{BlobClient, BlobContainerClient};
7use azure_core::{
8    credentials::TokenCredential,
9    http::{
10        policies::{auth::BearerTokenAuthorizationPolicy, Policy},
11        Pipeline, Url,
12    },
13    tracing, Result,
14};
15use std::sync::Arc;
16
17impl BlobServiceClient {
18    /// Creates a new BlobServiceClient from a service URL.
19    ///
20    /// # Arguments
21    ///
22    /// * `service_url` - The full URL of the Azure storage account, for example `https://myaccount.blob.core.windows.net/`.
23    ///   The caller is responsible for percent-encoding the URL correctly; it will be used as-is.
24    /// * `credential` - An optional implementation of [`TokenCredential`] that can provide an Entra ID token to use when authenticating.
25    /// * `options` - Optional configuration for the client.
26    #[tracing::new("Storage.Blob.Service")]
27    pub fn new(
28        service_url: Url,
29        credential: Option<Arc<dyn TokenCredential>>,
30        options: Option<BlobServiceClientOptions>,
31    ) -> Result<Self> {
32        // Storage endpoints must be base URLs.
33        if service_url.cannot_be_a_base() {
34            return Err(azure_core::Error::with_message(
35                azure_core::error::ErrorKind::Other,
36                format!("{service_url} is not a valid base URL"),
37            ));
38        }
39        let mut options = options.unwrap_or_default();
40        super::apply_client_defaults(&mut options.client_options);
41
42        let mut per_retry_policies: Vec<Arc<dyn Policy>> = Vec::default();
43        if let Some(token_credential) = credential {
44            if !service_url.scheme().starts_with("https") {
45                return Err(azure_core::Error::with_message(
46                    azure_core::error::ErrorKind::Other,
47                    format!("{service_url} must use https"),
48                ));
49            }
50            per_retry_policies.push(Arc::new(BearerTokenAuthorizationPolicy::new(
51                token_credential,
52                vec!["https://storage.azure.com/.default"],
53            )));
54        }
55
56        let pipeline = Pipeline::new(
57            option_env!("CARGO_PKG_NAME"),
58            option_env!("CARGO_PKG_VERSION"),
59            options.client_options.clone(),
60            Vec::default(),
61            per_retry_policies,
62            None,
63        );
64
65        Ok(Self {
66            endpoint: service_url,
67            version: options.version,
68            pipeline,
69        })
70    }
71
72    /// Returns a new instance of BlobContainerClient.
73    ///
74    /// # Arguments
75    ///
76    /// * `container_name` - The name of the container.
77    pub fn blob_container_client(&self, container_name: &str) -> BlobContainerClient {
78        let mut container_url = self.url().clone();
79        container_url
80            .path_segments_mut()
81            // This should not fail as service URL has already been validated on client construction.
82            .expect("Cannot be a base URL.")
83            .push(container_name);
84
85        BlobContainerClient {
86            endpoint: container_url,
87            pipeline: self.pipeline.clone(),
88            version: self.version.clone(),
89            tracer: self.tracer.clone(),
90        }
91    }
92
93    /// Returns a new instance of BlobClient.
94    ///
95    /// # Arguments
96    ///
97    /// * `container_name` - The name of the container.
98    /// * `blob_name` - The name of the blob.
99    pub fn blob_client(&self, container_name: &str, blob_name: &str) -> BlobClient {
100        let mut blob_url = self.url().clone();
101        blob_url
102            .path_segments_mut()
103            // This should not fail as service URL has already been validated on client construction.
104            .expect("Cannot be a base URL.")
105            .extend([container_name, blob_name]);
106
107        BlobClient {
108            endpoint: blob_url,
109            pipeline: self.pipeline.clone(),
110            version: self.version.clone(),
111            tracer: self.tracer.clone(),
112        }
113    }
114
115    /// Gets the URL of the resource this client is configured for.
116    pub fn url(&self) -> &Url {
117        &self.endpoint
118    }
119}