-
-
Notifications
You must be signed in to change notification settings - Fork 154
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
MapDerivedType
for existing target type mapping
- Loading branch information
1 parent
157d47e
commit 4a3d5ac
Showing
7 changed files
with
485 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
src/Riok.Mapperly/Descriptors/Mappings/DerivedExistingTargetTypeMapping.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Riok.Mapperly.Descriptors.Mappings.ExistingTarget; | ||
using Riok.Mapperly.Emit.Syntax; | ||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; | ||
using static Riok.Mapperly.Emit.Syntax.SyntaxFactoryHelper; | ||
|
||
namespace Riok.Mapperly.Descriptors.Mappings; | ||
|
||
/// <summary> | ||
/// A derived type mapping maps one base type or interface to another | ||
/// by implementing a if with instance checks over known types and performs the provided mapping for each type. | ||
/// </summary> | ||
public class DerivedExistingTargetTypeMapping : ExistingTargetMapping | ||
{ | ||
private const string SourceName = "source"; | ||
private const string TargetName = "target"; | ||
private const string GetTypeMethodName = nameof(GetType); | ||
private readonly IReadOnlyCollection<IExistingTargetMapping> _existingTargetTypeMappings; | ||
|
||
public DerivedExistingTargetTypeMapping( | ||
ITypeSymbol sourceType, | ||
ITypeSymbol targetType, | ||
IReadOnlyCollection<IExistingTargetMapping> existingTargetTypeMappings | ||
) | ||
: base(sourceType, targetType) | ||
{ | ||
_existingTargetTypeMappings = existingTargetTypeMappings; | ||
} | ||
|
||
public override IEnumerable<StatementSyntax> Build(TypeMappingBuildContext ctx, ExpressionSyntax target) | ||
{ | ||
var sourceExpression = TupleExpression(CommaSeparatedList(Argument(ctx.Source), Argument(target))); | ||
var sections = _existingTargetTypeMappings.Select(x => BuildSwitchSection(ctx, x)).Append(BuildDefaultSwitchSection(ctx)); | ||
|
||
yield return ctx.SyntaxFactory.SwitchStatement(sourceExpression, List(sections)).AddLeadingLineFeed(ctx.SyntaxFactory.Indentation); | ||
} | ||
|
||
private SwitchSectionSyntax BuildSwitchSection(TypeMappingBuildContext ctx, IExistingTargetMapping mapping) | ||
{ | ||
// MapToB(source, target); | ||
var (sectionCtx, sourceVariableName) = ctx.WithNewScopedSource(SourceName); | ||
var targetVariableName = sectionCtx.NameBuilder.New(TargetName); | ||
sectionCtx = sectionCtx.AddIndentation(); | ||
|
||
var positionalTypeMatch = PositionalPatternClause( | ||
CommaSeparatedList( | ||
Subpattern(DeclarationPattern(mapping.SourceType, sourceVariableName)), | ||
Subpattern(DeclarationPattern(mapping.TargetType, targetVariableName)) | ||
) | ||
); | ||
|
||
var pattern = RecursivePattern().WithPositionalPatternClause(positionalTypeMatch); | ||
var caseLabel = CasePatternSwitchLabel(pattern).AddLeadingLineFeed(sectionCtx.SyntaxFactory.Indentation); | ||
|
||
var target = IdentifierName(targetVariableName); | ||
|
||
var statementContext = sectionCtx.AddIndentation(); | ||
var statements = List( | ||
mapping.Build(statementContext, target).Append(BreakStatement().AddLeadingLineFeed(statementContext.SyntaxFactory.Indentation)) | ||
); | ||
var section = SwitchSection().WithLabels(SingletonList<SwitchLabelSyntax>(caseLabel)).WithStatements(statements); | ||
|
||
return section; | ||
} | ||
|
||
private SwitchSectionSyntax BuildDefaultSwitchSection(TypeMappingBuildContext ctx) | ||
{ | ||
// default: | ||
var defaultCaseLabel = DefaultSwitchLabel().AddLeadingLineFeed(ctx.SyntaxFactory.Indentation + 1); | ||
|
||
// throw new ArgumentException(msg, nameof(ctx.Source)), | ||
var sourceType = Invocation(MemberAccess(ctx.Source, GetTypeMethodName)); | ||
var throwExpression = ThrowArgumentExpression( | ||
InterpolatedString($"Cannot map {sourceType} to {TargetType.ToDisplayString()} as there is no known derived type mapping"), | ||
ctx.Source | ||
) | ||
.AddLeadingLineFeed(ctx.SyntaxFactory.Indentation + 2); | ||
|
||
var stat = new StatementSyntax[] { ExpressionStatement(throwExpression) }; | ||
return SwitchSection().WithLabels(SingletonList<SwitchLabelSyntax>(defaultCaseLabel)).WithStatements(List(stat)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.