1use crate::*;
3
4#[allow(unused_imports)]
5use self::file::FileLoader;
6use std::path::PathBuf;
7
8pub mod key {
10 pub use crate::key::{CacheKey, PartialKey, PartialKeyCollector};
11}
12pub use super::configuration::ManualSource;
13pub use memory::ConfigSourceBuilder;
14
15pub(crate) mod cargo;
16pub(crate) mod environment;
17pub(crate) mod file;
18pub(crate) mod memory;
19#[doc(hidden)]
20#[cfg(feature = "rand")]
21#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
22pub(crate) mod random;
23
24#[allow(dead_code)]
25#[derive(Debug, FromConfig)]
26#[config(crate = "crate")]
27pub(crate) struct EnabledOption {
28 #[config(default = true)]
29 pub(crate) enabled: bool,
30}
31
32macro_rules! file_block {
33 ($($nm:ident.$name:literal.$file:literal: $($k:pat)|* => $x:path,)+) => {
34$(
35#[doc(hidden)]
36#[cfg(feature = $name)]
37#[cfg_attr(docsrs, doc(cfg(feature = $name)))]
38pub mod $nm;
39)+
40
41#[derive(Debug, FromConfig)]
42#[config(prefix = "app.sources", crate = "crate")]
43pub(crate) struct SourceOption {
44 #[cfg(feature = "rand")]
45 pub(crate) random: EnabledOption,
46 $(
47 #[cfg(feature = $name)]
48 $nm: EnabledOption,
49 )+
50}
51
52#[inline]
53#[allow(unreachable_code, unused_variables, unused_mut)]
54pub(crate) fn register_by_ext(
55 mut config: Configuration,
56 path: PathBuf,
57 required: bool,
58) -> Result<Configuration, ConfigError> {
59 let ext = path
60 .extension()
61 .and_then(|x| x.to_str())
62 .ok_or_else(|| ConfigError::ConfigFileNotSupported(path.clone()))?;
63 match ext {
64 $(
65 #[cfg(feature = $name)]
66 $($k)|* => {
67 config = config.register_source(<FileLoader<$x>>::new(
68 path.clone(),
69 required,
70 true,
71 ))?;
72 }
73 )+
74 _ => return Err(ConfigError::ConfigFileNotSupported(path)),
75 }
76 Ok(config)
77}
78
79#[allow(unused_mut, unused_variables)]
80pub(crate) fn register_files(
81 mut config: Configuration,
82 option: &SourceOption,
83 path: PathBuf,
84 has_ext: bool,
85) -> Result<Configuration, ConfigError> {
86 $(
87 #[cfg(feature = $name)]
88 if option.$nm.enabled {
89 config =
90 config.register_source(<FileLoader<$x>>::new(path.clone(), false, has_ext))?;
91 }
92 )+
93 Ok(config)
94}
95
96
97#[cfg_attr(coverage_nightly, coverage(off))]
98#[cfg(test)]
99mod test {
100 $(
101 #[test]
102 #[cfg(not(feature = $name))]
103 fn $nm() {
104 use super::memory::HashSource;
105 use crate::*;
106
107 let _v: Result<HashSource, ConfigError> = inline_source!($file);
108 match _v {
109 Err(ConfigError::ConfigFileNotSupported(_)) =>{}
110 _ => assert_eq!(true, false),
111 }
112 }
113 )+
114}
115 };
116}
117
118file_block!(
119 toml."toml"."../../app.toml" : "toml" | "tml" => toml::Toml,
120 yaml."yaml"."../../app.yaml" : "yaml" | "yml" => yaml::Yaml,
121 json."json"."../../app.json" : "json" => json::Json,
122 ini."ini"."../../app.ini" : "ini" => ini::Ini,
123);
124
125#[macro_export]
127macro_rules! inline_source {
128 ($path:literal) => {
129 $crate::inline_source_internal!(
130 $path:
131 toml."toml": "toml" | "tml" => $crate::source::toml::Toml,
132 yaml."yaml": "yaml" | "yml" => $crate::source::yaml::Yaml,
133 json."json": "json" => $crate::source::json::Json,
134 ini."ini": "ini" => $crate::source::ini::Ini,
135 )
136 };
137}
138
139#[doc(hidden)]
140#[macro_export]
141macro_rules! inline_source_internal {
142 ($path:literal: $($nm:ident.$name:literal: $($k:pat)|* => $x:path,)+) => {
143 match $path.rsplit_once(".") {
144 Some((_, ext)) => {
145 let _name = format!("inline:{}", $path);
146 let _content = include_str!($path);
147 match ext {
148 $(
149 #[cfg(feature = $name)]
150 $($k)|* => $crate::inline_source_config::<$x>(_name, _content),
151 )+
152 _ => Err($crate::ConfigError::ConfigFileNotSupported($path.into()))
153 }
154 }
155 _ => Err($crate::ConfigError::ConfigFileNotSupported($path.into()))
156 }
157 };
158}
159
160pub trait ConfigSourceAdaptor {
170 fn convert_source(self, builder: &mut ConfigSourceBuilder<'_>) -> Result<(), ConfigError>;
172}
173
174pub trait ConfigSourceParser: Send {
176 type Adaptor: ConfigSourceAdaptor;
178
179 fn parse_source(_: &str) -> Result<Self::Adaptor, ConfigError>;
181
182 fn file_extensions() -> Vec<&'static str>;
184}
185
186pub trait ConfigSource: Send {
195 fn name(&self) -> &str;
197
198 fn load(&self, builder: &mut ConfigSourceBuilder<'_>) -> Result<(), ConfigError>;
200
201 fn allow_refresh(&self) -> bool {
203 false
204 }
205
206 fn refreshable(&self) -> Result<bool, ConfigError> {
210 Ok(false)
211 }
212}