di/
collection.rs

1use crate::{
2    validate, ServiceCardinality, ServiceDescriptor, ServiceLifetime, ServiceProvider, Type,
3    ValidationError,
4};
5use std::any::Any;
6use std::collections::HashMap;
7use std::fmt::{Formatter, Result as FormatResult, Write};
8use std::iter::{DoubleEndedIterator, ExactSizeIterator};
9use std::ops::Index;
10use std::slice::{Iter, IterMut};
11use std::vec::IntoIter;
12
13#[cfg(feature = "fmt")]
14use colored::Colorize;
15
16#[cfg(feature = "fmt")]
17use std::fmt::Display;
18
19/// Represents a service collection.
20#[derive(Default)]
21pub struct ServiceCollection {
22    items: Vec<ServiceDescriptor>,
23}
24
25impl ServiceCollection {
26    /// Creates and returns a new instance of the service collection.
27    pub fn new() -> Self {
28        Self::default()
29    }
30
31    /// Returns true if the collection contains no elements.
32    pub fn is_empty(&self) -> bool {
33        self.items.is_empty()
34    }
35
36    /// Returns the number of elements in the collection.
37    pub fn len(&self) -> usize {
38        self.items.len()
39    }
40
41    /// Removes all elements from the collection.
42    pub fn clear(&mut self) {
43        self.items.clear()
44    }
45
46    /// Removes and returns the element at position index within the collection.
47    ///
48    /// # Argument
49    ///
50    /// * `index` - The index of the element to remove
51    ///
52    /// # Panics
53    ///
54    /// Panics if `index` is out of bounds.
55    pub fn remove(&mut self, index: usize) -> ServiceDescriptor {
56        self.items.remove(index)
57    }
58
59    /// Adds a service using the specified service descriptor.
60    ///
61    /// # Arguments
62    ///
63    /// * `descriptor` - The [`ServiceDescriptor`](crate::ServiceDescriptor) to register
64    pub fn add<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
65        self.items.push(descriptor.into());
66        self
67    }
68
69    /// Adds a service using the specified service descriptor if the service has not already been registered.
70    ///
71    /// # Arguments
72    ///
73    /// * `descriptor` - The [`ServiceDescriptor`](crate::ServiceDescriptor) to register
74    pub fn try_add<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
75        let new_item = descriptor.into();
76        let service_type = new_item.service_type();
77
78        for item in &self.items {
79            if item.service_type() == service_type {
80                return self;
81            }
82        }
83
84        self.items.push(new_item);
85        self
86    }
87
88    /// Adds a service using the specified service descriptor if the service with same service and
89    /// implementation type has not already been registered.
90    ///
91    /// # Arguments
92    ///
93    /// * `descriptor` - The [`ServiceDescriptor`](crate::ServiceDescriptor) to register
94    pub fn try_add_to_all<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
95        let new_item = descriptor.into();
96        let service_type = new_item.service_type();
97        let implementation_type = new_item.implementation_type();
98
99        if service_type == implementation_type {
100            return self;
101        }
102
103        for item in &self.items {
104            if item.service_type() == service_type
105                && item.implementation_type() == implementation_type
106            {
107                return self;
108            }
109        }
110
111        self.items.push(new_item);
112        self
113    }
114
115    /// Adds the specified service descriptors if each of the services are not already registered
116    /// with the same service and implementation type.
117    ///
118    /// # Arguments
119    ///
120    /// * `descriptors` - The [`ServiceDescriptor`](crate::ServiceDescriptor) sequence to register
121    pub fn try_add_all(
122        &mut self,
123        descriptors: impl IntoIterator<Item = ServiceDescriptor>,
124    ) -> &mut Self {
125        for descriptor in descriptors {
126            self.try_add_to_all(descriptor);
127        }
128        self
129    }
130
131    /// Removes the first service descriptor with the same service type and adds the replacement.
132    ///
133    /// # Arguments
134    ///
135    /// * `descriptor` - The replacement [`ServiceDescriptor`](crate::ServiceDescriptor)
136    pub fn replace<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
137        let new_item = descriptor.into();
138        let service_type = new_item.service_type();
139
140        for i in 0..self.items.len() {
141            if self.items[i].service_type() == service_type {
142                self.items.remove(i);
143                break;
144            }
145        }
146
147        self.items.push(new_item);
148        self
149    }
150
151    /// Adds or replaces a service with the specified descriptor if the service has not already been registered.
152    ///
153    /// # Arguments
154    ///
155    /// * `descriptor` - The replacement [`ServiceDescriptor`](crate::ServiceDescriptor)
156    pub fn try_replace<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
157        self.try_add(descriptor)
158    }
159
160    /// Removes all specified descriptors of the specified type.
161    pub fn remove_all<T: Any + ?Sized>(&mut self) -> &mut Self {
162        let service_type = Type::of::<T>();
163
164        for i in (0..self.items.len()).rev() {
165            if self.items[i].service_type() == service_type {
166                self.items.remove(i);
167            }
168        }
169
170        self
171    }
172
173    /// Builds and returns a new [`ServiceProvider`](crate::ServiceProvider).
174    pub fn build_provider(&self) -> Result<ServiceProvider, ValidationError> {
175        if let Err(error) = validate(self) {
176            return Err(error);
177        }
178
179        let mut services = HashMap::with_capacity(self.items.len());
180
181        for item in &self.items {
182            let key = item.service_type().clone();
183            let descriptors = services.entry(key).or_insert_with(Vec::new);
184
185            // note: dependencies are only interesting for validation. after a ServiceProvider
186            // is created, no further validation occurs. prevent copying unnecessary memory
187            // and allow it to potentially be freed if the ServiceCollection is dropped.
188            descriptors.push(item.clone_with(false));
189        }
190
191        for values in services.values_mut() {
192            values.shrink_to_fit();
193        }
194
195        services.shrink_to_fit();
196        Ok(ServiceProvider::new(services))
197    }
198
199    /// Gets a read-only iterator for the collection
200    pub fn iter(
201        &self,
202    ) -> impl Iterator<Item = &ServiceDescriptor> + ExactSizeIterator + DoubleEndedIterator {
203        self.items.iter()
204    }
205}
206
207impl<'a> IntoIterator for &'a ServiceCollection {
208    type Item = &'a ServiceDescriptor;
209    type IntoIter = Iter<'a, ServiceDescriptor>;
210
211    fn into_iter(self) -> Self::IntoIter {
212        self.items.iter()
213    }
214}
215
216impl<'a> IntoIterator for &'a mut ServiceCollection {
217    type Item = &'a mut ServiceDescriptor;
218    type IntoIter = IterMut<'a, ServiceDescriptor>;
219
220    fn into_iter(self) -> Self::IntoIter {
221        self.items.iter_mut()
222    }
223}
224
225impl IntoIterator for ServiceCollection {
226    type Item = ServiceDescriptor;
227    type IntoIter = IntoIter<Self::Item>;
228
229    fn into_iter(self) -> Self::IntoIter {
230        self.items.into_iter()
231    }
232}
233
234impl Index<usize> for ServiceCollection {
235    type Output = ServiceDescriptor;
236
237    fn index(&self, index: usize) -> &Self::Output {
238        &self.items[index]
239    }
240}
241
242impl std::fmt::Debug for ServiceCollection {
243    fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
244        print(self, TextRenderer::default(), f)
245    }
246}
247
248#[cfg(feature = "fmt")]
249impl Display for ServiceCollection {
250    fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
251        print(self, TerminalRenderer::default(), f)
252    }
253}
254
255trait Renderer {
256    fn write(&mut self, ch: char, f: &mut Formatter<'_>) -> FormatResult {
257        f.write_char(ch)
258    }
259
260    fn write_str<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
261        f.write_str(text.as_ref())
262    }
263
264    fn service<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
265        f.write_str(text.as_ref())
266    }
267
268    fn implementation<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
269        f.write_str(text.as_ref())
270    }
271
272    fn keyword<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
273        f.write_str(text.as_ref())
274    }
275
276    fn info<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
277        f.write_str(text.as_ref())
278    }
279
280    fn warn<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
281        f.write_str(text.as_ref())
282    }
283
284    fn error<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
285        f.write_str(text.as_ref())
286    }
287
288    fn accent<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
289        f.write_str(text.as_ref())
290    }
291}
292
293#[derive(Default)]
294struct TextRenderer;
295
296impl Renderer for TextRenderer {}
297
298#[cfg(feature = "fmt")]
299#[derive(Default)]
300struct TerminalRenderer;
301
302#[cfg(feature = "fmt")]
303impl Renderer for TerminalRenderer {
304    fn write(&mut self, ch: char, f: &mut Formatter<'_>) -> FormatResult {
305        f.write_char(ch)
306    }
307
308    fn write_str<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
309        f.write_str(text.as_ref())
310    }
311
312    fn keyword<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
313        text.as_ref().truecolor(75, 154, 214).fmt(f)
314    }
315
316    fn service<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
317        text.as_ref().truecolor(158, 211, 163).fmt(f)
318    }
319
320    fn implementation<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
321        text.as_ref().truecolor(78, 201, 176).fmt(f)
322    }
323
324    fn info<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
325        text.as_ref().truecolor(118, 118, 118).fmt(f)
326    }
327
328    fn warn<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
329        text.as_ref().truecolor(220, 220, 170).fmt(f)
330    }
331
332    fn error<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
333        text.as_ref().truecolor(231, 72, 86).fmt(f)
334    }
335
336    fn accent<T: AsRef<str>>(&mut self, text: T, f: &mut Formatter<'_>) -> FormatResult {
337        text.as_ref().truecolor(218, 112, 179).fmt(f)
338    }
339}
340
341enum PrintItem<'a> {
342    One(&'a ServiceDescriptor),
343    Many((&'a Type, &'a str, &'a Vec<&'a ServiceDescriptor>)),
344    Warning((&'a Type, &'a str)),
345    Error((&'a Type, &'a str)),
346}
347
348struct PrintContext<'a> {
349    scope: ServiceLifetime,
350    visited: Vec<&'a ServiceDescriptor>,
351    lookup: &'a HashMap<&'a Type, Vec<&'a ServiceDescriptor>>,
352}
353
354impl<'a> PrintContext<'a> {
355    fn new(lookup: &'a HashMap<&'a Type, Vec<&'a ServiceDescriptor>>) -> Self {
356        Self {
357            scope: ServiceLifetime::Transient,
358            visited: Vec::new(),
359            lookup,
360        }
361    }
362
363    fn reset(&mut self, descriptor: &'a ServiceDescriptor) {
364        self.scope = descriptor.lifetime();
365        self.visited.clear();
366        self.visited.push(descriptor);
367    }
368
369    fn lookup(&self, key: &Type) -> Option<&'a Vec<&'a ServiceDescriptor>> {
370        self.lookup.get(key)
371    }
372
373    fn enter(&mut self, descriptor: &'a ServiceDescriptor) {
374        if self.scope != ServiceLifetime::Singleton
375            && descriptor.lifetime() == ServiceLifetime::Singleton
376        {
377            self.scope = ServiceLifetime::Singleton;
378        }
379
380        self.visited.push(descriptor);
381    }
382
383    fn exit(&mut self) {
384        self.visited.pop();
385
386        for item in self.visited.iter().rev() {
387            self.scope = item.lifetime();
388
389            if self.scope == ServiceLifetime::Singleton {
390                return;
391            }
392        }
393
394        self.scope = self
395            .visited
396            .last()
397            .map_or(ServiceLifetime::Transient, |s| s.lifetime());
398    }
399
400    fn is_circular_ref(&self, descriptor: &ServiceDescriptor) -> bool {
401        for item in self.visited.iter().rev() {
402            if item.service_type() == descriptor.service_type() {
403                return true;
404            }
405        }
406
407        false
408    }
409
410    fn is_invalid_lifetime(&self, descriptor: &ServiceDescriptor) -> bool {
411        self.scope == ServiceLifetime::Singleton && descriptor.lifetime() == ServiceLifetime::Scoped
412    }
413}
414
415fn print<R: Renderer>(
416    services: &ServiceCollection,
417    mut renderer: R,
418    f: &mut Formatter<'_>,
419) -> FormatResult {
420    let count = services.items.len();
421
422    if count == 0 {
423        return Ok(());
424    }
425
426    let last = count - 1;
427    let mut branches = Vec::<char>::new();
428    let mut lookup = HashMap::with_capacity(count);
429
430    for item in &services.items {
431        let key = item.service_type();
432        let descriptors = lookup.entry(key).or_insert_with(Vec::new);
433        descriptors.push(item);
434    }
435
436    let mut context = PrintContext::new(&lookup);
437
438    branches.push('│');
439    branches.push(' ');
440
441    for (index, descriptor) in services.items.iter().enumerate() {
442        if index == last {
443            renderer.write('└', f)?;
444            branches[0] = ' ';
445        } else if index == 0 {
446            renderer.write('┌', f)?;
447        } else {
448            renderer.write('├', f)?;
449        }
450
451        renderer.write(' ', f)?;
452        context.reset(descriptor);
453
454        print_item(
455            PrintItem::One(descriptor),
456            ServiceCardinality::ExactlyOne,
457            &mut context,
458            0,
459            &mut branches,
460            f,
461            &mut renderer,
462        )?;
463
464        if index != last {
465            renderer.write_str("│\n", f)?;
466        }
467    }
468
469    Ok(())
470}
471
472fn print_item<R: Renderer>(
473    item: PrintItem,
474    cardinality: ServiceCardinality,
475    context: &mut PrintContext,
476    depth: usize,
477    branches: &mut Vec<char>,
478    formatter: &mut Formatter,
479    renderer: &mut R,
480) -> FormatResult {
481    match item {
482        PrintItem::One(sd) => {
483            append_service(sd.service_type(), cardinality, renderer, formatter)?;
484
485            if context.is_invalid_lifetime(sd) {
486                renderer.error(
487                    format!(
488                        "⧗ {} [{:?}]",
489                        sd.implementation_type().name(),
490                        sd.lifetime()
491                    ),
492                    formatter,
493                )?;
494            } else {
495                append_implementation(sd, renderer, formatter)?;
496            }
497        }
498        PrintItem::Many((ty, impl_count, _)) => {
499            append_service(ty, cardinality, renderer, formatter)?;
500            renderer.write_str(impl_count, formatter)?;
501        }
502        PrintItem::Warning((sd, msg)) => {
503            append_service(sd, cardinality, renderer, formatter)?;
504            renderer.warn(msg, formatter)?;
505        }
506        PrintItem::Error((sd, msg)) => {
507            append_service(sd, cardinality, renderer, formatter)?;
508            renderer.error(msg, formatter)?;
509        }
510    }
511
512    renderer.write('\n', formatter)?;
513
514    match item {
515        PrintItem::One(child) => {
516            traverse_dependencies(child, context, depth, branches, formatter, renderer)
517        }
518        PrintItem::Many((_, _, children)) => {
519            traverse_services(children, context, depth, branches, formatter, renderer)
520        }
521        _ => Ok(()),
522    }
523}
524
525fn append_service<R: Renderer>(
526    ty: &Type,
527    cardinality: ServiceCardinality,
528    renderer: &mut R,
529    f: &mut Formatter,
530) -> FormatResult {
531    let (type_, key) = Type::deconstruct(ty);
532
533    if type_.starts_with("dyn") {
534        renderer.keyword("dyn", f)?;
535        renderer.write(' ', f)?;
536        renderer.service(&type_[(type_.char_indices().nth(4).unwrap().0)..], f)?;
537    } else {
538        renderer.implementation(type_, f)?;
539    }
540
541    if cardinality == ServiceCardinality::ZeroOrMore {
542        renderer.accent("*", f)?;
543    } else if cardinality == ServiceCardinality::ZeroOrOne {
544        renderer.accent("?", f)?;
545    }
546
547    if let Some(name) = key {
548        renderer.write(' ', f)?;
549        renderer.info("[⚿ ", f)?;
550        renderer.info(name, f)?;
551        renderer.info("]", f)?;
552    }
553
554    renderer.write_str(" → ", f)
555}
556
557fn append_implementation<R: Renderer>(
558    item: &ServiceDescriptor,
559    renderer: &mut R,
560    f: &mut Formatter,
561) -> FormatResult {
562    renderer.implementation(item.implementation_type().name(), f)?;
563    renderer.write(' ', f)?;
564
565    match item.lifetime() {
566        ServiceLifetime::Scoped => renderer.info("[Scoped]", f),
567        ServiceLifetime::Singleton => renderer.info("[Singleton]", f),
568        ServiceLifetime::Transient => renderer.info("[Transient]", f),
569    }
570}
571
572fn indent<R: Renderer>(
573    branches: &mut Vec<char>,
574    formatter: &mut Formatter,
575    renderer: &mut R,
576    last: bool,
577) -> FormatResult {
578    for i in 0..branches.len() {
579        renderer.write(branches[i], formatter)?;
580    }
581
582    if last {
583        renderer.write('└', formatter)?;
584    } else {
585        renderer.write('├', formatter)?;
586    }
587
588    renderer.write(' ', formatter)?;
589
590    if last {
591        branches.push(' ');
592    } else {
593        branches.push('│');
594    }
595
596    branches.push(' ');
597    Ok(())
598}
599
600fn unindent(branches: &mut Vec<char>) {
601    branches.pop();
602    branches.pop();
603}
604
605fn traverse_dependencies<R: Renderer>(
606    descriptor: &ServiceDescriptor,
607    context: &mut PrintContext,
608    depth: usize,
609    branches: &mut Vec<char>,
610    formatter: &mut Formatter,
611    renderer: &mut R,
612) -> FormatResult {
613    for (index, dependency) in descriptor.dependencies().iter().enumerate() {
614        let type_ = dependency.injected_type();
615        let cardinality = dependency.cardinality();
616        let last = index == descriptor.dependencies().len() - 1;
617
618        indent(branches, formatter, renderer, last)?;
619
620        if let Some(children) = context.lookup(type_) {
621            if cardinality == ServiceCardinality::ZeroOrMore {
622                print_item(
623                    PrintItem::Many((type_, &format!("Count: {}", children.len()), children)),
624                    cardinality,
625                    context,
626                    depth + 1,
627                    branches,
628                    formatter,
629                    renderer,
630                )?;
631            } else {
632                for child in children {
633                    let msg;
634                    let item = if context.is_circular_ref(child) {
635                        msg = format!("♺ {}", child.service_type().name());
636                        PrintItem::Error((child.service_type(), &msg))
637                    } else {
638                        PrintItem::One(child)
639                    };
640
641                    context.enter(child);
642                    print_item(
643                        item,
644                        cardinality,
645                        context,
646                        depth + 1,
647                        branches,
648                        formatter,
649                        renderer,
650                    )?;
651                    context.exit();
652                }
653            }
654        } else {
655            let item = match cardinality {
656                ServiceCardinality::ExactlyOne => PrintItem::Error((type_, "‼ Missing")),
657                ServiceCardinality::ZeroOrOne => PrintItem::Warning((type_, "▲ Missing")),
658                ServiceCardinality::ZeroOrMore => PrintItem::Warning((type_, "▲ Count: 0")),
659            };
660
661            print_item(
662                item,
663                cardinality,
664                context,
665                depth + 1,
666                branches,
667                formatter,
668                renderer,
669            )?;
670        }
671
672        unindent(branches);
673    }
674
675    Ok(())
676}
677
678fn traverse_services<R: Renderer>(
679    descriptors: &Vec<&ServiceDescriptor>,
680    context: &mut PrintContext,
681    depth: usize,
682    branches: &mut Vec<char>,
683    formatter: &mut Formatter,
684    renderer: &mut R,
685) -> FormatResult {
686    for (index, descriptor) in descriptors.iter().enumerate() {
687        let last = index == descriptors.len() - 1;
688
689        indent(branches, formatter, renderer, last)?;
690        print_item(
691            PrintItem::One(descriptor),
692            ServiceCardinality::ExactlyOne,
693            context,
694            depth + 1,
695            branches,
696            formatter,
697            renderer,
698        )?;
699        unindent(branches);
700    }
701
702    Ok(())
703}
704
705#[cfg(test)]
706mod tests {
707
708    use super::*;
709    use crate::{test::*, *};
710    use std::fs::remove_file;
711    use std::path::{Path, PathBuf};
712
713    #[test]
714    fn is_empty_should_return_true_when_empty() {
715        // arrange
716        let collection = ServiceCollection::default();
717
718        // act
719        let empty = collection.is_empty();
720
721        // assert
722        assert!(empty);
723    }
724
725    #[test]
726    fn length_should_return_zero_when_empty() {
727        // arrange
728        let collection = ServiceCollection::default();
729
730        // act
731        let length = collection.len();
732
733        // assert
734        assert_eq!(length, 0);
735    }
736
737    #[test]
738    fn is_empty_should_return_false_when_not_empty() {
739        // arrange
740        let descriptor =
741            existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default()));
742        let mut collection = ServiceCollection::new();
743
744        collection.add(descriptor);
745
746        // act
747        let not_empty = !collection.is_empty();
748
749        // assert
750        assert!(not_empty);
751    }
752
753    #[test]
754    fn length_should_return_count_when_not_empty() {
755        // arrange
756        let descriptor =
757            existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default()));
758        let mut collection = ServiceCollection::new();
759
760        collection.add(descriptor);
761
762        // act
763        let length = collection.len();
764
765        // assert
766        assert_eq!(length, 1);
767    }
768
769    #[test]
770    fn clear_should_remove_all_elements() {
771        // arrange
772        let descriptor =
773            existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default()));
774        let mut collection = ServiceCollection::new();
775
776        collection.add(descriptor);
777
778        // act
779        collection.clear();
780
781        // assert
782        assert!(collection.is_empty());
783    }
784
785    #[test]
786    fn try_add_should_do_nothing_when_service_is_registered() {
787        // arrange
788        let mut collection = ServiceCollection::new();
789
790        collection.add(
791            singleton::<dyn TestService, TestServiceImpl>()
792                .from(|_| Ref::new(TestServiceImpl::default())),
793        );
794
795        // act
796        collection.try_add(
797            singleton::<dyn TestService, TestServiceImpl>()
798                .from(|_| Ref::new(TestServiceImpl::default())),
799        );
800
801        // assert
802        assert_eq!(collection.len(), 1);
803    }
804
805    #[test]
806    fn try_add_to_all_should_add_descriptor_when_implementation_is_unregistered() {
807        // arrange
808        let mut collection = ServiceCollection::new();
809
810        collection.add(existing::<dyn TestService, TestServiceImpl>(Box::new(
811            TestServiceImpl::default(),
812        )));
813
814        collection.try_add_to_all(
815            singleton::<dyn OtherTestService, OtherTestServiceImpl>().from(|sp| {
816                Ref::new(OtherTestServiceImpl::new(
817                    sp.get_required::<dyn TestService>(),
818                ))
819            }),
820        );
821
822        // act
823        let count = collection.len();
824
825        // assert
826        assert_eq!(count, 2);
827    }
828
829    #[test]
830    fn try_add_to_all_should_not_add_descriptor_when_implementation_is_registered() {
831        // arrange
832        let mut collection = ServiceCollection::new();
833
834        collection.add(existing::<dyn TestService, TestServiceImpl>(Box::new(
835            TestServiceImpl::default(),
836        )));
837
838        collection.try_add_to_all(
839            transient::<dyn TestService, TestServiceImpl>()
840                .from(|_| Ref::new(TestServiceImpl::default())),
841        );
842
843        // act
844        let count = collection.len();
845
846        // assert
847        assert_eq!(count, 1);
848    }
849
850    #[test]
851    fn try_add_all_should_only_add_descriptors_for_unregistered_implementations() {
852        // arrange
853        let descriptors = vec![
854            existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default())),
855            transient::<dyn TestService, TestServiceImpl>()
856                .from(|_| Ref::new(TestServiceImpl::default())),
857        ];
858        let mut collection = ServiceCollection::new();
859
860        collection.try_add_all(descriptors.into_iter());
861
862        // act
863        let count = collection.len();
864
865        // assert
866        assert_eq!(count, 1);
867    }
868
869    #[test]
870    fn replace_should_replace_first_registered_service() {
871        // arrange
872        let mut collection = ServiceCollection::new();
873
874        collection
875            .add(
876                singleton::<dyn TestService, TestServiceImpl>()
877                    .from(|_| Ref::new(TestServiceImpl::default())),
878            )
879            .add(
880                singleton::<dyn TestService, TestServiceImpl>()
881                    .from(|_| Ref::new(TestServiceImpl::default())),
882            );
883
884        // act
885        collection.replace(
886            singleton::<dyn TestService, TestServiceImpl>()
887                .from(|_| Ref::new(TestServiceImpl::default())),
888        );
889
890        // assert
891        assert_eq!(collection.len(), 2);
892    }
893
894    #[test]
895    fn remove_all_should_remove_registered_services() {
896        // arrange
897        let mut collection = ServiceCollection::new();
898
899        collection
900            .add(
901                singleton::<dyn TestService, TestServiceImpl>()
902                    .from(|_| Ref::new(TestServiceImpl::default())),
903            )
904            .add(
905                singleton::<dyn TestService, TestServiceImpl>()
906                    .from(|_| Ref::new(TestServiceImpl::default())),
907            );
908
909        // act
910        collection.remove_all::<dyn TestService>();
911
912        // assert
913        assert!(collection.is_empty());
914    }
915
916    #[test]
917    fn try_replace_should_do_nothing_when_service_is_registered() {
918        // arrange
919        let mut collection = ServiceCollection::new();
920
921        collection.add(
922            singleton::<dyn TestService, TestServiceImpl>()
923                .from(|_| Ref::new(TestServiceImpl { value: 1 })),
924        );
925
926        // act
927        collection.try_replace(
928            singleton::<dyn TestService, TestServiceImpl>()
929                .from(|_| Ref::new(TestServiceImpl { value: 2 })),
930        );
931
932        // assert
933        let value = collection
934            .build_provider()
935            .unwrap()
936            .get_required::<dyn TestService>()
937            .value();
938        assert_eq!(value, 1);
939    }
940
941    #[test]
942    fn remove_should_remove_element_at_index() {
943        // arrange
944        let descriptor =
945            existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default()));
946        let mut collection = ServiceCollection::new();
947
948        collection.add(descriptor);
949
950        // act
951        let _ = collection.remove(0);
952
953        // assert
954        assert!(collection.is_empty());
955    }
956
957    #[test]
958    fn service_collection_should_drop_existing_as_service() {
959        // arrange
960        let file = new_temp_file("drop1");
961
962        // act
963        {
964            let mut services = ServiceCollection::new();
965            services.add(existing_as_self(Droppable::new(file.clone())));
966        }
967
968        // assert
969        let dropped = !file.exists();
970        remove_file(&file).ok();
971        assert!(dropped);
972    }
973
974    #[test]
975    fn service_collection_should_not_drop_service_if_never_instantiated() {
976        // arrange
977        let file = new_temp_file("drop4");
978        let mut services = ServiceCollection::new();
979
980        // act
981        {
982            services
983                .add(existing::<Path, PathBuf>(file.clone().into_boxed_path()))
984                .add(singleton_as_self().from(|sp| {
985                    Ref::new(Droppable::new(sp.get_required::<Path>().to_path_buf()))
986                }));
987        }
988
989        // assert
990        let not_dropped = file.exists();
991        remove_file(&file).ok();
992        assert!(not_dropped);
993    }
994}