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