opendal/services/azdls/
config.rs1use std::fmt::Debug;
19use std::fmt::Formatter;
20
21use super::backend::AzdlsBuilder;
22use serde::Deserialize;
23use serde::Serialize;
24
25#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
27pub struct AzdlsConfig {
28 pub root: Option<String>,
30 pub filesystem: String,
32 pub endpoint: Option<String>,
34 pub account_name: Option<String>,
36 pub account_key: Option<String>,
39 pub client_secret: Option<String>,
43 pub tenant_id: Option<String>,
47 pub client_id: Option<String>,
51 pub sas_token: Option<String>,
55 pub authority_host: Option<String>,
60}
61
62impl Debug for AzdlsConfig {
63 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
64 let mut ds = f.debug_struct("AzdlsConfig");
65
66 ds.field("root", &self.root);
67 ds.field("filesystem", &self.filesystem);
68 ds.field("endpoint", &self.endpoint);
69
70 if self.account_name.is_some() {
71 ds.field("account_name", &"<redacted>");
72 }
73 if self.account_key.is_some() {
74 ds.field("account_key", &"<redacted>");
75 }
76 if self.client_secret.is_some() {
77 ds.field("client_secret", &"<redacted>");
78 }
79 if self.tenant_id.is_some() {
80 ds.field("tenant_id", &"<redacted>");
81 }
82 if self.client_id.is_some() {
83 ds.field("client_id", &"<redacted>");
84 }
85 if self.sas_token.is_some() {
86 ds.field("sas_token", &"<redacted>");
87 }
88 ds.finish()
89 }
90}
91
92impl crate::Configurator for AzdlsConfig {
93 type Builder = AzdlsBuilder;
94
95 fn from_uri(uri: &crate::types::OperatorUri) -> crate::Result<Self> {
96 let authority = uri.authority().ok_or_else(|| {
97 crate::Error::new(crate::ErrorKind::ConfigInvalid, "uri authority is required")
98 .with_context("service", crate::Scheme::Azdls)
99 })?;
100
101 let mut map = uri.options().clone();
102 map.insert("endpoint".to_string(), format!("https://{authority}"));
103
104 if let Some(host) = uri.name() {
105 if let Some(account) = host.split('.').next() {
106 if !account.is_empty() {
107 map.entry("account_name".to_string())
108 .or_insert_with(|| account.to_string());
109 }
110 }
111 }
112
113 if let Some(root) = uri.root() {
114 if let Some((filesystem, rest)) = root.split_once('/') {
115 if filesystem.is_empty() {
116 return Err(crate::Error::new(
117 crate::ErrorKind::ConfigInvalid,
118 "filesystem is required in uri path",
119 )
120 .with_context("service", crate::Scheme::Azdls));
121 }
122 map.insert("filesystem".to_string(), filesystem.to_string());
123 if !rest.is_empty() {
124 map.insert("root".to_string(), rest.to_string());
125 }
126 } else if !root.is_empty() {
127 map.insert("filesystem".to_string(), root.to_string());
128 }
129 }
130
131 if !map.contains_key("filesystem") {
132 return Err(crate::Error::new(
133 crate::ErrorKind::ConfigInvalid,
134 "filesystem is required",
135 )
136 .with_context("service", crate::Scheme::Azdls));
137 }
138
139 Self::from_iter(map)
140 }
141
142 #[allow(deprecated)]
143 fn into_builder(self) -> Self::Builder {
144 AzdlsBuilder {
145 config: self,
146 http_client: None,
147 }
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use crate::Configurator;
155 use crate::types::OperatorUri;
156
157 #[test]
158 fn from_uri_sets_endpoint_filesystem_root_and_account() {
159 let uri = OperatorUri::new(
160 "azdls://account.dfs.core.windows.net/fs/data/2024",
161 Vec::<(String, String)>::new(),
162 )
163 .unwrap();
164
165 let cfg = AzdlsConfig::from_uri(&uri).unwrap();
166 assert_eq!(
167 cfg.endpoint.as_deref(),
168 Some("https://account.dfs.core.windows.net")
169 );
170 assert_eq!(cfg.filesystem, "fs".to_string());
171 assert_eq!(cfg.root.as_deref(), Some("data/2024"));
172 assert_eq!(cfg.account_name.as_deref(), Some("account"));
173 }
174
175 #[test]
176 fn from_uri_accepts_filesystem_from_query() {
177 let uri = OperatorUri::new(
178 "azdls://account.dfs.core.windows.net",
179 vec![("filesystem".to_string(), "logs".to_string())],
180 )
181 .unwrap();
182
183 let cfg = AzdlsConfig::from_uri(&uri).unwrap();
184 assert_eq!(cfg.filesystem, "logs".to_string());
185 }
186}