/
DerivedTypeMappingBuilder.cs
112 lines (99 loc) · 4.38 KB
/
DerivedTypeMappingBuilder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
using Microsoft.CodeAnalysis;
using Riok.Mapperly.Configuration;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Descriptors.Mappings.ExistingTarget;
using Riok.Mapperly.Diagnostics;
using Riok.Mapperly.Helpers;
namespace Riok.Mapperly.Descriptors.MappingBuilders;
public static class DerivedTypeMappingBuilder
{
public static INewInstanceMapping? TryBuildMapping(MappingBuilderContext ctx)
{
var derivedTypeMappings = TryBuildContainedMappings(ctx);
if (derivedTypeMappings == null)
return null;
return ctx.IsExpression
? new DerivedTypeIfExpressionMapping(ctx.Source, ctx.Target, derivedTypeMappings)
: new DerivedTypeSwitchMapping(ctx.Source, ctx.Target, derivedTypeMappings);
}
public static IExistingTargetMapping? TryBuildExistingTargetMapping(MappingBuilderContext ctx)
{
var derivedTypeMappings = TryBuildExistingTargetContainedMappings(ctx);
return derivedTypeMappings == null ? null : new DerivedExistingTargetTypeSwitchMapping(ctx.Source, ctx.Target, derivedTypeMappings);
}
public static IReadOnlyCollection<INewInstanceMapping>? TryBuildContainedMappings(
MappingBuilderContext ctx,
bool duplicatedSourceTypesAllowed = false
)
{
return ctx.Configuration.DerivedTypes.Count == 0
? null
: BuildContainedMappings(ctx, ctx.Configuration.DerivedTypes, ctx.FindOrBuildMapping, duplicatedSourceTypesAllowed);
}
private static IReadOnlyCollection<IExistingTargetMapping>? TryBuildExistingTargetContainedMappings(
MappingBuilderContext ctx,
bool duplicatedSourceTypesAllowed = false
)
{
return ctx.Configuration.DerivedTypes.Count == 0
? null
: BuildContainedMappings(
ctx,
ctx.Configuration.DerivedTypes,
(source, target, options, _) => ctx.FindOrBuildExistingTargetMapping(source, target, options),
duplicatedSourceTypesAllowed
);
}
private static IReadOnlyCollection<TMapping> BuildContainedMappings<TMapping>(
MappingBuilderContext ctx,
IReadOnlyCollection<DerivedTypeMappingConfiguration> configs,
Func<ITypeSymbol, ITypeSymbol, MappingBuildingOptions, Location?, TMapping?> findOrBuildMapping,
bool duplicatedSourceTypesAllowed
)
where TMapping : ITypeMapping
{
var derivedTypeMappingSourceTypes = new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default);
var derivedTypeMappings = new List<TMapping>(configs.Count);
foreach (var config in configs)
{
// set types non-nullable as they can never be null when type-switching.
var sourceType = config.SourceType.NonNullable();
var targetType = config.TargetType.NonNullable();
if (!duplicatedSourceTypesAllowed && !derivedTypeMappingSourceTypes.Add(sourceType))
{
ctx.ReportDiagnostic(DiagnosticDescriptors.DerivedSourceTypeDuplicated, sourceType);
continue;
}
var typeCheckerResult = ctx.GenericTypeChecker.InferAndCheckTypes(
ctx.UserSymbol!.TypeParameters,
(ctx.Source, sourceType),
(ctx.Target, targetType)
);
if (!typeCheckerResult.Success)
{
if (ReferenceEquals(sourceType, typeCheckerResult.FailedArgument))
{
ctx.ReportDiagnostic(DiagnosticDescriptors.DerivedSourceTypeIsNotAssignableToParameterType, sourceType, ctx.Source);
}
else
{
ctx.ReportDiagnostic(DiagnosticDescriptors.DerivedTargetTypeIsNotAssignableToReturnType, targetType, ctx.Target);
}
continue;
}
var mapping = findOrBuildMapping(
sourceType,
targetType,
MappingBuildingOptions.KeepUserSymbol | MappingBuildingOptions.MarkAsReusable | MappingBuildingOptions.IgnoreDerivedTypes,
config.Location
);
if (mapping == null)
{
ctx.ReportDiagnostic(DiagnosticDescriptors.CouldNotCreateMapping, sourceType, targetType);
continue;
}
derivedTypeMappings.Add(mapping);
}
return derivedTypeMappings;
}
}