1use parking_lot::Mutex;
2#[cfg(feature = "app")]
3use std::any::Any;
4use std::collections::HashMap;
5
6#[cfg(feature = "args")]
7use crate::AppInfo;
8
9use crate::{
10 raw_ioref::IORefT, source_raw::PropertyRegistryInternal, Environment, FromEnvironment,
11 PropertySource, Res,
12};
13#[cfg(feature = "app")]
14use crate::{Resource, ResourceBuilder, ResourceRegistry};
15
16#[allow(unused_imports)]
17use crate::source_raw::FileConfig;
18#[cfg(feature = "derive")]
19use crate::{DescFromEnvironment, Key, KeyDesc, PrefixedFromEnvironment, SalakDescContext};
20
21#[allow(missing_debug_implementations)]
23pub struct SalakBuilder {
24 args: HashMap<String, String>,
25 #[cfg(any(feature = "toml", feature = "yaml"))]
26 disable_file: bool,
27 #[cfg(feature = "rand")]
28 disable_random: bool,
29 registry: PropertyRegistryInternal<'static>,
30 #[cfg(any(feature = "args", feature = "derive"))]
31 pub(crate) app_desc: Vec<Box<dyn Fn(&mut Salak) -> Vec<KeyDesc>>>,
32 #[cfg(feature = "args")]
33 app_info: Option<AppInfo<'static>>,
34 iorefs: Mutex<Vec<Box<dyn IORefT + Send>>>,
35 #[cfg(feature = "app")]
36 resource: ResourceRegistry,
37}
38
39#[allow(dead_code)]
40pub(crate) const PREFIX: &str = "salak.app";
41
42impl SalakBuilder {
43 #[inline]
45 pub fn set_args(mut self, args: HashMap<String, String>) -> Self {
46 self.args.extend(args);
47 self
48 }
49
50 pub fn set<K: Into<String>, V: Into<String>>(mut self, k: K, v: V) -> Self {
52 self.args.insert(k.into(), v.into());
53 self
54 }
55
56 #[cfg(any(feature = "toml", feature = "yaml"))]
57 #[cfg_attr(docsrs, doc(cfg(any(feature = "toml", feature = "yaml"))))]
58 pub fn configure_files(mut self, enabled: bool) -> Self {
60 self.disable_file = !enabled;
61 self
62 }
63
64 #[cfg(feature = "rand")]
65 #[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
66 pub fn configure_random(mut self, enabled: bool) -> Self {
68 self.disable_random = !enabled;
69 self
70 }
71
72 #[cfg(feature = "args")]
73 #[cfg_attr(docsrs, doc(cfg(feature = "args")))]
74 pub fn configure_args(mut self, info: AppInfo<'static>) -> Self {
76 self.app_info = Some(info);
77 self
78 }
79
80 #[allow(unused_mut)]
82 pub fn build(mut self) -> Res<Salak> {
83 #[cfg(feature = "derive")]
84 let mut _desc: Vec<KeyDesc> = vec![];
85 #[cfg(feature = "derive")]
86 #[cfg(any(feature = "toml", feature = "yaml"))]
87 {
88 self.app_desc
89 .insert(0, Box::new(|env| env.get_desc::<FileConfig>("")));
90 }
91 let mut env = self.registry;
92
93 #[cfg(feature = "rand")]
94 if !self.disable_random {
95 env.register_by_ref(Box::new(crate::source_rand::Random));
96 }
97 let mut salak = Salak {
98 reg: env,
99 ior: self.iorefs,
100 #[cfg(feature = "app")]
101 res: self.resource,
102 };
103
104 #[cfg(feature = "args")]
105 if let Some(app) = self.app_info {
106 self.args
107 .insert(format!("{}.name", PREFIX), app.name.into());
108 self.args
109 .insert(format!("{}.version", PREFIX), app.version.into());
110
111 #[cfg(feature = "derive")]
112 {
113 _desc.push(KeyDesc::new(
114 format!("{}.name", PREFIX),
115 "String",
116 Some(false),
117 Some(app.name),
118 None,
119 ));
120 _desc.push(KeyDesc::new(
121 format!("{}.version", PREFIX),
122 "String",
123 Some(false),
124 Some(app.version),
125 None,
126 ));
127
128 for x in self.app_desc {
129 _desc.extend((x)(&mut salak));
130 }
131 }
132
133 self.args.extend(crate::source::from_args(_desc, app)?);
134 }
135
136 salak.reg = salak
137 .reg
138 .register(crate::source::HashMapSource::new("Arguments").set_all(self.args))
139 .register(crate::source::system_environment());
140
141 #[cfg(any(feature = "toml", feature = "yaml"))]
142 if !self.disable_file {
143 let mut fc = FileConfig::new(&salak.reg, &salak.ior)?;
144 #[cfg(feature = "toml")]
145 {
146 fc.build("toml", crate::source_toml::Toml::new)?;
147 }
148 #[cfg(feature = "yaml")]
149 {
150 fc.build("yaml", crate::source_yaml::YamlValue::new)?;
151 }
152 fc.register_to_env(&mut salak.reg);
153 }
154
155 #[cfg(feature = "app")]
156 salak.res.initialize(&salak)?;
157 Ok(salak)
158 }
159
160 #[inline]
161 #[cfg(feature = "app")]
162 #[cfg_attr(docsrs, doc(cfg(feature = "app")))]
163 pub fn register_default_resource<R: Resource + Send + Sync + Any>(self) -> Res<Self> {
165 self.register_resource::<R>(ResourceBuilder::default())
166 }
167
168 #[inline]
169 #[cfg(feature = "app")]
170 #[cfg_attr(docsrs, doc(cfg(feature = "app")))]
171 pub fn register_resource<R: Resource + Send + Sync + Any>(
173 self,
174 builder: ResourceBuilder<R>,
175 ) -> Res<Self> {
176 let mut env = self.configure_resource_description_by_builder(&builder);
177 env.resource.register(builder)?;
178 Ok(env)
179 }
180
181 #[inline]
182 #[cfg(feature = "app")]
183 #[cfg_attr(docsrs, doc(cfg(feature = "app")))]
185 pub(crate) fn configure_resource_description_by_builder<R: Resource>(
186 self,
187 builder: &ResourceBuilder<R>,
188 ) -> Self {
189 self.configure_description_by_namespace::<R::Config>(builder.namespace)
190 }
191
192 #[cfg(feature = "derive")]
193 #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
194 pub fn configure_description<T: PrefixedFromEnvironment + DescFromEnvironment>(self) -> Self {
196 self.configure_description_by_namespace::<T>("")
197 }
198
199 #[cfg(feature = "derive")]
200 #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
201 pub fn configure_description_by_namespace<T: PrefixedFromEnvironment + DescFromEnvironment>(
203 mut self,
204 namespace: &'static str,
205 ) -> Self {
206 self.app_desc
207 .push(Box::new(move |env| env.get_desc::<T>(namespace)));
208 self
209 }
210}
211
212#[allow(missing_debug_implementations)]
217pub struct Salak {
218 reg: PropertyRegistryInternal<'static>,
219 ior: Mutex<Vec<Box<dyn IORefT + Send>>>,
220 #[cfg(feature = "app")]
221 pub(crate) res: ResourceRegistry,
222}
223
224impl Salak {
225 pub fn builder() -> SalakBuilder {
227 SalakBuilder {
228 args: HashMap::new(),
229 #[cfg(any(feature = "toml", feature = "yaml"))]
230 disable_file: false,
231 #[cfg(feature = "rand")]
232 disable_random: false,
233 registry: PropertyRegistryInternal::new("registry"),
234 #[cfg(any(feature = "args", feature = "derive"))]
235 app_desc: vec![],
236 #[cfg(feature = "args")]
237 app_info: None,
238 iorefs: Mutex::new(vec![]),
239 #[cfg(feature = "app")]
240 resource: ResourceRegistry::new(),
241 }
242 }
243
244 pub fn new() -> Res<Self> {
246 Self::builder().build()
247 }
248
249 pub fn register<P: PropertySource + 'static>(&mut self, provider: P) {
252 self.reg.register_by_ref(Box::new(provider))
253 }
254
255 #[cfg(feature = "derive")]
256 #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
258 pub(crate) fn get_desc<T: PrefixedFromEnvironment + DescFromEnvironment>(
259 &self,
260 namespace: &'static str,
261 ) -> Vec<KeyDesc> {
262 let mut key = Key::new();
263 let mut key_descs = vec![];
264 let mut context = SalakDescContext::new(&mut key, &mut key_descs);
265 if namespace.is_empty() {
266 context.add_key_desc::<T>(T::prefix(), None, None, None);
267 } else {
268 context.add_key_desc::<T>(&format!("{}.{}", T::prefix(), namespace), None, None, None);
269 };
270 key_descs
271 }
272}
273
274impl Environment for Salak {
275 #[inline]
276 fn reload(&self) -> Res<bool> {
277 self.reg.reload(&self.ior)
278 }
279
280 #[inline]
281 fn require<T: FromEnvironment>(&self, key: &str) -> Res<T> {
282 self.reg.require(key, &self.ior)
283 }
284}