-
-
Notifications
You must be signed in to change notification settings - Fork 141
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
21fdc99
commit 11dec6b
Showing
13 changed files
with
536 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, target)); | ||
|
||
yield return ctx.SyntaxFactory.SwitchStatement(sourceExpression, List(sections)).AddLeadingLineFeed(ctx.SyntaxFactory.Indentation); | ||
} | ||
|
||
private SwitchSectionSyntax BuildSwitchSection(TypeMappingBuildContext ctx, IExistingTargetMapping mapping) | ||
{ | ||
var (sectionCtx, sourceVariableName) = ctx.WithNewScopedSource(SourceName); | ||
var targetVariableName = sectionCtx.NameBuilder.New(TargetName); | ||
sectionCtx = sectionCtx.AddIndentation(); | ||
|
||
// (A source, B target) | ||
var positionalTypeMatch = PositionalPatternClause( | ||
CommaSeparatedList( | ||
Subpattern(DeclarationPattern(mapping.SourceType, sourceVariableName)), | ||
Subpattern(DeclarationPattern(mapping.TargetType, targetVariableName)) | ||
) | ||
); | ||
var pattern = RecursivePattern().WithPositionalPatternClause(positionalTypeMatch); | ||
|
||
// case (A source, B target): | ||
var caseLabel = CasePatternSwitchLabel(pattern).AddLeadingLineFeed(sectionCtx.SyntaxFactory.Indentation); | ||
|
||
// break; | ||
var statementContext = sectionCtx.AddIndentation(); | ||
var breakStatement = BreakStatement().AddLeadingLineFeed(statementContext.SyntaxFactory.Indentation); | ||
var target = IdentifierName(targetVariableName); | ||
var statements = List(mapping.Build(statementContext, target).Append(breakStatement)); | ||
|
||
return SwitchSection().WithLabels(SingletonList<SwitchLabelSyntax>(caseLabel)).WithStatements(statements); | ||
} | ||
|
||
private SwitchSectionSyntax BuildDefaultSwitchSection(TypeMappingBuildContext ctx, ExpressionSyntax target) | ||
{ | ||
// default: | ||
var defaultCaseLabel = DefaultSwitchLabel().AddLeadingLineFeed(ctx.SyntaxFactory.Indentation + 1); | ||
|
||
// throw new ArgumentException(msg, nameof(ctx.Source)), | ||
var sourceType = Invocation(MemberAccess(ctx.Source, GetTypeMethodName)); | ||
var targetType = Invocation(MemberAccess(target, GetTypeMethodName)); | ||
var throwExpression = ThrowArgumentExpression( | ||
InterpolatedString($"Cannot map {sourceType} to {targetType} 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.