qiniu_download/config/
multi_clusters.rs1use 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#[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 #[inline]
35 pub fn builder() -> MultipleClustersConfigBuilder {
36 MultipleClustersConfigBuilder(Default::default())
37 }
38
39 #[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#[derive(Error, Debug)]
122#[non_exhaustive]
123pub enum MultipleClustersConfigParseError {
124 #[error("Parse config error: {0}")]
126 ParseError(#[from] ClustersConfigParseError),
127
128 #[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#[derive(Default, Debug)]
166pub struct MultipleClustersConfigBuilder(MultipleClustersConfig);
167
168impl MultipleClustersConfigBuilder {
169 #[inline]
171 pub fn build(self) -> MultipleClustersConfig {
172 self.0
173 }
174
175 #[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 #[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}