opendal_core/services/azfile/
config.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::fmt::Debug;
19
20use serde::Deserialize;
21use serde::Serialize;
22
23use super::AZFILE_SCHEME;
24use super::backend::AzfileBuilder;
25
26/// Azure File services support.
27#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
28pub struct AzfileConfig {
29    /// The root path for azfile.
30    pub root: Option<String>,
31    /// The endpoint for azfile.
32    pub endpoint: Option<String>,
33    /// The share name for azfile.
34    pub share_name: String,
35    /// The account name for azfile.
36    pub account_name: Option<String>,
37    /// The account key for azfile.
38    pub account_key: Option<String>,
39    /// The sas token for azfile.
40    pub sas_token: Option<String>,
41}
42
43impl Debug for AzfileConfig {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        f.debug_struct("AzfileConfig")
46            .field("root", &self.root)
47            .field("endpoint", &self.endpoint)
48            .field("share_name", &self.share_name)
49            .finish_non_exhaustive()
50    }
51}
52
53impl crate::Configurator for AzfileConfig {
54    type Builder = AzfileBuilder;
55
56    fn from_uri(uri: &crate::types::OperatorUri) -> crate::Result<Self> {
57        let mut map = uri.options().clone();
58        if let Some(authority) = uri.authority() {
59            map.insert("endpoint".to_string(), format!("https://{authority}"));
60        }
61
62        if let Some(host) = uri.name() {
63            if let Some(account) = host.split('.').next() {
64                if !account.is_empty() {
65                    map.entry("account_name".to_string())
66                        .or_insert_with(|| account.to_string());
67                }
68            }
69        }
70
71        if let Some(root) = uri.root() {
72            if let Some((share, rest)) = root.split_once('/') {
73                if share.is_empty() {
74                    return Err(crate::Error::new(
75                        crate::ErrorKind::ConfigInvalid,
76                        "share name is required in uri path",
77                    )
78                    .with_context("service", AZFILE_SCHEME));
79                }
80                map.insert("share_name".to_string(), share.to_string());
81                if !rest.is_empty() {
82                    map.insert("root".to_string(), rest.to_string());
83                }
84            } else if !root.is_empty() {
85                map.insert("share_name".to_string(), root.to_string());
86            }
87        }
88
89        if !map.contains_key("share_name") {
90            return Err(crate::Error::new(
91                crate::ErrorKind::ConfigInvalid,
92                "share name is required",
93            )
94            .with_context("service", AZFILE_SCHEME));
95        }
96
97        Self::from_iter(map)
98    }
99
100    #[allow(deprecated)]
101    fn into_builder(self) -> Self::Builder {
102        AzfileBuilder {
103            config: self,
104            http_client: None,
105        }
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use crate::Configurator;
113    use crate::types::OperatorUri;
114
115    #[test]
116    fn from_uri_sets_endpoint_share_root_and_account() {
117        let uri = OperatorUri::new(
118            "azfile://account.file.core.windows.net/share/documents/reports",
119            Vec::<(String, String)>::new(),
120        )
121        .unwrap();
122
123        let cfg = AzfileConfig::from_uri(&uri).unwrap();
124        assert_eq!(
125            cfg.endpoint.as_deref(),
126            Some("https://account.file.core.windows.net")
127        );
128        assert_eq!(cfg.share_name, "share".to_string());
129        assert_eq!(cfg.root.as_deref(), Some("documents/reports"));
130        assert_eq!(cfg.account_name.as_deref(), Some("account"));
131    }
132
133    #[test]
134    fn from_uri_accepts_share_from_query() {
135        let uri = OperatorUri::new(
136            "azfile://account.file.core.windows.net",
137            vec![("share_name".to_string(), "data".to_string())],
138        )
139        .unwrap();
140
141        let cfg = AzfileConfig::from_uri(&uri).unwrap();
142        assert_eq!(
143            cfg.endpoint.as_deref(),
144            Some("https://account.file.core.windows.net")
145        );
146        assert_eq!(cfg.share_name, "data".to_string());
147    }
148}