Skip to content

Commit

Permalink
feat: support mappers nested in classes (#354)
Browse files Browse the repository at this point in the history
  • Loading branch information
latonz authored Apr 18, 2023
1 parent 1deba92 commit 80a193a
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 20 deletions.
21 changes: 21 additions & 0 deletions src/Riok.Mapperly/Descriptors/MapperDescriptor.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Descriptors.Mappings;
Expand All @@ -14,13 +15,16 @@ public MapperDescriptor(ClassDeclarationSyntax syntax, INamedTypeSymbol symbol,
Syntax = syntax;
Symbol = symbol;
NameBuilder = nameBuilder;
Name = BuildName(symbol);

if (!Symbol.ContainingNamespace.IsGlobalNamespace)
{
Namespace = Symbol.ContainingNamespace.ToDisplayString();
}
}

public string Name { get; }

public string? Namespace { get; }

public ClassDeclarationSyntax Syntax { get; }
Expand All @@ -33,4 +37,21 @@ public MapperDescriptor(ClassDeclarationSyntax syntax, INamedTypeSymbol symbol,

public void AddTypeMapping(MethodMapping mapping)
=> _methodMappings.Add(mapping);

private string BuildName(INamedTypeSymbol symbol)
{
if (symbol.ContainingType == null)
return symbol.Name;

var sb = new StringBuilder(symbol.Name);
var containingType = symbol.ContainingType;
while (containingType != null)
{
sb.Insert(0, '.').Insert(0, containingType.Name);
containingType = containingType.ContainingType;
}

return sb.ToString();
}
}

24 changes: 22 additions & 2 deletions src/Riok.Mapperly/Emit/SourceEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ public static class SourceEmitter
public static CompilationUnitSyntax Build(MapperDescriptor descriptor)
{
var sourceEmitterContext = new SourceEmitterContext(descriptor.Symbol.IsStatic, descriptor.NameBuilder);
var classDeclaration = ClassDeclaration(descriptor.Syntax.Identifier)
MemberDeclarationSyntax member = ClassDeclaration(descriptor.Syntax.Identifier)
.WithModifiers(descriptor.Syntax.Modifiers)
.WithMembers(List(BuildMembers(descriptor, sourceEmitterContext)));

member = WrapInClassesAsNeeded(descriptor.Symbol, member);
member = WrapInNamespaceIfNeeded(descriptor.Namespace, member);

return CompilationUnit()
.WithMembers(SingletonList(WrapInNamespaceIfNeeded(descriptor.Namespace, classDeclaration)))
.WithMembers(SingletonList(member))
.WithLeadingTrivia(Nullable(true))
.NormalizeWhitespace();
}
Expand All @@ -28,6 +31,23 @@ private static IEnumerable<MemberDeclarationSyntax> BuildMembers(
return descriptor.MethodTypeMappings.Select(mapping => mapping.BuildMethod(sourceEmitterContext));
}

private static MemberDeclarationSyntax WrapInClassesAsNeeded(
INamedTypeSymbol symbol,
MemberDeclarationSyntax syntax)
{
var containingType = symbol.ContainingType;
while (containingType != null)
{
if (containingType.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not ClassDeclarationSyntax containingTypeSyntax)
return syntax;

syntax = containingTypeSyntax.WithMembers(SingletonList(syntax));
containingType = containingType.ContainingType;
}

return syntax;
}

private static MemberDeclarationSyntax WrapInNamespaceIfNeeded(string? namespaceName, MemberDeclarationSyntax classDeclaration)
{
return namespaceName == null
Expand Down
2 changes: 1 addition & 1 deletion src/Riok.Mapperly/MapperGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private static void Execute(Compilation compilation, ImmutableArray<ClassDeclara
var descriptor = builder.Build();

ctx.AddSource(
uniqueNameBuilder.New(mapperSymbol.Name) + GeneratedFileSuffix,
uniqueNameBuilder.New(descriptor.Name) + GeneratedFileSuffix,
SourceText.From(SourceEmitter.Build(descriptor).ToFullString(), Encoding.UTF8));
}
}
Expand Down
16 changes: 16 additions & 0 deletions test/Riok.Mapperly.IntegrationTests/Mapper/NestedMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Riok.Mapperly.Abstractions;

namespace Riok.Mapperly.IntegrationTests.Mapper
{
public static partial class NestedTestMapper
{
public static partial class TestNesting
{
[Mapper]
public static partial class NestedMapper
{
public static partial int ToInt(decimal value);
}
}
}
}
26 changes: 26 additions & 0 deletions test/Riok.Mapperly.IntegrationTests/NestedMapperTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Threading.Tasks;
using FluentAssertions;
using Riok.Mapperly.IntegrationTests.Mapper;
using VerifyXunit;
using Xunit;

namespace Riok.Mapperly.IntegrationTests
{
[UsesVerify]
public class NestedMapperTest : BaseMapperTest
{
[Fact]
public Task SnapshotGeneratedSource()
{
var path = GetGeneratedMapperFilePath($"{nameof(NestedTestMapper)}.{nameof(NestedTestMapper.TestNesting)}.{nameof(NestedTestMapper.TestNesting.NestedMapper)}");
return Verifier.VerifyFile(path);
}

[Fact]
public void RunMappingShouldWork()
{
var v = NestedTestMapper.TestNesting.NestedMapper.ToInt(10.25m);
v.Should().Be(10);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#nullable enable
namespace Riok.Mapperly.IntegrationTests.Mapper
{
public static partial class NestedTestMapper
{
public static partial class TestNesting
{
public static partial class NestedMapper
{
public static partial int ToInt(decimal value)
{
return (int)value;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#nullable enable
namespace Riok.Mapperly.IntegrationTests.Mapper
{
public static partial class NestedTestMapper
{
public static partial class TestNesting
{
public static partial class NestedMapper
{
public static partial int ToInt(decimal value)
{
return (int)value;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#nullable enable
namespace Riok.Mapperly.IntegrationTests.Mapper
{
public static partial class NestedTestMapper
{
public static partial class TestNesting
{
public static partial class NestedMapper
{
public static partial int ToInt(decimal value)
{
return (int)value;
}
}
}
}
}
58 changes: 41 additions & 17 deletions test/Riok.Mapperly.Tests/Mapping/MapperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,51 @@ public class MapperTest
[Fact]
public Task SameMapperNameInMultipleNamespacesShouldWork()
{
var source = @"
using Riok.Mapperly.Abstractions;
var source = TestSourceBuilder.CSharp(
"""
using Riok.Mapperly.Abstractions;
namespace Test.A
{
[Mapper]
internal partial class FooBarMapper
{
internal partial string FooToBar(string value);
namespace Test.A
{
[Mapper]
internal partial class FooBarMapper
{
internal partial string FooToBar(string value);
}
}
namespace Test.B
{
[Mapper]
internal partial class FooBarMapper
{
internal partial string FooToBar(string value);
}
}
""");

return TestHelper.VerifyGenerator(source);
}
}

namespace Test.B
{
[Mapper]
internal partial class FooBarMapper
[Fact]
public Task MapperInNestedClassShouldWork()
{
internal partial string FooToBar(string value);
}
}
";
var source = TestSourceBuilder.CSharp(
"""
using Riok.Mapperly.Abstractions;
public static partial class CarFeature
{
public static partial class Mappers
{
[Mapper]
public partial class CarMapper
{
public partial int ToInt(double value);
}
}
}
""");

return TestHelper.VerifyGenerator(source);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//HintName: CarFeature.Mappers.CarMapper.g.cs
#nullable enable
public static partial class CarFeature
{
public static partial class Mappers
{
public partial class CarMapper
{
public partial int ToInt(double value)
{
return (int)value;
}
}
}
}

0 comments on commit 80a193a

Please sign in to comment.