// Author: Gockner, Simon
// Created: 2021-12-02
// Copyright(c) 2021 SimonG. All Rights Reserved.
using LightweightIocContainer.Interfaces.Registrations;
using LightweightIocContainer.Interfaces.Registrations.Fluent;
using NSubstitute;
namespace LightweightIocContainer.Validation;
///
/// Validator for your to check if everything can be resolved with your current setup
///
public class IocValidator(IocContainer iocContainer)
{
private readonly List<(Type type, object? parameter)> _parameters = [];
///
/// Add parameters that can't be default for your type to be created successfully
///
/// The new value of the parameter
/// The of your registered interface
/// The of your
public void AddParameter(TParameter parameter) => _parameters.Add((typeof(TInterface), parameter));
///
/// Validates your given and checks if everything can be resolved with the current setup
/// Collection of all exceptions that are thrown during validation
///
public void Validate()
{
List validationExceptions = [];
foreach (IRegistration registration in iocContainer.Registrations)
{
var definedParameters = _parameters.Where(p => p.type == registration.InterfaceType);
if (registration is IWithFactoryInternal { Factory: not null } withFactoryRegistration)
{
(from createMethod in withFactoryRegistration.Factory.CreateMethods.Where(m => m.ReturnType == registration.InterfaceType)
select createMethod.GetParameters().Select(p => p.ParameterType)
into parameterTypes
select (from parameterType in parameterTypes
let definedParameter = definedParameters
.FirstOrDefault(p => parameterType.IsInstanceOfType(p.parameter))
select definedParameter == default ? GetMockOrDefault(parameterType) : definedParameter.parameter).ToArray())
.ToList()
.ForEach(p => TryResolve(registration.InterfaceType, p, validationExceptions, true));
}
else
{
object?[] arguments = definedParameters.Select(p => p.parameter).ToArray();
TryResolve(registration.InterfaceType, arguments, validationExceptions);
}
}
if (validationExceptions.Any())
throw new AggregateException("Validation failed.", validationExceptions);
}
private void TryResolve(Type type, object?[]? arguments, List validationExceptions, bool isFactoryResolve = false)
{
(bool success, object _, Exception? exception) = iocContainer.TryResolveNonGeneric(type, arguments, null, isFactoryResolve);
if (success)
return;
if (exception is not null)
validationExceptions.Add(exception);
}
private T GetMock() where T : class => Substitute.For();
private object? GetMockOrDefault(Type type)
{
if (type.IsValueType)
return Activator.CreateInstance(type);
if (type == typeof(string))
return string.Empty;
if (!type.IsInterface)
return new NullParameter(type);
try
{
return GenericMethodCaller.CallPrivate(this, nameof(GetMock), type);
}
catch (Exception)
{
return new NullParameter(type);
}
}
}