opendal/services/azfile/
config.rs1use std::fmt::Debug;
19use std::fmt::Formatter;
20
21use super::backend::AzfileBuilder;
22use serde::Deserialize;
23use serde::Serialize;
24
25#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
27pub struct AzfileConfig {
28 pub root: Option<String>,
30 pub endpoint: Option<String>,
32 pub share_name: String,
34 pub account_name: Option<String>,
36 pub account_key: Option<String>,
38 pub sas_token: Option<String>,
40}
41
42impl Debug for AzfileConfig {
43 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
44 let mut ds = f.debug_struct("AzfileConfig");
45
46 ds.field("root", &self.root);
47 ds.field("share_name", &self.share_name);
48 ds.field("endpoint", &self.endpoint);
49
50 if self.account_name.is_some() {
51 ds.field("account_name", &"<redacted>");
52 }
53 if self.account_key.is_some() {
54 ds.field("account_key", &"<redacted>");
55 }
56 if self.sas_token.is_some() {
57 ds.field("sas_token", &"<redacted>");
58 }
59
60 ds.finish()
61 }
62}
63
64impl crate::Configurator for AzfileConfig {
65 type Builder = AzfileBuilder;
66
67 fn from_uri(uri: &crate::types::OperatorUri) -> crate::Result<Self> {
68 let authority = uri.authority().ok_or_else(|| {
69 crate::Error::new(crate::ErrorKind::ConfigInvalid, "uri authority is required")
70 .with_context("service", crate::Scheme::Azfile)
71 })?;
72
73 let mut map = uri.options().clone();
74 map.insert("endpoint".to_string(), format!("https://{authority}"));
75
76 if let Some(host) = uri.name() {
77 if let Some(account) = host.split('.').next() {
78 if !account.is_empty() {
79 map.entry("account_name".to_string())
80 .or_insert_with(|| account.to_string());
81 }
82 }
83 }
84
85 if let Some(root) = uri.root() {
86 if let Some((share, rest)) = root.split_once('/') {
87 if share.is_empty() {
88 return Err(crate::Error::new(
89 crate::ErrorKind::ConfigInvalid,
90 "share name is required in uri path",
91 )
92 .with_context("service", crate::Scheme::Azfile));
93 }
94 map.insert("share_name".to_string(), share.to_string());
95 if !rest.is_empty() {
96 map.insert("root".to_string(), rest.to_string());
97 }
98 } else if !root.is_empty() {
99 map.insert("share_name".to_string(), root.to_string());
100 }
101 }
102
103 if !map.contains_key("share_name") {
104 return Err(crate::Error::new(
105 crate::ErrorKind::ConfigInvalid,
106 "share name is required",
107 )
108 .with_context("service", crate::Scheme::Azfile));
109 }
110
111 Self::from_iter(map)
112 }
113
114 #[allow(deprecated)]
115 fn into_builder(self) -> Self::Builder {
116 AzfileBuilder {
117 config: self,
118 http_client: None,
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::Configurator;
127 use crate::types::OperatorUri;
128
129 #[test]
130 fn from_uri_sets_endpoint_share_root_and_account() {
131 let uri = OperatorUri::new(
132 "azfile://account.file.core.windows.net/share/documents/reports",
133 Vec::<(String, String)>::new(),
134 )
135 .unwrap();
136
137 let cfg = AzfileConfig::from_uri(&uri).unwrap();
138 assert_eq!(
139 cfg.endpoint.as_deref(),
140 Some("https://account.file.core.windows.net")
141 );
142 assert_eq!(cfg.share_name, "share".to_string());
143 assert_eq!(cfg.root.as_deref(), Some("documents/reports"));
144 assert_eq!(cfg.account_name.as_deref(), Some("account"));
145 }
146
147 #[test]
148 fn from_uri_accepts_share_from_query() {
149 let uri = OperatorUri::new(
150 "azfile://account.file.core.windows.net",
151 vec![("share_name".to_string(), "data".to_string())],
152 )
153 .unwrap();
154
155 let cfg = AzfileConfig::from_uri(&uri).unwrap();
156 assert_eq!(
157 cfg.endpoint.as_deref(),
158 Some("https://account.file.core.windows.net")
159 );
160 assert_eq!(cfg.share_name, "data".to_string());
161 }
162}