qiniu_download/config/
multi_clusters.rs

1use super::{single_cluster::Config, ClustersConfigParseError, Timeouts};
2use once_cell::sync::Lazy;
3use serde::Deserialize;
4use std::{
5    collections::{HashMap, HashSet},
6    convert::TryFrom,
7    fmt, fs,
8    io::Error as IOError,
9    mem::swap,
10    path::{Path, PathBuf},
11    sync::Arc,
12};
13use tap::TapFallible;
14use thiserror::Error;
15
16type SelectConfigFn = Arc<
17    dyn for<'a> Fn(&'a HashMap<String, Config>, &str) -> Option<&'a Config> + Send + Sync + 'static,
18>;
19
20static DEFAULT_CONFIG_SELECT_CALLBACK: Lazy<SelectConfigFn> =
21    Lazy::new(|| Arc::new(default_select_config));
22
23/// 多集群七牛配置信息
24#[derive(Clone, Deserialize)]
25#[serde(try_from = "HashMap<String, PathBuf>")]
26pub struct MultipleClustersConfig {
27    configs: HashMap<String, Config>,
28    original_path: Option<PathBuf>,
29    select_config: SelectConfigFn,
30}
31
32impl MultipleClustersConfig {
33    /// 创建多集群七牛配置信息构建器
34    #[inline]
35    pub fn builder() -> MultipleClustersConfigBuilder {
36        MultipleClustersConfigBuilder(Default::default())
37    }
38
39    /// 设置配置选取回调函数,提供多集群配置信息和当前要访问的对象名称,返回要使用的配置信息
40    #[inline]
41    pub fn set_config_select_callback(
42        &mut self,
43        f: impl for<'a> Fn(&'a HashMap<String, Config>, &str) -> Option<&'a Config>
44            + Send
45            + Sync
46            + 'static,
47    ) -> &mut Self {
48        self.set_config_select_callback_raw(Arc::new(f));
49        self
50    }
51
52    pub(super) fn take_config_select_callback(&mut self) -> SelectConfigFn {
53        let mut new_config_select_callback = DEFAULT_CONFIG_SELECT_CALLBACK.to_owned();
54        swap(&mut self.select_config, &mut new_config_select_callback);
55        new_config_select_callback
56    }
57
58    pub(super) fn set_config_select_callback_raw(&mut self, callback: SelectConfigFn) {
59        self.select_config = callback;
60    }
61
62    pub(super) fn with_key<T>(&self, key: &str, f: impl FnOnce(&Config) -> T) -> Option<T> {
63        (self.select_config)(&self.configs, key).map(f)
64    }
65
66    pub(super) fn parse(path: &Path, bytes: &[u8]) -> Result<Self, ClustersConfigParseError> {
67        match path.extension().and_then(|s| s.to_str()) {
68            Some("toml") => toml::from_slice(bytes).map_err(|err| err.into()),
69            Some("json") => serde_json::from_slice(bytes).map_err(|err| err.into()),
70            _ => panic!("QINIU env can only support to be given .toml or .json file"),
71        }
72        .tap_ok_mut(|config: &mut Self| {
73            config.original_path = Some(path.to_owned());
74        })
75    }
76
77    pub(super) fn config_paths(&self) -> Vec<PathBuf> {
78        let mut paths = self
79            .original_path
80            .as_ref()
81            .map(|path| vec![path.to_owned()])
82            .unwrap_or_default();
83        paths.extend(
84            self.configs
85                .iter()
86                .filter_map(|(_, config)| config.original_path())
87                .map(|path| path.to_owned()),
88        );
89        paths
90    }
91
92    pub(super) fn timeouts_set(&self) -> HashSet<Timeouts> {
93        self.configs.values().map(Timeouts::from).collect()
94    }
95}
96
97impl TryFrom<HashMap<String, PathBuf>> for MultipleClustersConfig {
98    type Error = MultipleClustersConfigParseError;
99
100    fn try_from(configs: HashMap<String, PathBuf>) -> Result<Self, Self::Error> {
101        Ok(Self {
102            configs: configs
103                .into_iter()
104                .map(|(name, path)| {
105                    fs::read(&path)
106                        .map_err(MultipleClustersConfigParseError::from)
107                        .and_then(|bytes| {
108                            Config::parse(&path, &bytes)
109                                .map_err(MultipleClustersConfigParseError::from)
110                        })
111                        .map(|config| (name, config))
112                })
113                .collect::<Result<_, _>>()?,
114            original_path: None,
115            select_config: DEFAULT_CONFIG_SELECT_CALLBACK.to_owned(),
116        })
117    }
118}
119
120/// 多集群七牛配置信息解析错误
121#[derive(Error, Debug)]
122#[non_exhaustive]
123pub enum MultipleClustersConfigParseError {
124    /// 多集群七牛配置信息解析错误
125    #[error("Parse config error: {0}")]
126    ParseError(#[from] ClustersConfigParseError),
127
128    /// 多集群七牛配置信息读取 I/O 错误
129    #[error("I/O error: {0}")]
130    IOError(#[from] IOError),
131}
132
133impl Default for MultipleClustersConfig {
134    #[inline]
135    fn default() -> Self {
136        Self {
137            configs: Default::default(),
138            original_path: None,
139            select_config: DEFAULT_CONFIG_SELECT_CALLBACK.to_owned(),
140        }
141    }
142}
143
144impl fmt::Debug for MultipleClustersConfig {
145    #[inline]
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        f.debug_struct("MultipleClustersConfig")
148            .field("configs", &self.configs)
149            .field("original_path", &self.original_path)
150            .finish()
151    }
152}
153
154fn default_select_config<'a>(
155    configs: &'a HashMap<String, Config>,
156    key: &str,
157) -> Option<&'a Config> {
158    configs
159        .iter()
160        .find(|(name, _)| Path::new(key).starts_with(Path::new(name.as_str())))
161        .map(|(_, config)| config)
162}
163
164/// 多集群七牛配置信息构建器
165#[derive(Default, Debug)]
166pub struct MultipleClustersConfigBuilder(MultipleClustersConfig);
167
168impl MultipleClustersConfigBuilder {
169    /// 构建多集群七牛配置信息
170    #[inline]
171    pub fn build(self) -> MultipleClustersConfig {
172        self.0
173    }
174
175    /// 增加集群配置
176    #[inline]
177    pub fn add_cluster(mut self, name: impl Into<String>, config: Config) -> Self {
178        self.0.configs.insert(name.into(), config);
179        self
180    }
181
182    #[inline]
183    #[cfg(test)]
184    pub(super) fn original_path(mut self, original_path: Option<PathBuf>) -> Self {
185        self.0.original_path = original_path;
186        self
187    }
188
189    /// 配置选取回调函数,提供多集群配置信息和当前要访问的对象名称,返回要使用的配置信息
190    #[inline]
191    pub fn config_select_callback(
192        mut self,
193        f: impl for<'a> Fn(&'a HashMap<String, Config>, &str) -> Option<&'a Config>
194            + Send
195            + Sync
196            + 'static,
197    ) -> Self {
198        self.0.set_config_select_callback(f);
199        self
200    }
201}