1use crate::*;
2use cfg_if::cfg_if;
3use di::{
4 exactly_one, scoped, singleton, singleton_as_self, transient, transient_factory, zero_or_more, ServiceCollection,
5 ServiceDescriptor, ServiceProvider,
6};
7
8macro_rules! opts_ext {
9 (($($bounds:tt)+)) => {
10 pub trait OptionsServiceExtensions {
12 fn add_options<T: Value + Default + 'static>(&mut self) -> OptionsBuilder<'_, T>;
14
15 fn add_named_options<T: Value + Default + 'static>(
21 &mut self,
22 name: impl AsRef<str>,
23 ) -> OptionsBuilder<'_, T>;
24
25 fn add_options_with<T, F>(&mut self, factory: F) -> OptionsBuilder<'_, T>
31 where
32 T: Value,
33 F: Fn(&ServiceProvider) -> Ref<dyn OptionsFactory<T>> + $($bounds)+;
34
35 fn add_named_options_with<T, F>(
42 &mut self,
43 name: impl AsRef<str>,
44 factory: F,
45 ) -> OptionsBuilder<'_, T>
46 where
47 T: Value,
48 F: Fn(&ServiceProvider) -> Ref<dyn OptionsFactory<T>> + $($bounds)+;
49
50 fn configure_options<T, F>(&mut self, setup: F) -> &mut Self
56 where
57 T: Value + Default + 'static,
58 F: Fn(&mut T) + $($bounds)+;
59
60 fn configure_named_options<T, F>(
67 &mut self,
68 name: impl AsRef<str>,
69 setup: F,
70 ) -> &mut Self
71 where
72 T: Value + Default + 'static,
73 F: Fn(&mut T) + $($bounds)+;
74
75 fn post_configure_options<T, F>(&mut self, setup: F) -> &mut Self
81 where
82 T: Value + Default + 'static,
83 F: Fn(&mut T) + $($bounds)+;
84
85 fn post_configure_named_options<T, F>(
92 &mut self,
93 name: impl AsRef<str>,
94 setup: F,
95 ) -> &mut Self
96 where
97 T: Value + Default + 'static,
98 F: Fn(&mut T) + $($bounds)+;
99 }
100 };
101}
102
103fn _add_options<'a, T: Value>(
104 services: &'a mut ServiceCollection,
105 name: Option<&str>,
106 descriptor: ServiceDescriptor,
107) -> OptionsBuilder<'a, T> {
108 services
109 .try_add(
110 singleton_as_self::<OptionsManager<T>>()
111 .depends_on(exactly_one::<dyn OptionsFactory<T>>())
112 .from(|sp| Ref::new(OptionsManager::new(sp.get_required::<dyn OptionsFactory<T>>()))),
113 )
114 .try_add(
115 singleton::<dyn Options<T>, OptionsManager<T>>()
116 .depends_on(exactly_one::<OptionsManager<T>>())
117 .from(|sp| sp.get_required::<OptionsManager<T>>()),
118 )
119 .try_add(
120 scoped::<dyn OptionsSnapshot<T>, OptionsManager<T>>()
121 .depends_on(exactly_one::<OptionsManager<T>>())
122 .from(|sp| sp.get_required::<OptionsManager<T>>()),
123 )
124 .try_add(
125 singleton::<dyn OptionsMonitor<T>, DefaultOptionsMonitor<T>>()
126 .depends_on(exactly_one::<dyn OptionsMonitorCache<T>>())
127 .depends_on(zero_or_more::<dyn OptionsChangeTokenSource<T>>())
128 .depends_on(exactly_one::<dyn OptionsFactory<T>>())
129 .from(|sp| {
130 Ref::new(DefaultOptionsMonitor::new(
131 sp.get_required::<dyn OptionsMonitorCache<T>>(),
132 sp.get_all::<dyn OptionsChangeTokenSource<T>>().collect(),
133 sp.get_required::<dyn OptionsFactory<T>>(),
134 ))
135 }),
136 )
137 .try_add(descriptor)
138 .try_add(
139 singleton::<dyn OptionsMonitorCache<T>, OptionsCache<T>>().from(|_| Ref::new(OptionsCache::default())),
140 );
141
142 OptionsBuilder::new(services, name)
143}
144
145macro_rules! opts_ext_impl {
146 (($($bounds:tt)+)) => {
147 impl OptionsServiceExtensions for ServiceCollection {
148 fn add_options<T: Value + Default + 'static>(&mut self) -> OptionsBuilder<'_, T> {
149 let descriptor = transient::<dyn OptionsFactory<T>, DefaultOptionsFactory<T>>()
150 .depends_on(zero_or_more::<dyn ConfigureOptions<T>>())
151 .depends_on(zero_or_more::<dyn PostConfigureOptions<T>>())
152 .depends_on(zero_or_more::<dyn ValidateOptions<T>>())
153 .from(|sp| {
154 Ref::new(DefaultOptionsFactory::new(
155 sp.get_all::<dyn ConfigureOptions<T>>().collect(),
156 sp.get_all::<dyn PostConfigureOptions<T>>().collect(),
157 sp.get_all::<dyn ValidateOptions<T>>().collect(),
158 ))
159 });
160
161 _add_options(self, None, descriptor)
162 }
163
164 fn add_named_options<T: Value + Default + 'static>(
165 &mut self,
166 name: impl AsRef<str>,
167 ) -> OptionsBuilder<'_, T> {
168 let descriptor = transient::<dyn OptionsFactory<T>, DefaultOptionsFactory<T>>()
169 .depends_on(zero_or_more::<dyn ConfigureOptions<T>>())
170 .depends_on(zero_or_more::<dyn PostConfigureOptions<T>>())
171 .depends_on(zero_or_more::<dyn ValidateOptions<T>>())
172 .from(|sp| {
173 Ref::new(DefaultOptionsFactory::new(
174 sp.get_all::<dyn ConfigureOptions<T>>().collect(),
175 sp.get_all::<dyn PostConfigureOptions<T>>().collect(),
176 sp.get_all::<dyn ValidateOptions<T>>().collect(),
177 ))
178 });
179
180 _add_options(self, Some(name.as_ref()), descriptor)
181 }
182
183 #[inline]
184 fn add_options_with<T, F>(&mut self, factory: F) -> OptionsBuilder<'_, T>
185 where
186 T: Value,
187 F: Fn(&ServiceProvider) -> Ref<dyn OptionsFactory<T>> + $($bounds)+,
188 {
189 _add_options(self, None, transient_factory(factory))
190 }
191
192 #[inline]
193 fn add_named_options_with<T, F>(
194 &mut self,
195 name: impl AsRef<str>,
196 factory: F,
197 ) -> OptionsBuilder<'_, T>
198 where
199 T: Value,
200 F: Fn(&ServiceProvider) -> Ref<dyn OptionsFactory<T>> + $($bounds)+,
201 {
202 _add_options(self, Some(name.as_ref()), transient_factory(factory))
203 }
204
205 #[inline]
206 fn configure_options<T, F>(&mut self, setup: F) -> &mut Self
207 where
208 T: Value + Default + 'static,
209 F: Fn(&mut T) + $($bounds)+,
210 {
211 self.add_options().configure(setup).into()
212 }
213
214 #[inline]
215 fn configure_named_options<T, F>(
216 &mut self,
217 name: impl AsRef<str>,
218 setup: F,
219 ) -> &mut Self
220 where
221 T: Value + Default + 'static,
222 F: Fn(&mut T) + $($bounds)+,
223 {
224 self.add_named_options(name).configure(setup).into()
225 }
226
227 #[inline]
228 fn post_configure_options<T, F>(&mut self, setup: F) -> &mut Self
229 where
230 T: Value + Default + 'static,
231 F: Fn(&mut T) + $($bounds)+,
232 {
233 self.add_options().post_configure(setup).into()
234 }
235
236 #[inline]
237 fn post_configure_named_options<T, F>(
238 &mut self,
239 name: impl AsRef<str>,
240 setup: F,
241 ) -> &mut Self
242 where
243 T: Value + Default + 'static,
244 F: Fn(&mut T) + $($bounds)+,
245 {
246 self.add_named_options(name).configure(setup).into()
247 }
248 }
249 };
250}
251
252cfg_if! {
253 if #[cfg(feature = "async")] {
254 opts_ext!((Send + Sync + 'static));
255 opts_ext_impl!((Send + Sync + 'static));
256 } else {
257 opts_ext!(('static));
258 opts_ext_impl!(('static));
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265 use di::{existing_as_self, transient};
266 use std::sync::RwLock;
267
268 #[derive(Default, Debug, PartialEq, Eq)]
269 struct TestOptions {
270 enabled: bool,
271 setting: usize,
272 }
273
274 #[derive(Default)]
275 struct TestValidation;
276
277 impl ValidateOptions<TestOptions> for TestValidation {
278 fn validate(&self, _name: Option<&str>, options: &TestOptions) -> ValidateOptionsResult {
279 if !options.enabled && options.setting > 0 {
280 ValidateOptionsResult::fail("Setting must be zero when disabled")
281 } else {
282 ValidateOptionsResult::success()
283 }
284 }
285 }
286
287 struct TestService {
288 value: RwLock<usize>,
289 }
290
291 impl TestService {
292 fn next(&self) -> usize {
293 let mut value = self.value.write().unwrap();
294 let current = *value;
295 *value += 1;
296 current
297 }
298
299 fn calls(&self) -> usize {
300 *self.value.read().unwrap() - 1
301 }
302 }
303
304 impl Default for TestService {
305 fn default() -> Self {
306 Self { value: RwLock::new(1) }
307 }
308 }
309
310 #[test]
311 fn get_should_resolve_service() {
312 let provider = ServiceCollection::new()
314 .add_options::<TestOptions>()
315 .build_provider()
316 .unwrap();
317
318 let result = provider.get::<dyn Options<TestOptions>>();
320
321 assert!(result.is_some());
323 }
324
325 #[test]
326 fn get_required_should_configure_options() {
327 let provider = ServiceCollection::new()
329 .configure_options(|o: &mut TestOptions| o.setting = 1)
330 .build_provider()
331 .unwrap();
332
333 let options = provider.get_required::<dyn Options<TestOptions>>();
335
336 assert_eq!(options.value().setting, 1);
338 }
339
340 #[test]
341 fn get_required_should_post_configure_options() {
342 let provider = ServiceCollection::new()
344 .post_configure_options(|o: &mut TestOptions| o.setting = 1)
345 .build_provider()
346 .unwrap();
347
348 let options = provider.get_required::<dyn Options<TestOptions>>();
350
351 assert_eq!(options.value().setting, 1);
353 }
354
355 #[test]
356 fn get_required_should_apply_all_configurations() {
357 let provider = ServiceCollection::new()
359 .configure_options(|o: &mut TestOptions| o.setting = 1)
360 .configure_options(|o: &mut TestOptions| o.enabled = true)
361 .post_configure_options(|o: &mut TestOptions| o.setting = 2)
362 .build_provider()
363 .unwrap();
364
365 let result = provider.get_required::<dyn Options<TestOptions>>();
367 let options = result.value();
368
369 assert!(options.enabled);
371 assert_eq!(options.setting, 2);
372 }
373
374 #[test]
375 fn get_required_should_not_panic_when_configured_options_are_valid() {
376 let provider = ServiceCollection::new()
378 .configure_options(|o: &mut TestOptions| {
379 o.enabled = true;
380 o.setting = 1;
381 })
382 .add(
383 transient::<dyn ValidateOptions<TestOptions>, TestValidation>()
384 .from(|_| Ref::new(TestValidation::default())),
385 )
386 .build_provider()
387 .unwrap();
388
389 let options = provider.get_required::<dyn Options<TestOptions>>();
391
392 let _ = options.value();
394 }
395
396 #[test]
397 #[should_panic(expected = "Setting must be zero when disabled")]
398 fn get_required_should_panic_when_configured_options_are_invalid() {
399 let provider = ServiceCollection::new()
401 .configure_options(|o: &mut TestOptions| {
402 o.enabled = false;
403 o.setting = 1;
404 })
405 .add(
406 transient::<dyn ValidateOptions<TestOptions>, TestValidation>()
407 .from(|_| Ref::new(TestValidation::default())),
408 )
409 .build_provider()
410 .unwrap();
411
412 let options = provider.get_required::<dyn Options<TestOptions>>();
414
415 let _ = options.value();
417 }
418
419 #[test]
420 fn get_required_should_configure_options_with_1_dependency() {
421 let provider = ServiceCollection::new()
423 .add_options::<TestOptions>()
424 .configure1(|o, d1: Ref<TestService>| o.setting = d1.next())
425 .add(existing_as_self(TestService::default()))
426 .build_provider()
427 .unwrap();
428
429 let options = provider.get_required::<dyn Options<TestOptions>>();
431
432 assert_eq!(options.value().setting, 1);
434 }
435
436 #[test]
437 fn get_required_should_configure_options_with_2_dependencies() {
438 let provider = ServiceCollection::new()
440 .add_options::<TestOptions>()
441 .configure2(|o, d1: Ref<TestService>, d2: Ref<TestService>| o.setting = d1.next() + d2.next())
442 .add(existing_as_self(TestService::default()))
443 .build_provider()
444 .unwrap();
445
446 let options = provider.get_required::<dyn Options<TestOptions>>();
448
449 assert_eq!(options.value().setting, 3);
451 }
452
453 #[test]
454 fn get_required_should_configure_options_with_3_dependencies() {
455 let provider = ServiceCollection::new()
457 .add_options::<TestOptions>()
458 .configure3(|o, d1: Ref<TestService>, d2: Ref<TestService>, d3: Ref<TestService>| {
459 o.setting = d1.next() + d2.next() + d3.next()
460 })
461 .add(existing_as_self(TestService::default()))
462 .build_provider()
463 .unwrap();
464
465 let options = provider.get_required::<dyn Options<TestOptions>>();
467
468 assert_eq!(options.value().setting, 6);
470 }
471
472 #[test]
473 fn get_required_should_configure_options_with_4_dependencies() {
474 let provider = ServiceCollection::new()
476 .add_options::<TestOptions>()
477 .configure4(
478 |o, d1: Ref<TestService>, d2: Ref<TestService>, d3: Ref<TestService>, d4: Ref<TestService>| {
479 o.setting = d1.next() + d2.next() + d3.next() + d4.next()
480 },
481 )
482 .add(existing_as_self(TestService::default()))
483 .build_provider()
484 .unwrap();
485
486 let options = provider.get_required::<dyn Options<TestOptions>>();
488
489 assert_eq!(options.value().setting, 10);
491 }
492
493 #[test]
494 fn get_required_should_configure_options_with_5_dependencies() {
495 let provider = ServiceCollection::new()
497 .add_options::<TestOptions>()
498 .configure5(
499 |o,
500 d1: Ref<TestService>,
501 d2: Ref<TestService>,
502 d3: Ref<TestService>,
503 d4: Ref<TestService>,
504 d5: Ref<TestService>| {
505 o.setting = d1.next() + d2.next() + d3.next() + d4.next() + d5.next()
506 },
507 )
508 .add(existing_as_self(TestService::default()))
509 .build_provider()
510 .unwrap();
511
512 let options = provider.get_required::<dyn Options<TestOptions>>();
514
515 assert_eq!(options.value().setting, 15);
517 }
518
519 #[test]
520 fn get_required_should_post_configure_options_with_1_dependency() {
521 let provider = ServiceCollection::new()
523 .add_options::<TestOptions>()
524 .post_configure1(|o, d1: Ref<TestService>| o.setting = d1.next())
525 .add(existing_as_self(TestService::default()))
526 .build_provider()
527 .unwrap();
528
529 let options = provider.get_required::<dyn Options<TestOptions>>();
531
532 assert_eq!(options.value().setting, 1);
534 }
535
536 #[test]
537 fn get_required_should_post_configure_options_with_2_dependencies() {
538 let provider = ServiceCollection::new()
540 .add_options::<TestOptions>()
541 .post_configure2(|o, d1: Ref<TestService>, d2: Ref<TestService>| o.setting = d1.next() + d2.next())
542 .add(existing_as_self(TestService::default()))
543 .build_provider()
544 .unwrap();
545
546 let options = provider.get_required::<dyn Options<TestOptions>>();
548
549 assert_eq!(options.value().setting, 3);
551 }
552
553 #[test]
554 fn get_required_should_post_configure_options_with_3_dependencies() {
555 let provider = ServiceCollection::new()
557 .add_options::<TestOptions>()
558 .post_configure3(|o, d1: Ref<TestService>, d2: Ref<TestService>, d3: Ref<TestService>| {
559 o.setting = d1.next() + d2.next() + d3.next()
560 })
561 .add(existing_as_self(TestService::default()))
562 .build_provider()
563 .unwrap();
564
565 let options = provider.get_required::<dyn Options<TestOptions>>();
567
568 assert_eq!(options.value().setting, 6);
570 }
571
572 #[test]
573 fn get_required_should_post_configure_options_with_4_dependencies() {
574 let provider = ServiceCollection::new()
576 .add_options::<TestOptions>()
577 .post_configure4(
578 |o, d1: Ref<TestService>, d2: Ref<TestService>, d3: Ref<TestService>, d4: Ref<TestService>| {
579 o.setting = d1.next() + d2.next() + d3.next() + d4.next()
580 },
581 )
582 .add(existing_as_self(TestService::default()))
583 .build_provider()
584 .unwrap();
585
586 let options = provider.get_required::<dyn Options<TestOptions>>();
588
589 assert_eq!(options.value().setting, 10);
591 }
592
593 #[test]
594 fn get_required_should_post_configure_options_with_5_dependencies() {
595 let provider = ServiceCollection::new()
597 .add_options::<TestOptions>()
598 .post_configure5(
599 |o,
600 d1: Ref<TestService>,
601 d2: Ref<TestService>,
602 d3: Ref<TestService>,
603 d4: Ref<TestService>,
604 d5: Ref<TestService>| {
605 o.setting = d1.next() + d2.next() + d3.next() + d4.next() + d5.next()
606 },
607 )
608 .add(existing_as_self(TestService::default()))
609 .build_provider()
610 .unwrap();
611
612 let options = provider.get_required::<dyn Options<TestOptions>>();
614
615 assert_eq!(options.value().setting, 15);
617 }
618
619 #[test]
620 fn get_required_should_validate_options_with_1_dependency() {
621 let provider = ServiceCollection::new()
623 .add_options::<TestOptions>()
624 .configure(|o| o.enabled = true)
625 .validate1(
626 |o, d1: Ref<TestService>| {
627 let _ = d1.next();
628 o.enabled
629 },
630 "Not enabled!",
631 )
632 .add(existing_as_self(TestService::default()))
633 .build_provider()
634 .unwrap();
635
636 let options = provider.get_required::<dyn Options<TestOptions>>();
638 let service = provider.get_required::<TestService>();
639
640 assert_eq!(options.value().enabled, true);
642 assert_eq!(service.calls(), 1);
643 }
644
645 #[test]
646 fn get_required_should_validate_options_with_2_dependencies() {
647 let provider = ServiceCollection::new()
649 .add_options::<TestOptions>()
650 .configure(|o| o.enabled = true)
651 .validate2(
652 |o, d1: Ref<TestService>, d2: Ref<TestService>| {
653 let _ = d1.next() + d2.next();
654 o.enabled
655 },
656 "Not enabled!",
657 )
658 .add(existing_as_self(TestService::default()))
659 .build_provider()
660 .unwrap();
661
662 let options = provider.get_required::<dyn Options<TestOptions>>();
664 let service = provider.get_required::<TestService>();
665
666 assert_eq!(options.value().enabled, true);
668 assert_eq!(service.calls(), 2);
669 }
670
671 #[test]
672 fn get_required_should_validate_options_with_3_dependencies() {
673 let provider = ServiceCollection::new()
675 .add_options::<TestOptions>()
676 .configure(|o| o.enabled = true)
677 .validate3(
678 |o, d1: Ref<TestService>, d2: Ref<TestService>, d3: Ref<TestService>| {
679 let _ = d1.next() + d2.next() + d3.next();
680 o.enabled
681 },
682 "Not enabled!",
683 )
684 .add(existing_as_self(TestService::default()))
685 .build_provider()
686 .unwrap();
687
688 let options = provider.get_required::<dyn Options<TestOptions>>();
690 let service = provider.get_required::<TestService>();
691
692 assert_eq!(options.value().enabled, true);
694 assert_eq!(service.calls(), 3);
695 }
696
697 #[test]
698 fn get_required_should_validate_options_with_4_dependencies() {
699 let provider = ServiceCollection::new()
701 .add_options::<TestOptions>()
702 .configure(|o| o.enabled = true)
703 .validate4(
704 |o, d1: Ref<TestService>, d2: Ref<TestService>, d3: Ref<TestService>, d4: Ref<TestService>| {
705 let _ = d1.next() + d2.next() + d3.next() + d4.next();
706 o.enabled
707 },
708 "Not enabled!",
709 )
710 .add(existing_as_self(TestService::default()))
711 .build_provider()
712 .unwrap();
713
714 let options = provider.get_required::<dyn Options<TestOptions>>();
716 let service = provider.get_required::<TestService>();
717
718 assert_eq!(options.value().enabled, true);
720 assert_eq!(service.calls(), 4);
721 }
722
723 #[test]
724 fn get_required_should_validate_options_with_5_dependencies() {
725 let provider = ServiceCollection::new()
727 .add_options::<TestOptions>()
728 .configure(|o| o.enabled = true)
729 .validate5(
730 |o,
731 d1: Ref<TestService>,
732 d2: Ref<TestService>,
733 d3: Ref<TestService>,
734 d4: Ref<TestService>,
735 d5: Ref<TestService>| {
736 let _ = d1.next() + d2.next() + d3.next() + d4.next() + d5.next();
737 o.enabled
738 },
739 "Not enabled!",
740 )
741 .add(existing_as_self(TestService::default()))
742 .build_provider()
743 .unwrap();
744
745 let options = provider.get_required::<dyn Options<TestOptions>>();
747 let service = provider.get_required::<TestService>();
748
749 assert_eq!(options.value().enabled, true);
751 assert_eq!(service.calls(), 5);
752 }
753}