Skip to content

Commit

Permalink
feat: Add MapperAttribute.GenerateStaticMethods
Browse files Browse the repository at this point in the history
  • Loading branch information
trejjam committed Aug 29, 2023
1 parent 975f374 commit 02175cf
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 4 deletions.
21 changes: 21 additions & 0 deletions docs/docs/configuration/mapper.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,24 @@ dotnet_diagnostic.RMG020.severity = error # Unmapped source member
### Strict enum mappings

To enforce strict enum mappings set `RMG037` and `RMG038` to error, see [strict enum mappings](./enum.mdx).

### Static methods in instantiable class

When you need to generate static methods inside nonstatic/instantiable class you can use `GenerateStaticMethods` option.

```csharp
public interface IMyInterface
{
static abstract CarDto ToDto(Car car);
}

// highlight-start
[Mapper(GenerateStaticMethods = true)]
// highlight-end
public partial class CarMapper : IMyInterface
{
[MapperIgnoreTarget(nameof(CarDto.MakeId))]
[MapperIgnoreSource(nameof(Car.Id))]
public static partial CarDto ToDto(Car car);
}
```
5 changes: 5 additions & 0 deletions src/Riok.Mapperly.Abstractions/MapperAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,9 @@ public sealed class MapperAttribute : Attribute
/// Defaults to <see cref="IgnoreObsoleteMembersStrategy.None"/>.
/// </summary>
public IgnoreObsoleteMembersStrategy IgnoreObsoleteMembersStrategy { get; set; } = IgnoreObsoleteMembersStrategy.None;

/// <summary>
/// Enables static methods in instantiable mappers (e.g. <code>public partial class A { public static partial string MapToString(int value); }</code>)
/// </summary>
public bool GenerateStaticMethods { get; set; }
}
2 changes: 2 additions & 0 deletions src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingIgnoreCase.get -> bool
Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingIgnoreCase.set -> void
Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingStrategy.get -> Riok.Mapperly.Abstractions.EnumMappingStrategy
Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingStrategy.set -> void
Riok.Mapperly.Abstractions.MapperAttribute.GenerateStaticMethods.get -> bool
Riok.Mapperly.Abstractions.MapperAttribute.GenerateStaticMethods.set -> void
Riok.Mapperly.Abstractions.MapperAttribute.IgnoreObsoleteMembersStrategy.get -> Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy
Riok.Mapperly.Abstractions.MapperAttribute.IgnoreObsoleteMembersStrategy.set -> void
Riok.Mapperly.Abstractions.MapperAttribute.MapperAttribute() -> void
Expand Down
4 changes: 3 additions & 1 deletion src/Riok.Mapperly/Descriptors/Mappings/MethodMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public abstract class MethodMapping : NewInstanceMapping
private readonly ITypeSymbol _returnType;

private string? _methodName;
private bool? _methodIsStatic;

protected MethodMapping(ITypeSymbol sourceType, ITypeSymbol targetType)
: base(sourceType, targetType)
Expand All @@ -47,6 +48,7 @@ ITypeSymbol targetType
ReferenceHandlerParameter = referenceHandlerParameter;
_accessibility = method.DeclaredAccessibility;
_methodName = method.Name;
_methodIsStatic = method.IsStatic;
_returnType = method.ReturnType.UpgradeNullable();
}

Expand Down Expand Up @@ -77,7 +79,7 @@ public virtual MethodDeclarationSyntax BuildMethod(SourceEmitterContext ctx)
ReserveParameterNames(typeMappingBuildContext.NameBuilder, parameters);

return MethodDeclaration(returnType, Identifier(MethodName))
.WithModifiers(TokenList(BuildModifiers(ctx.IsStatic)))
.WithModifiers(TokenList(BuildModifiers(ctx.IsStatic || (_methodIsStatic ?? false))))
.WithParameterList(parameters)
.WithBody(Block(BuildBody(typeMappingBuildContext)));
}
Expand Down
6 changes: 4 additions & 2 deletions src/Riok.Mapperly/Descriptors/UserMethodMappingExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ internal static IEnumerable<IUserMapping> ExtractUserMappings(SimpleMappingBuild
// extract user implemented and user defined mappings from mapper
foreach (var methodSymbol in ExtractMethods(mapperSymbol))
{
var isStatic = mapperSymbol.IsStatic || ctx.MapperConfiguration.GenerateStaticMethods;

var mapping =
BuilderUserDefinedMapping(ctx, methodSymbol, mapperSymbol.IsStatic)
?? BuildUserImplementedMapping(ctx, methodSymbol, null, false, mapperSymbol.IsStatic);
BuilderUserDefinedMapping(ctx, methodSymbol, isStatic)
?? BuildUserImplementedMapping(ctx, methodSymbol, null, false, isStatic);
if (mapping != null)
yield return mapping;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public static class DiagnosticDescriptors
public static readonly DiagnosticDescriptor PartialStaticMethodInInstanceMapper = new DiagnosticDescriptor(
"RMG018",
"Partial static mapping method in an instance mapper",
"{0} is a partial static mapping method in an instance mapper. Static mapping methods are only supported in static mappers.",
$"{{0}} is a partial static mapping method in an instance mapper. Static mapping methods are supported in static mappers or using following mapper configuration: Mapper({nameof(MapperAttribute.GenerateStaticMethods)}=true).",
DiagnosticCategories.Mapper,
DiagnosticSeverity.Error,
true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Riok.Mapperly.IntegrationTests.Dto
{
public interface ITestStaticInterface
{
#if NET7_0_OR_GREATER
static abstract int StaticAbstractDirectInt(int value);
#endif
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Riok.Mapperly.Abstractions;
using Riok.Mapperly.IntegrationTests.Dto;

namespace Riok.Mapperly.IntegrationTests.Mapper
{
[Mapper(GenerateStaticMethods = true)]
public partial class TestMapperWithStaticMethods : ITestStaticInterface
{
public static partial double DirectDouble(double value);

public static partial int StaticAbstractDirectInt(int value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Threading.Tasks;
using Riok.Mapperly.IntegrationTests.Dto;
using Riok.Mapperly.IntegrationTests.Helpers;
using Riok.Mapperly.IntegrationTests.Mapper;
using VerifyXunit;
using Xunit;

namespace Riok.Mapperly.IntegrationTests
{
[UsesVerify]
public class TestMapperWithStaticMethodsTest : BaseMapperTest
{
[Fact]
[VersionedSnapshot(Versions.NET6_0 | Versions.NET7_0)]
public Task SnapshotGeneratedSource()
{
var path = GetGeneratedMapperFilePath(nameof(TestMapperWithStaticMethods));
return Verifier.VerifyFile(path);
}

[Theory
#if !NET7_0_OR_GREATER
(Skip = "Requires language version '11.0' or greater")
#endif
]
[InlineData(8)]
[InlineData(12)]
public void CallStaticInterfaceMemberShouldWork(int value)
{
Assert.Equal(value, GenericMethod<TestMapperWithStaticMethods>(value));

static int GenericMethod<T>(int value)
where T : ITestStaticInterface
{
#if NET7_0_OR_GREATER
return T.StaticAbstractDirectInt(value);
#else
// It's intentional "wrong" value, it's here only to return something
return 0;
#endif
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// <auto-generated />
#nullable enable
namespace Riok.Mapperly.IntegrationTests.Mapper
{
public partial class TestMapperWithStaticMethods
{
public static partial double DirectDouble(double value)
{
return value;
}

public static partial int StaticAbstractDirectInt(int value)
{
return value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// <auto-generated />
#nullable enable
namespace Riok.Mapperly.IntegrationTests.Mapper
{
public partial class TestMapperWithStaticMethods
{
public static partial double DirectDouble(double value)
{
return value;
}

public static partial int StaticAbstractDirectInt(int value)
{
return value;
}
}
}

0 comments on commit 02175cf

Please sign in to comment.