use crate::{fmt, validate, Ref, ServiceDescriptor, ServiceDescriptorBuilder, ServiceProvider, Type, ValidationError};
use std::any::Any;
use std::collections::HashMap;
use std::fmt::{Formatter, Result as FormatResult};
use std::iter::{DoubleEndedIterator, ExactSizeIterator};
use std::ops::Index;
use std::slice::{Iter, IterMut};
use std::vec::IntoIter;
macro_rules! decorate {
(($($traits:tt)+), ($($bounds:tt)+)) => {
pub fn decorate<TSvc: ?Sized + $($traits)+, TImpl>(
&mut self,
activate: impl Fn(&ServiceProvider, Ref<TSvc>) -> Ref<TSvc> + $($bounds)+,
) -> &mut Self {
let service_type = Type::of::<TSvc>();
for item in self.items.iter_mut().rev() {
if item.service_type() != service_type {
continue;
}
let impl_type = Type::of::<TImpl>();
if item.implementation_type() == impl_type {
return self;
}
let original = item.clone();
let builder = ServiceDescriptorBuilder::<TSvc, TImpl>::new(original.lifetime(), impl_type);
*item = builder.from(move |sp| {
let decorated = original.get(sp).downcast_ref::<Ref<TSvc>>().unwrap().clone();
activate(sp, decorated)
});
break;
}
self
}
pub fn decorate_all<TSvc: ?Sized + $($traits)+, TImpl>(
&mut self,
activate: impl Fn(&ServiceProvider, Ref<TSvc>) -> Ref<TSvc> + $($bounds)+,
) -> &mut Self {
let service_type = Type::of::<TSvc>();
let func = Ref::new(activate);
for item in self.items.iter_mut() {
let impl_type = Type::of::<TImpl>();
if item.service_type() != service_type || item.implementation_type() == impl_type {
continue;
}
let original = item.clone();
let activate = func.clone();
let builder = ServiceDescriptorBuilder::<TSvc, TImpl>::new(original.lifetime(), impl_type);
*item = builder.from(move |sp| {
let decorated = original.get(sp).downcast_ref::<Ref<TSvc>>().unwrap().clone();
(activate)(sp, decorated)
});
}
self
}
};
}
#[derive(Default)]
pub struct ServiceCollection {
items: Vec<ServiceDescriptor>,
}
impl ServiceCollection {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.items.len()
}
#[inline]
pub fn clear(&mut self) {
self.items.clear()
}
#[inline]
pub fn remove(&mut self, index: usize) -> ServiceDescriptor {
self.items.remove(index)
}
pub fn add<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
self.items.push(descriptor.into());
self
}
pub fn try_add<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
let new_item = descriptor.into();
let service_type = new_item.service_type();
for item in &self.items {
if item.service_type() == service_type {
return self;
}
}
self.items.push(new_item);
self
}
pub fn try_add_to_all<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
let new_item = descriptor.into();
let service_type = new_item.service_type();
let implementation_type = new_item.implementation_type();
if service_type == implementation_type {
return self;
}
for item in &self.items {
if item.service_type() == service_type && item.implementation_type() == implementation_type {
return self;
}
}
self.items.push(new_item);
self
}
pub fn try_add_all(&mut self, descriptors: impl IntoIterator<Item = ServiceDescriptor>) -> &mut Self {
for descriptor in descriptors {
self.try_add_to_all(descriptor);
}
self
}
pub fn replace<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
let new_item = descriptor.into();
let service_type = new_item.service_type();
for i in 0..self.items.len() {
if self.items[i].service_type() == service_type {
self.items.remove(i);
break;
}
}
self.items.push(new_item);
self
}
#[inline]
pub fn try_replace<T: Into<ServiceDescriptor>>(&mut self, descriptor: T) -> &mut Self {
self.try_add(descriptor)
}
pub fn remove_all<T: Any + ?Sized>(&mut self) -> &mut Self {
let service_type = Type::of::<T>();
for i in (0..self.items.len()).rev() {
if self.items[i].service_type() == service_type {
self.items.remove(i);
}
}
self
}
pub fn build_provider(&self) -> Result<ServiceProvider, ValidationError> {
validate(self)?;
let mut services = HashMap::with_capacity(self.items.len());
for item in &self.items {
let key = item.service_type().clone();
let descriptors = services.entry(key).or_insert_with(Vec::new);
descriptors.push(item.clone_with(false));
}
for values in services.values_mut() {
values.shrink_to_fit();
}
services.shrink_to_fit();
Ok(ServiceProvider::new(services))
}
#[inline]
pub fn iter(&self) -> impl ExactSizeIterator<Item = &ServiceDescriptor> + DoubleEndedIterator {
self.items.iter()
}
cfg_if::cfg_if! {
if #[cfg(feature = "async")] {
decorate!((Any + Send + Sync), (Send + Sync + 'static));
} else {
decorate!((Any), ('static));
}
}
}
impl<'a> IntoIterator for &'a ServiceCollection {
type Item = &'a ServiceDescriptor;
type IntoIter = Iter<'a, ServiceDescriptor>;
fn into_iter(self) -> Self::IntoIter {
self.items.iter()
}
}
impl<'a> IntoIterator for &'a mut ServiceCollection {
type Item = &'a mut ServiceDescriptor;
type IntoIter = IterMut<'a, ServiceDescriptor>;
fn into_iter(self) -> Self::IntoIter {
self.items.iter_mut()
}
}
impl IntoIterator for ServiceCollection {
type Item = ServiceDescriptor;
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.items.into_iter()
}
}
impl Index<usize> for ServiceCollection {
type Output = ServiceDescriptor;
fn index(&self, index: usize) -> &Self::Output {
&self.items[index]
}
}
impl std::fmt::Debug for ServiceCollection {
fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
fmt::write(self, fmt::text::Renderer, f)
}
}
impl std::fmt::Display for ServiceCollection {
fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
cfg_if::cfg_if! {
if #[cfg(feature = "fmt")] {
if f.alternate() {
return fmt::write(self, fmt::terminal::Renderer, f);
}
}
}
fmt::write(self, fmt::text::Renderer, f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{existing, existing_as_self, singleton, singleton_as_self, test::*, transient};
use std::fs::remove_file;
use std::path::{Path, PathBuf};
#[test]
fn is_empty_should_return_true_when_empty() {
let collection = ServiceCollection::default();
let empty = collection.is_empty();
assert!(empty);
}
#[test]
fn length_should_return_zero_when_empty() {
let collection = ServiceCollection::default();
let length = collection.len();
assert_eq!(length, 0);
}
#[test]
fn is_empty_should_return_false_when_not_empty() {
let descriptor = existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default()));
let mut collection = ServiceCollection::new();
collection.add(descriptor);
let not_empty = !collection.is_empty();
assert!(not_empty);
}
#[test]
fn length_should_return_count_when_not_empty() {
let descriptor = existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default()));
let mut collection = ServiceCollection::new();
collection.add(descriptor);
let length = collection.len();
assert_eq!(length, 1);
}
#[test]
fn clear_should_remove_all_elements() {
let descriptor = existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default()));
let mut collection = ServiceCollection::new();
collection.add(descriptor);
collection.clear();
assert!(collection.is_empty());
}
#[test]
fn try_add_should_do_nothing_when_service_is_registered() {
let mut collection = ServiceCollection::new();
collection.add(singleton::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl::default())));
collection
.try_add(singleton::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl::default())));
assert_eq!(collection.len(), 1);
}
#[test]
fn try_add_to_all_should_add_descriptor_when_implementation_is_unregistered() {
let mut collection = ServiceCollection::new();
collection.add(existing::<dyn TestService, TestServiceImpl>(Box::new(
TestServiceImpl::default(),
)));
collection.try_add_to_all(
singleton::<dyn OtherTestService, OtherTestServiceImpl>()
.from(|sp| Ref::new(OtherTestServiceImpl::new(sp.get_required::<dyn TestService>()))),
);
let count = collection.len();
assert_eq!(count, 2);
}
#[test]
fn try_add_to_all_should_not_add_descriptor_when_implementation_is_registered() {
let mut collection = ServiceCollection::new();
collection.add(existing::<dyn TestService, TestServiceImpl>(Box::new(
TestServiceImpl::default(),
)));
collection.try_add_to_all(
transient::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl::default())),
);
let count = collection.len();
assert_eq!(count, 1);
}
#[test]
fn try_add_all_should_only_add_descriptors_for_unregistered_implementations() {
let descriptors = vec![
existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default())),
transient::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl::default())),
];
let mut collection = ServiceCollection::new();
collection.try_add_all(descriptors.into_iter());
let count = collection.len();
assert_eq!(count, 1);
}
#[test]
fn replace_should_replace_first_registered_service() {
let mut collection = ServiceCollection::new();
collection
.add(singleton::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl::default())))
.add(singleton::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl::default())));
collection
.replace(singleton::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl::default())));
assert_eq!(collection.len(), 2);
}
#[test]
fn remove_all_should_remove_registered_services() {
let mut collection = ServiceCollection::new();
collection
.add(singleton::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl::default())))
.add(singleton::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl::default())));
collection.remove_all::<dyn TestService>();
assert!(collection.is_empty());
}
#[test]
fn try_replace_should_do_nothing_when_service_is_registered() {
let mut collection = ServiceCollection::new();
collection
.add(singleton::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl { value: 1 })));
collection.try_replace(
singleton::<dyn TestService, TestServiceImpl>().from(|_| Ref::new(TestServiceImpl { value: 2 })),
);
let value = collection
.build_provider()
.unwrap()
.get_required::<dyn TestService>()
.value();
assert_eq!(value, 1);
}
#[test]
fn remove_should_remove_element_at_index() {
let descriptor = existing::<dyn TestService, TestServiceImpl>(Box::new(TestServiceImpl::default()));
let mut collection = ServiceCollection::new();
collection.add(descriptor);
let _ = collection.remove(0);
assert!(collection.is_empty());
}
#[test]
fn service_collection_should_drop_existing_as_service() {
let file = new_temp_file("drop1");
{
let mut services = ServiceCollection::new();
services.add(existing_as_self(Droppable::new(file.clone())));
}
let dropped = !file.exists();
remove_file(&file).ok();
assert!(dropped);
}
#[test]
fn service_collection_should_not_drop_service_if_never_instantiated() {
let file = new_temp_file("drop4");
let mut services = ServiceCollection::new();
{
services
.add(existing::<Path, PathBuf>(file.clone().into_boxed_path()))
.add(singleton_as_self().from(|sp| Ref::new(Droppable::new(sp.get_required::<Path>().to_path_buf()))));
}
let not_dropped = file.exists();
remove_file(&file).ok();
assert!(not_dropped);
}
}