using System.Collections.Concurrent;
namespace zero.Mapper;
public class ZeroMapper : IZeroMapper
{
///
/// Concurrent cache for all ctor definitions
///
protected ConcurrentDictionary>> MapDefinitions { get; private set; } = new();
///
/// Concurrent cache for all constructor definitions
///
protected ConcurrentDictionary>> ConstructorDefinitions { get; private set; } = new();
public ZeroMapper(IEnumerable profiles)
{
foreach (IMapperProfile profile in profiles)
{
profile.Configure(this);
}
}
///
public void Define(Func ctor, Action map)
{
Type sourceType = typeof(TSource);
Type destinationType = typeof(TDestination);
var sourceMaps = MapDefinitions.GetOrAdd(sourceType, _ => new());
var sourceCtors = ConstructorDefinitions.GetOrAdd(sourceType, _ => new());
sourceCtors[destinationType] = (source, ctx) => ctor((TSource)source, ctx);
sourceMaps[destinationType] = (source, destination, ctx) => map((TSource)source, (TDestination)destination, ctx);
}
///
public void Override(Func ctor, Action> map)
{
Type sourceType = typeof(TSource);
Type destinationType = typeof(TDestination);
var sourceMaps = MapDefinitions.GetValueOrDefault(sourceType);
var sourceCtors = ConstructorDefinitions.GetValueOrDefault(sourceType);
if (sourceMaps == null || sourceCtors == null || !sourceMaps.ContainsKey(destinationType))
{
return;
}
var baseMap = sourceMaps[destinationType];
sourceCtors[destinationType] = (source, ctx) => ctor((TSource)source, ctx);
sourceMaps[destinationType] = (source, destination, ctx) => map((TSource)source, (TDestination)destination, ctx, (source, destination, ctx) => baseMap(source, destination, ctx));
}
///
public TDestination Map(object source, Type sourceType, TDestination destination = default)
{
if (source == null)
{
return default;
}
Type destinationType = typeof(TDestination);
if (destinationType == source.GetType())
{
return (TDestination)source;
}
ZeroMapperContext mapperContext = new(this);
var constructor = GetConstructor(sourceType, destinationType);
var map = GetMap(sourceType, destinationType);
if (constructor != null && map != null)
{
destination ??= (TDestination)constructor(source, mapperContext);
map(source, destination, mapperContext);
return destination;
}
// TODO enumerables
throw new InvalidOperationException($"Don't know how to map {sourceType.FullName} to {destinationType.FullName}.");
}
protected virtual Func