diff --git a/docs/docs/02-configuration/10-conversions.md b/docs/docs/02-configuration/10-conversions.md
new file mode 100644
index 0000000000..152ac5b83c
--- /dev/null
+++ b/docs/docs/02-configuration/10-conversions.md
@@ -0,0 +1,31 @@
+# Conversions
+
+Mapperly implements several types of automatic conversions.
+A list of conversions supported by Mapperly is available [here](../api/riok.mapperly.abstractions.mappingconversiontype#fields).
+
+## Disable all automatic conversions
+
+To disable all conversions supported by Mapperly set `EnabledConversions` to `None`:
+```csharp
+// highlight-start
+[Mapper(EnabledConversions = MappingConversionType.None)]
+// highlight-end
+public partial class CarMapper
+{
+ ...
+}
+```
+
+## Disable specific automatic conversions
+
+To disable a specific conversion type, set `EnabledConversions` to `All` excluding the conversion type to disable:
+```csharp
+// this disables conversions using the ToString() method:
+// highlight-start
+[Mapper(EnabledConversions = MappingConversionType.All & ~MappingConversionType.ToStringMethod)]
+// highlight-end
+public partial class CarMapper
+{
+ ...
+}
+```
diff --git a/docs/docs/02-configuration/10-analyzer-diagnostics.mdx b/docs/docs/02-configuration/11-analyzer-diagnostics.mdx
similarity index 100%
rename from docs/docs/02-configuration/10-analyzer-diagnostics.mdx
rename to docs/docs/02-configuration/11-analyzer-diagnostics.mdx
diff --git a/docs/docs/02-configuration/11-generated-source.mdx b/docs/docs/02-configuration/12-generated-source.mdx
similarity index 100%
rename from docs/docs/02-configuration/11-generated-source.mdx
rename to docs/docs/02-configuration/12-generated-source.mdx
diff --git a/src/Riok.Mapperly.Abstractions/MapperAttribute.cs b/src/Riok.Mapperly.Abstractions/MapperAttribute.cs
index 0a679a6636..1dd8f9fbe5 100644
--- a/src/Riok.Mapperly.Abstractions/MapperAttribute.cs
+++ b/src/Riok.Mapperly.Abstractions/MapperAttribute.cs
@@ -46,4 +46,18 @@ public sealed class MapperAttribute : Attribute
/// With =false, the array and each person is cloned.
///
public bool UseDeepCloning { get; set; }
+
+ ///
+ /// Enabled conversions which Mapperly automatically implements.
+ /// By default all supported type conversions are enabled.
+ ///
+ /// Eg. to disable all automatically implemented conversions:
+ /// EnabledConversions = MappingConversionType.None
+ ///
+ ///
+ /// Eg. to disable ToString() method calls:
+ /// EnabledConversions = MappingConversionType.All & ~MappingConversionType.ToStringMethod
+ ///
+ ///
+ public MappingConversionType EnabledConversions { get; set; } = MappingConversionType.All;
}
diff --git a/src/Riok.Mapperly.Abstractions/MappingConversionType.cs b/src/Riok.Mapperly.Abstractions/MappingConversionType.cs
new file mode 100644
index 0000000000..17b4da9b02
--- /dev/null
+++ b/src/Riok.Mapperly.Abstractions/MappingConversionType.cs
@@ -0,0 +1,69 @@
+namespace Riok.Mapperly.Abstractions;
+
+///
+/// A represents a type of conversion
+/// how one type can be converted into another.
+///
+[Flags]
+public enum MappingConversionType
+{
+ ///
+ /// None.
+ ///
+ None = 0,
+
+ ///
+ /// Use the constructor of the target type,
+ /// which accepts the source type as a single parameter.
+ ///
+ Constructor = 1 << 0,
+
+ ///
+ /// An implicit cast from the source type to the target type.
+ ///
+ ImplicitCast = 1 << 1,
+
+ ///
+ /// An explicit cast from the source type to the target type.
+ ///
+ ExplicitCast = 1 << 2,
+
+ ///
+ /// If the source type is a ,
+ /// uses a a static visible method named `Parse` on the target type
+ /// with a return type equal to the target type and a string as single parameter.
+ ///
+ ParseMethod = 1 << 3,
+
+ ///
+ /// If the target type is a ,
+ /// uses the `ToString` method on the source type.
+ ///
+ ToStringMethod = 1 << 4,
+
+ ///
+ /// If the target is an
+ /// and the source is a ,
+ /// parses the string to match the name of an enum member.
+ ///
+ StringToEnum = 1 << 5,
+
+ ///
+ /// If the source is an
+ /// and the target is a ,
+ /// uses the name of the enum member to convert it to a string.
+ ///
+ EnumToString = 1 << 6,
+
+ ///
+ /// If the source is an
+ /// and the target is another ,
+ /// map it according to the .
+ ///
+ EnumToEnum = 1 << 7,
+
+ ///
+ /// Enables all supported conversions.
+ ///
+ All = ~None,
+}
diff --git a/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt b/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
index a44fd2935d..67cebe031c 100644
--- a/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
+++ b/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
@@ -9,6 +9,8 @@ Riok.Mapperly.Abstractions.MapEnumAttribute.IgnoreCase.set -> void
Riok.Mapperly.Abstractions.MapEnumAttribute.MapEnumAttribute(Riok.Mapperly.Abstractions.EnumMappingStrategy strategy) -> void
Riok.Mapperly.Abstractions.MapEnumAttribute.Strategy.get -> Riok.Mapperly.Abstractions.EnumMappingStrategy
Riok.Mapperly.Abstractions.MapperAttribute
+Riok.Mapperly.Abstractions.MapperAttribute.EnabledConversions.get -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MapperAttribute.EnabledConversions.set -> void
Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingIgnoreCase.get -> bool
Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingIgnoreCase.set -> void
Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingStrategy.get -> Riok.Mapperly.Abstractions.EnumMappingStrategy
@@ -45,3 +47,14 @@ Riok.Mapperly.Abstractions.ObjectFactoryAttribute.ObjectFactoryAttribute() -> vo
Riok.Mapperly.Abstractions.PropertyNameMappingStrategy
Riok.Mapperly.Abstractions.PropertyNameMappingStrategy.CaseInsensitive = 1 -> Riok.Mapperly.Abstractions.PropertyNameMappingStrategy
Riok.Mapperly.Abstractions.PropertyNameMappingStrategy.CaseSensitive = 0 -> Riok.Mapperly.Abstractions.PropertyNameMappingStrategy
+Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.All = -1 -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.Constructor = 1 -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.EnumToEnum = 128 -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.EnumToString = 64 -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.ExplicitCast = 4 -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.ImplicitCast = 2 -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.None = 0 -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.ParseMethod = 8 -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.StringToEnum = 32 -> Riok.Mapperly.Abstractions.MappingConversionType
+Riok.Mapperly.Abstractions.MappingConversionType.ToStringMethod = 16 -> Riok.Mapperly.Abstractions.MappingConversionType
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/CtorMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/CtorMappingBuilder.cs
index 98b7944341..46c57e64f0 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/CtorMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/CtorMappingBuilder.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
+using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Helpers;
@@ -8,6 +9,9 @@ public static class CtorMappingBuilder
{
public static CtorMapping? TryBuildMapping(MappingBuilderContext ctx)
{
+ if (!ctx.IsConversionEnabled(MappingConversionType.Constructor))
+ return null;
+
if (ctx.Target is not INamedTypeSymbol namedTarget)
return null;
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/EnumMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/EnumMappingBuilder.cs
index 43f492753a..b7da424694 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/EnumMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/EnumMappingBuilder.cs
@@ -20,7 +20,8 @@ public static class EnumMappingBuilder
// one is an enum, other may be an underlying type (eg. int)
if (!sourceIsEnum || !targetIsEnum)
{
- return ctx.FindOrBuildMapping(sourceEnumType ?? ctx.Source, targetEnumType ?? ctx.Target) is { } delegateMapping
+ return ctx.IsConversionEnabled(MappingConversionType.ExplicitCast)
+ && ctx.FindOrBuildMapping(sourceEnumType ?? ctx.Source, targetEnumType ?? ctx.Target) is { } delegateMapping
? new CastMapping(ctx.Source, ctx.Target, delegateMapping)
: null;
}
@@ -29,6 +30,9 @@ public static class EnumMappingBuilder
if (SymbolEqualityComparer.IncludeNullability.Equals(ctx.Source, ctx.Target))
return new DirectAssignmentMapping(ctx.Source);
+ if (!ctx.IsConversionEnabled(MappingConversionType.EnumToEnum))
+ return null;
+
// map enums by strategy
var config = ctx.GetConfigurationOrDefault();
return config.Strategy switch
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/EnumToStringMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/EnumToStringMappingBuilder.cs
index 125ca04ae8..4c4b95847b 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/EnumToStringMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/EnumToStringMappingBuilder.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
+using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Helpers;
@@ -8,6 +9,9 @@ public static class EnumToStringMappingBuilder
{
public static TypeMapping? TryBuildMapping(MappingBuilderContext ctx)
{
+ if (!ctx.IsConversionEnabled(MappingConversionType.EnumToString))
+ return null;
+
if (ctx.Target.SpecialType != SpecialType.System_String || !ctx.Source.IsEnum())
return null;
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/ExplicitCastMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/ExplicitCastMappingBuilder.cs
index cfba194c83..d95df3f0d9 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/ExplicitCastMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/ExplicitCastMappingBuilder.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis.CSharp;
+using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Helpers;
@@ -8,6 +9,9 @@ public static class ExplicitCastMappingBuilder
{
public static CastMapping? TryBuildMapping(MappingBuilderContext ctx)
{
+ if (!ctx.IsConversionEnabled(MappingConversionType.ExplicitCast))
+ return null;
+
if (ctx.MapperConfiguration.UseDeepCloning && !ctx.Source.IsImmutable() && !ctx.Target.IsImmutable())
return null;
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/ImplicitCastMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/ImplicitCastMappingBuilder.cs
index c80547ac09..e4d3246331 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/ImplicitCastMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/ImplicitCastMappingBuilder.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis.CSharp;
+using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Helpers;
@@ -8,6 +9,9 @@ public static class ImplicitCastMappingBuilder
{
public static CastMapping? TryBuildMapping(MappingBuilderContext ctx)
{
+ if (!ctx.IsConversionEnabled(MappingConversionType.ImplicitCast))
+ return null;
+
if (ctx.MapperConfiguration.UseDeepCloning && !ctx.Source.IsImmutable() && !ctx.Target.IsImmutable())
return null;
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/NewInstanceObjectPropertyMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/NewInstanceObjectPropertyMappingBuilder.cs
index 9692769a0b..2f64e5cb1f 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/NewInstanceObjectPropertyMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/NewInstanceObjectPropertyMappingBuilder.cs
@@ -21,6 +21,9 @@ public static class NewInstanceObjectPropertyMappingBuilder
if (ctx.Target is not INamedTypeSymbol namedTarget || namedTarget.Constructors.All(x => !x.IsAccessible()))
return null;
+ if (ctx.Source.IsEnum() || ctx.Target.IsEnum())
+ return null;
+
return new NewInstanceObjectPropertyMapping(ctx.Source, ctx.Target.NonNullable());
}
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/ParseMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/ParseMappingBuilder.cs
index a64cfe38f5..7b18069851 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/ParseMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/ParseMappingBuilder.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
+using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Helpers;
@@ -10,6 +11,9 @@ public static class ParseMappingBuilder
public static StaticMethodMapping? TryBuildMapping(MappingBuilderContext ctx)
{
+ if (!ctx.IsConversionEnabled(MappingConversionType.ParseMethod))
+ return null;
+
if (ctx.Source.SpecialType != SpecialType.System_String)
return null;
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/SpecialTypeMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/SpecialTypeMappingBuilder.cs
index 89faa38c9c..aad35e5455 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/SpecialTypeMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/SpecialTypeMappingBuilder.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
+using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
namespace Riok.Mapperly.Descriptors.MappingBuilder;
@@ -7,6 +8,9 @@ public static class SpecialTypeMappingBuilder
{
public static TypeMapping? TryBuildMapping(MappingBuilderContext ctx)
{
+ if (!ctx.IsConversionEnabled(MappingConversionType.ExplicitCast))
+ return null;
+
return ctx.Target.SpecialType switch
{
SpecialType.System_Object when ctx.MapperConfiguration.UseDeepCloning
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/StringToEnumMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/StringToEnumMappingBuilder.cs
index 9c0769fb43..b1a1147b8f 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/StringToEnumMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/StringToEnumMappingBuilder.cs
@@ -9,6 +9,9 @@ public static class StringToEnumMappingBuilder
{
public static TypeMapping? TryBuildMapping(MappingBuilderContext ctx)
{
+ if (!ctx.IsConversionEnabled(MappingConversionType.StringToEnum))
+ return null;
+
if (ctx.Source.SpecialType != SpecialType.System_String || !ctx.Target.IsEnum())
return null;
diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilder/ToStringMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilder/ToStringMappingBuilder.cs
index ef6f421dcd..7683c1ea7c 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBuilder/ToStringMappingBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBuilder/ToStringMappingBuilder.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
+using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
namespace Riok.Mapperly.Descriptors.MappingBuilder;
@@ -8,6 +9,9 @@ public static class ToStringMappingBuilder
public static TypeMapping? TryBuildMapping(MappingBuilderContext ctx)
{
+ if (!ctx.IsConversionEnabled(MappingConversionType.ToStringMethod))
+ return null;
+
return ctx.Target.SpecialType == SpecialType.System_String
? new SourceObjectMethodMapping(ctx.Source, ctx.Target, nameof(ToString))
: null;
diff --git a/src/Riok.Mapperly/Descriptors/SimpleMappingBuilderContext.cs b/src/Riok.Mapperly/Descriptors/SimpleMappingBuilderContext.cs
index 44bc11ad82..34f7044e17 100644
--- a/src/Riok.Mapperly/Descriptors/SimpleMappingBuilderContext.cs
+++ b/src/Riok.Mapperly/Descriptors/SimpleMappingBuilderContext.cs
@@ -19,6 +19,9 @@ public SimpleMappingBuilderContext(DescriptorBuilder builder)
public MapperAttribute MapperConfiguration => _builder.MapperConfiguration;
+ public bool IsConversionEnabled(MappingConversionType conversionType)
+ => MapperConfiguration.EnabledConversions.HasFlag(conversionType);
+
public INamedTypeSymbol GetTypeSymbol(Type type)
=> Compilation.GetTypeByMetadataName(type.FullName ?? throw new InvalidOperationException("Could not get name of type " + type))
?? throw new InvalidOperationException("Could not get type " + type.FullName);
diff --git a/test/Riok.Mapperly.Tests/Mapping/CastTest.cs b/test/Riok.Mapperly.Tests/Mapping/CastTest.cs
index 79f6e35088..e29e411630 100644
--- a/test/Riok.Mapperly.Tests/Mapping/CastTest.cs
+++ b/test/Riok.Mapperly.Tests/Mapping/CastTest.cs
@@ -1,3 +1,6 @@
+using Riok.Mapperly.Abstractions;
+using Riok.Mapperly.Diagnostics;
+
namespace Riok.Mapperly.Tests.Mapping;
public class CastTest
@@ -344,4 +347,28 @@ public void OperatorImplicitStructWithMutableStructTargetDeepCloning()
.HaveSingleMethodBody(@"var target = new B();
return target;");
}
+
+ [Fact]
+ public void ImplicitCastMappingDisabledShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "byte",
+ "int",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.ImplicitCast));
+ TestHelper.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
+ .Should()
+ .HaveDiagnostic(new(DiagnosticDescriptors.CouldNotCreateMapping));
+ }
+
+ [Fact]
+ public void ExplicitCastMappingDisabledShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "int",
+ "byte",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.ExplicitCast));
+ TestHelper.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
+ .Should()
+ .HaveDiagnostic(new(DiagnosticDescriptors.CouldNotCreateMapping));
+ }
}
diff --git a/test/Riok.Mapperly.Tests/Mapping/CtorTest.cs b/test/Riok.Mapperly.Tests/Mapping/CtorTest.cs
index 39be0b26e0..abdb9e2f3d 100644
--- a/test/Riok.Mapperly.Tests/Mapping/CtorTest.cs
+++ b/test/Riok.Mapperly.Tests/Mapping/CtorTest.cs
@@ -1,3 +1,6 @@
+using Riok.Mapperly.Abstractions;
+using Riok.Mapperly.Diagnostics;
+
namespace Riok.Mapperly.Tests.Mapping;
public class CtorTest
@@ -25,4 +28,17 @@ public void CtorCustomStruct()
.Should()
.HaveSingleMethodBody("return new A(source);");
}
+
+ [Fact]
+ public void CtorMappingDisabledShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "A",
+ "string",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.ToStringMethod),
+ "class A { public A(string x) {} }");
+ TestHelper.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
+ .Should()
+ .HaveDiagnostic(new(DiagnosticDescriptors.CouldNotCreateMapping));
+ }
}
diff --git a/test/Riok.Mapperly.Tests/Mapping/EnumTest.cs b/test/Riok.Mapperly.Tests/Mapping/EnumTest.cs
index b3ae60b0fd..09541034ee 100644
--- a/test/Riok.Mapperly.Tests/Mapping/EnumTest.cs
+++ b/test/Riok.Mapperly.Tests/Mapping/EnumTest.cs
@@ -1,3 +1,6 @@
+using Riok.Mapperly.Abstractions;
+using Riok.Mapperly.Diagnostics;
+
namespace Riok.Mapperly.Tests.Mapping;
[UsesVerify]
@@ -244,4 +247,82 @@ public void StringToEnumShouldSwitch()
_ => (E1)System.Enum.Parse(typeof(E1), source, false),
};");
}
+
+ [Fact]
+ public void EnumToEnumMappingAndExplicitCastMappingDisabledShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "E2",
+ "E1",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.EnumToEnum, MappingConversionType.ExplicitCast),
+ "enum E1 {A, B, C}",
+ "enum E2 {A, B, C}");
+ TestHelper.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
+ .Should()
+ .HaveDiagnostic(new(DiagnosticDescriptors.CouldNotCreateMapping));
+ }
+
+ [Fact]
+ public void EnumToEnumMappingDisabledShouldUseExplicitCastMapping()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "E2",
+ "E1",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.EnumToEnum),
+ "enum E1 {A, B, C}",
+ "enum E2 {A, B, C}");
+ TestHelper.GenerateMapper(source)
+ .Should()
+ .HaveSingleMethodBody("return (E1)source;");
+ }
+
+ [Fact]
+ public void StringToEnumMappingAndParseMappingDisabledShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ "[MapEnum(EnumMappingStrategy.ByName)] partial E1 ToE1(string source);",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.StringToEnum, MappingConversionType.ParseMethod),
+ "enum E1 {A, B, C}");
+ TestHelper.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
+ .Should()
+ .HaveDiagnostic(new(DiagnosticDescriptors.CouldNotCreateMapping));
+ }
+
+ [Fact]
+ public void StringToEnumMappingDisabledShouldUseParseMethodMapping()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ "[MapEnum(EnumMappingStrategy.ByName)] partial E1 ToE1(string source);",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.StringToEnum),
+ "enum E1 {A, B, C}");
+ TestHelper.GenerateMapper(source)
+ .Should()
+ .HaveSingleMethodBody("return (E1)int.Parse(source);");
+ }
+
+ [Fact]
+ public void EnumToStringMappingAndToStringMethodMappingDisabledShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "E1",
+ "string",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.EnumToString, MappingConversionType.ToStringMethod),
+ "enum E1 {A, B, C}");
+ TestHelper.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
+ .Should()
+ .HaveDiagnostic(new(DiagnosticDescriptors.CouldNotCreateMapping));
+ }
+
+ [Fact]
+ public void EnumToStringMappingDisabledShouldUseToStringMapping()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "E1",
+ "string",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.EnumToString),
+ "enum E1 {A, B, C}");
+ TestHelper.GenerateMapper(source)
+ .Should()
+ .HaveSingleMethodBody("return (string)source.ToString();");
+ }
}
diff --git a/test/Riok.Mapperly.Tests/Mapping/ParseTest.cs b/test/Riok.Mapperly.Tests/Mapping/ParseTest.cs
index 7d74a45add..56467ceb04 100644
--- a/test/Riok.Mapperly.Tests/Mapping/ParseTest.cs
+++ b/test/Riok.Mapperly.Tests/Mapping/ParseTest.cs
@@ -1,3 +1,6 @@
+using Riok.Mapperly.Abstractions;
+using Riok.Mapperly.Diagnostics;
+
namespace Riok.Mapperly.Tests.Mapping;
public class ParseTest
@@ -52,4 +55,17 @@ public void ParseableCustomClass()
.Should()
.HaveSingleMethodBody("return A.Parse(source);");
}
+
+ [Fact]
+ public void ParseMappingDisabledShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "string",
+ "DateTime",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.ParseMethod),
+ "class A { public A(string x) {} }");
+ TestHelper.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
+ .Should()
+ .HaveDiagnostic(new(DiagnosticDescriptors.CouldNotCreateMapping));
+ }
}
diff --git a/test/Riok.Mapperly.Tests/Mapping/ToStringTest.cs b/test/Riok.Mapperly.Tests/Mapping/ToStringTest.cs
index 0b4b708be0..40f80b2c63 100644
--- a/test/Riok.Mapperly.Tests/Mapping/ToStringTest.cs
+++ b/test/Riok.Mapperly.Tests/Mapping/ToStringTest.cs
@@ -1,3 +1,6 @@
+using Riok.Mapperly.Abstractions;
+using Riok.Mapperly.Diagnostics;
+
namespace Riok.Mapperly.Tests.Mapping;
public class ToStringTest
@@ -47,4 +50,17 @@ public void ObjectToString()
.Should()
.HaveSingleMethodBody("return source.ToString();");
}
+
+ [Fact]
+ public void ToStringMappingDisabledShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "A",
+ "string",
+ TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.ToStringMethod),
+ "class A {}");
+ TestHelper.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
+ .Should()
+ .HaveDiagnostic(new(DiagnosticDescriptors.CouldNotCreateMapping));
+ }
}
diff --git a/test/Riok.Mapperly.Tests/TestHelperOptions.cs b/test/Riok.Mapperly.Tests/TestHelperOptions.cs
index a335269975..f047a2591f 100644
--- a/test/Riok.Mapperly.Tests/TestHelperOptions.cs
+++ b/test/Riok.Mapperly.Tests/TestHelperOptions.cs
@@ -8,21 +8,21 @@ public record TestHelperOptions(
LanguageVersion LanguageVersion = LanguageVersion.Default,
IReadOnlySet? AllowedDiagnostics = null)
{
- public static readonly TestHelperOptions Default = new();
+ public static readonly TestHelperOptions AllowDiagnostics = new();
- public static readonly TestHelperOptions DisabledNullable = Default with { NullableOption = NullableContextOptions.Disable };
+ public static readonly TestHelperOptions DisabledNullable = AllowDiagnostics with { NullableOption = NullableContextOptions.Disable };
- public static readonly TestHelperOptions NoDiagnostics = Default with
+ public static readonly TestHelperOptions NoDiagnostics = AllowDiagnostics with
{
AllowedDiagnostics = new HashSet(),
};
- public static readonly TestHelperOptions AllowAllDiagnostics = Default with
+ public static readonly TestHelperOptions AllowAllDiagnostics = AllowDiagnostics with
{
AllowedDiagnostics = Enum.GetValues().ToHashSet(),
};
- public static readonly TestHelperOptions AllowInfoDiagnostics = Default with
+ public static readonly TestHelperOptions AllowInfoDiagnostics = AllowDiagnostics with
{
AllowedDiagnostics = new HashSet
{
diff --git a/test/Riok.Mapperly.Tests/TestSourceBuilder.cs b/test/Riok.Mapperly.Tests/TestSourceBuilder.cs
index 68885ff43a..9ac04a13de 100644
--- a/test/Riok.Mapperly.Tests/TestSourceBuilder.cs
+++ b/test/Riok.Mapperly.Tests/TestSourceBuilder.cs
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
-using Riok.Mapperly.Abstractions;
namespace Riok.Mapperly.Tests;
@@ -39,27 +38,31 @@ public partial class Mapper
private static string BuildAttribute(TestSourceBuilderOptions options)
{
- var attrs = new List
+ var attrs = new[]
{
Attribute(options.UseDeepCloning),
Attribute(options.ThrowOnMappingNullMismatch),
- Attribute(options.ThrowOnPropertyMappingNullMismatch)
+ Attribute(options.ThrowOnPropertyMappingNullMismatch),
+ Attribute(options.EnabledConversions),
+ Attribute(options.PropertyNameMappingStrategy),
};
- if (options.PropertyNameMappingStrategy != PropertyNameMappingStrategy.CaseSensitive)
- {
- attrs.Add($"{nameof(MapperAttribute.PropertyNameMappingStrategy)} = {nameof(PropertyNameMappingStrategy)}.{options.PropertyNameMappingStrategy}");
- }
-
return $"[Mapper({string.Join(", ", attrs)})]";
}
+ private static string Attribute(T value, [CallerArgumentExpression("value")] string? expression = null)
+ where T : Enum
+ => Attribute(Convert.ChangeType(value, Enum.GetUnderlyingType(typeof(T))).ToString() ?? throw new ArgumentNullException(), expression);
+
private static string Attribute(bool value, [CallerArgumentExpression("value")] string? expression = null)
+ => Attribute(value ? "true" : "false", expression);
+
+ private static string Attribute(string value, [CallerArgumentExpression("value")] string? expression = null)
{
if (expression == null)
throw new ArgumentNullException(nameof(expression));
- return $"{expression.Split(".").Last()} = {(value ? "true" : "false")}";
+ return $"{expression.Split(".").Last()} = {value}";
}
public static string MapperWithBodyAndTypes(string body, params string[] types)
diff --git a/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs b/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs
index ec400daebe..e7158d24f4 100644
--- a/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs
+++ b/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs
@@ -7,8 +7,20 @@ public record TestSourceBuilderOptions(
bool UseDeepCloning = false,
bool ThrowOnMappingNullMismatch = true,
bool ThrowOnPropertyMappingNullMismatch = false,
- PropertyNameMappingStrategy PropertyNameMappingStrategy = PropertyNameMappingStrategy.CaseSensitive)
+ PropertyNameMappingStrategy PropertyNameMappingStrategy = PropertyNameMappingStrategy.CaseSensitive,
+ MappingConversionType EnabledConversions = MappingConversionType.All)
{
public static readonly TestSourceBuilderOptions Default = new();
public static readonly TestSourceBuilderOptions WithDeepCloning = new(UseDeepCloning: true);
+ public static TestSourceBuilderOptions WithDisabledMappingConversion(params MappingConversionType[] conversionTypes)
+ {
+ var enabled = MappingConversionType.All;
+
+ foreach (var disabledConversionType in conversionTypes)
+ {
+ enabled &= ~disabledConversionType;
+ }
+
+ return Default with { EnabledConversions = enabled };
+ }
}