Skip to main content

azure_core/
cloud.rs

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4//! Azure cloud configuration.
5
6use std::{any::TypeId, collections::HashMap};
7
8/// Configurations for different Azure clouds.
9#[derive(Clone, Debug, Default, Eq, PartialEq)]
10#[non_exhaustive]
11pub enum CloudConfiguration {
12    /// Azure Public Cloud
13    #[default]
14    AzurePublic,
15
16    /// Azure Government
17    AzureGovernment,
18
19    /// Azure in China
20    AzureChina,
21
22    /// A custom cloud.
23    ///
24    /// # Example
25    ///
26    /// ```
27    /// # mod azure_service_module {
28    /// #   pub struct Audience;
29    /// # }
30    ///
31    /// use azure_core::{
32    ///     cloud::{Audiences, CloudConfiguration, CustomConfiguration},
33    ///     http::ClientOptions,
34    /// };
35    ///
36    /// let mut custom = CustomConfiguration::default();
37    /// custom.audiences = Audiences::new()
38    ///     .with::<azure_service_module::Audience>("https://service.mycloud.local".to_string());
39    /// custom.authority_host = "https://login.mycloud.local".to_string();
40    /// let cloud: CloudConfiguration = custom.into();
41    /// ```
42    Custom(CustomConfiguration),
43}
44
45/// Configuration for a custom cloud.
46#[derive(Clone, Debug, Default, Eq, PartialEq)]
47#[non_exhaustive]
48pub struct CustomConfiguration {
49    /// Base URL for authentication, for example "https://login.microsoftonline.com"
50    pub authority_host: String,
51
52    /// Map of SDK modules to their Entra ID audiences.
53    pub audiences: Audiences,
54}
55
56impl From<CustomConfiguration> for CloudConfiguration {
57    fn from(config: CustomConfiguration) -> Self {
58        Self::Custom(config)
59    }
60}
61
62/// Collection of audiences for an Azure cloud's services
63#[derive(Clone, Debug, Default, Eq, PartialEq)]
64pub struct Audiences(HashMap<TypeId, String>);
65
66impl Audiences {
67    /// Create an empty `Audiences` map.
68    pub fn new() -> Self {
69        Self(HashMap::new())
70    }
71
72    /// Get a module's audience.
73    pub fn get<T: 'static>(&self) -> Option<&str> {
74        self.0.get(&TypeId::of::<T>()).map(|s| s.as_str())
75    }
76
77    /// Insert or replace an audience.
78    pub fn insert<T: 'static>(&mut self, audience: String) {
79        self.0.insert(TypeId::of::<T>(), audience);
80    }
81
82    /// Insert or replace an audience and return `Self` to allow chaining.
83    pub fn with<T: 'static>(mut self, audience: String) -> Self {
84        self.0.insert(TypeId::of::<T>(), audience);
85        self
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn custom() {
95        struct A;
96        struct B;
97        struct C;
98
99        let cloud = CustomConfiguration {
100            authority_host: "https://login.mycloud.local".to_string(),
101            audiences: Audiences::new()
102                .with::<A>("A".to_string())
103                .with::<B>("B".to_string()),
104        }
105        .into();
106
107        let CloudConfiguration::Custom(mut custom) = cloud else {
108            unreachable!();
109        };
110
111        assert_eq!(custom.authority_host, "https://login.mycloud.local");
112        assert_eq!(custom.audiences.get::<A>(), Some("A"));
113        assert_eq!(custom.audiences.get::<B>(), Some("B"));
114        assert_eq!(custom.audiences.get::<C>(), None);
115
116        custom.audiences.insert::<C>("C".to_string());
117        assert_eq!(custom.audiences.get::<C>(), Some("C"));
118    }
119
120    #[test]
121    fn default() {
122        assert_eq!(
123            CloudConfiguration::AzurePublic,
124            CloudConfiguration::default()
125        );
126    }
127}