From 2d82323a93a3cffb730b1e24132f5311992e6ceb Mon Sep 17 00:00:00 2001 From: TonEnfer Date: Sat, 30 Nov 2024 10:23:17 +0500 Subject: [PATCH] Add support for `From` and `To` method conversions (#1117) --- docs/docs/configuration/conversions.md | 42 +- .../MappingConversionType.cs | 40 +- .../ConvertInstanceMethodMappingBuilder.cs | 55 +++ .../ConvertStaticMethodMappingBuilder.cs | 204 ++++++++++ .../DateTimeToDateOnlyMappingBuilder.cs | 33 -- .../DateTimeToTimeOnlyMappingBuilder.cs | 33 -- .../MappingBuilders/MappingBuilder.cs | 4 +- .../Descriptors/Mappings/ToStringMapping.cs | 4 +- ...ApiTest.PublicApiHasNotChanged.verified.cs | 4 + .../BaseMapperTest.cs | 8 + .../Dto/TestObjectDto.cs | 16 + .../Mapper/TestMapper.cs | 2 + .../Models/ConvertWithStaticMethodObject.cs | 74 ++++ .../Models/TestObject.cs | 17 + .../Riok.Mapperly.IntegrationTests.csproj | 2 +- ...t.RunMappingShouldWork_NET6_0.verified.txt | 8 + ...SnapshotGeneratedSource_NET6_0.verified.cs | 64 +++ ...t.RunMappingShouldWork_NET8_0.verified.txt | 9 + ...SnapshotGeneratedSource_NET8_0.verified.cs | 79 ++++ ...nsionMappingShouldWork_NET6_0.verified.txt | 9 + ...t.RunMappingShouldWork_NET6_0.verified.txt | 9 + ...SnapshotGeneratedSource_NET6_0.verified.cs | 102 +++++ .../Mapping/DateTimeTest.cs | 18 - .../Mapping/StaticMethodConversionTest.cs | 373 ++++++++++++++++++ .../Mapping/ToTargetInstanceTest.cs | 58 +++ 25 files changed, 1152 insertions(+), 115 deletions(-) create mode 100644 src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertInstanceMethodMappingBuilder.cs create mode 100644 src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertStaticMethodMappingBuilder.cs delete mode 100644 src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToDateOnlyMappingBuilder.cs delete mode 100644 src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToTimeOnlyMappingBuilder.cs create mode 100644 test/Riok.Mapperly.IntegrationTests/Models/ConvertWithStaticMethodObject.cs delete mode 100644 test/Riok.Mapperly.Tests/Mapping/DateTimeTest.cs create mode 100644 test/Riok.Mapperly.Tests/Mapping/StaticMethodConversionTest.cs create mode 100644 test/Riok.Mapperly.Tests/Mapping/ToTargetInstanceTest.cs diff --git a/docs/docs/configuration/conversions.md b/docs/docs/configuration/conversions.md index bde8e45d92..efd00a488a 100644 --- a/docs/docs/configuration/conversions.md +++ b/docs/docs/configuration/conversions.md @@ -7,26 +7,28 @@ description: A list of conversions supported by Mapperly Mapperly implements several types of automatic conversions (in order of priority): -| Name | Description | Conditions | -| -------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -| Direct assignment | Directly assigns the source object to the target | Source type is assignable to the target type and `UseDeepCloning` is `false` | -| Queryable | Projects the source queryable to the target queryable | Source and target types are `IQueryable<>` | -| Dictionary | Maps a source dictionary to an enumerable target | Source type is an `IDictionary<,>` or an `IReadOnlyDictionary<,>` | -| Enumerable | Maps an enumerable source to an enumerable target | Source type is an `IEnumerable<>` | -| Span | Maps a `Span<>`, `ReadOnlySpan<>` to or from `Span<>`, `ReadOnlySpan<>` or enumerable | Source or target type is a `Span<>`, `ReadOnlySpan<>` | -| Tuple | Create a new instance of a `ValueTuple` or tuple expression i.e. `(10, 12)` | Target type is a `ValueTuple<>` or tuple expression | -| Memory | Maps a `Memory<>`, `ReadOnlyMemory<>` to or from `Memory<>`, `ReadOnlyMemory<>`, `Span<>`, `ReadOnlySpan<>` or enumerable | Source or target type is a `Memory<>` or `ReadOnlyMemory<>` | -| Implicit cast | Implicit cast operator | An implicit cast operator is defined to cast from the source type to the target type | -| Parse method | Uses a static `Parse` method on the target type | Source type is a `string` and target has a static method with the following signature: `TTarget Parse(string)`. | -| Constructor | Uses a constructor on the target type with the source as single parameter | Target type has a visible constructor with a single parameter of the source type. | -| String to enum | Maps a string to an enum member name | Source type is a `string` and the target type is an enum | -| Enum to string | Maps an enum member name to a string | Source type is an enum and the target type is a `string` | -| Enum to enum | Maps an enum to another enum either by value or by member name | Source and target types are enums | -| DateTime to DateOnly | Maps a `DateTime` to a `DateOnly` | Source type is a `DateTime` and target type is a `DateOnly` | -| DateTime to TimeOnly | Maps a `DateTime` to a `TimeOnly` | Source type is a `DateTime` and target type is a `TimeOnly` | -| Explicit cast | Explicit cast operator | An explicit cast operator is defined to cast from the source type to the target type | -| ToString | `ToString` method of an object | Target type is a `string` | -| New instance | Create a new instance of the target type and map all properties | The target type has a visible constructor or an object factory exists for the target type | +| Name | Description | Conditions | +| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Direct assignment | Directly assigns the source object to the target | Source type is assignable to the target type and `UseDeepCloning` is `false` | +| Queryable | Projects the source queryable to the target queryable | Source and target types are `IQueryable<>` | +| Dictionary | Maps a source dictionary to an enumerable target | Source type is an `IDictionary<,>` or an `IReadOnlyDictionary<,>` | +| Enumerable | Maps an enumerable source to an enumerable target | Source type is an `IEnumerable<>` | +| Span | Maps a `Span<>`, `ReadOnlySpan<>` to or from `Span<>`, `ReadOnlySpan<>` or enumerable | Source or target type is a `Span<>`, `ReadOnlySpan<>` | +| Tuple | Create a new instance of a `ValueTuple` or tuple expression i.e. `(10, 12)` | Target type is a `ValueTuple<>` or tuple expression | +| Memory | Maps a `Memory<>`, `ReadOnlyMemory<>` to or from `Memory<>`, `ReadOnlyMemory<>`, `Span<>`, `ReadOnlySpan<>` or enumerable | Source or target type is a `Memory<>` or `ReadOnlyMemory<>` | +| Implicit cast | Implicit cast operator | An implicit cast operator is defined to cast from the source type to the target type | +| Parse method | Uses a static `Parse` method on the target type | Source type is a `string` and target has a static method with the following signature: `TTarget Parse(string)`. | +| Constructor | Uses a constructor on the target type with the source as single parameter | Target type has a visible constructor with a single parameter of the source type. | +| String to enum | Maps a string to an enum member name | Source type is a `string` and the target type is an enum | +| Enum to string | Maps an enum member name to a string | Source type is an enum and the target type is a `string` | +| Enum to enum | Maps an enum to another enum either by value or by member name | Source and target types are enums | +| Explicit cast | Explicit cast operator | An explicit cast operator is defined to cast from the source type to the target type | +| ToString | `ToString` method of an object | Target type is a `string` | +| ToTarget | `ToTTarget` method of an object, excluded `ToString` | Source object contains method with signature `TTarget ToTTarget()` | +| DateTime to DateOnly | Maps a `DateTime` to a `DateOnly` | Source type is a `DateTime` and target type is a `DateOnly` | +| DateTime to TimeOnly | Maps a `DateTime` to a `TimeOnly` | Source type is a `DateTime` and target type is a `TimeOnly` | +| Static method | Convert source uses source type static `ToTTarget` method or target type static `Create`, `CreateFrom`, `FromTSource` except for converting `DateTime` to `DateOnly` or `TimeOnly` | The source type contains a static method with signature `TTarget ToTTarget()` or the target type contains one of the following static methods `TTarget Create(TSource)`, `TTarget CreateFrom (TSource)`, `TTarget FromTSource(TSource)` | +| New instance | Create a new instance of the target type and map all properties | The target type has a visible constructor or an object factory exists for the target type | ## Disable all automatic conversions diff --git a/src/Riok.Mapperly.Abstractions/MappingConversionType.cs b/src/Riok.Mapperly.Abstractions/MappingConversionType.cs index 580245d6dd..d4d0da28a1 100644 --- a/src/Riok.Mapperly.Abstractions/MappingConversionType.cs +++ b/src/Riok.Mapperly.Abstractions/MappingConversionType.cs @@ -30,7 +30,7 @@ public enum MappingConversionType /// /// If the source type is a , - /// uses a a static visible method named `Parse` on the target type + /// uses 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, @@ -64,26 +64,26 @@ public enum MappingConversionType /// /// If the source is a - /// and the target is a DateOnly + /// and the target is a DateOnly /// uses the `FromDateTime` method on the target type with the source as single parameter. /// DateTimeToDateOnly = 1 << 8, /// /// If the source is a - /// and the target is a TimeOnly + /// and the target is a TimeOnly /// uses the `FromDateTime` method on the target type with the source as single parameter. /// DateTimeToTimeOnly = 1 << 9, /// - /// If the source and the target is a . + /// If the source and the target are a . /// Only uses object initializers and inlines the mapping code. /// Queryable = 1 << 10, /// - /// If the source and the target is an + /// If the source and the target are an /// Maps each element individually. /// Enumerable = 1 << 11, @@ -115,10 +115,38 @@ public enum MappingConversionType Tuple = 1 << 15, /// - /// Allow using the underlying type of an enum to map from or to an enum type. + /// Allow using the underlying type of enum to map from or to an enum type. /// EnumUnderlyingType = 1 << 16, + /// + /// If the source type contains a `ToTarget` method other than `ToString`, use it + /// + ToTargetMethod = 1 << 17, + + /// + /// Combination of and + /// + AllToTargetMethods = ToStringMethod | ToTargetMethod, + + /// + /// If the source type contains a static `ToTarget` method + /// or the target type contains a static methods + /// `Create(TSource)`, + /// `CreateFrom(TSource)` + /// `From(TSource)` + /// `FromTSource(TSource)` + /// or similar methods with params keyword, use it. + /// The exception is conversions, + /// which are enabled by separate options (, ). + /// + StaticConvertMethods = 1 << 18, + + /// + /// Combination of , and + /// + AllStaticMethods = DateTimeToDateOnly | DateTimeToTimeOnly | StaticConvertMethods, + /// /// Enables all supported conversions. /// diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertInstanceMethodMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertInstanceMethodMappingBuilder.cs new file mode 100644 index 0000000000..39d0683b22 --- /dev/null +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertInstanceMethodMappingBuilder.cs @@ -0,0 +1,55 @@ +using Microsoft.CodeAnalysis; +using Riok.Mapperly.Abstractions; +using Riok.Mapperly.Descriptors.Mappings; +using Riok.Mapperly.Helpers; + +namespace Riok.Mapperly.Descriptors.MappingBuilders; + +public static class ConvertInstanceMethodMappingBuilder +{ + public static SourceObjectMethodMapping? TryBuildMapping(MappingBuilderContext ctx) + { + if (!ctx.IsConversionEnabled(MappingConversionType.ToTargetMethod)) + return null; + + var targetIsNullable = ctx.Target.NonNullable(out var nonNullableTarget); + + // ignore `ToString` mapping for backward compatibility + if (nonNullableTarget.SpecialType == SpecialType.System_String) + return null; + + var methodName = GetMappingMethodName(ctx); + + var methodCandidates = ctx + .SymbolAccessor.GetAllMethods(ctx.Source, methodName) + .Where(x => x is { IsStatic: false, ReturnsVoid: false, IsAsync: false, Parameters.Length: 0 }) + .ToArray(); + + // try to find method with equal nullability return type + var method = methodCandidates.FirstOrDefault(x => SymbolEqualityComparer.IncludeNullability.Equals(x.ReturnType, ctx.Target)); + if (method is not null) + return new SourceObjectMethodMapping(ctx.Source, ctx.Target, method.Name); + + if (!targetIsNullable) + return null; + + // otherwise try to find method ignoring the nullability + method = methodCandidates.FirstOrDefault(x => SymbolEqualityComparer.Default.Equals(x.ReturnType, nonNullableTarget)); + + return method is null ? null : new SourceObjectMethodMapping(ctx.Source, ctx.Target, method.Name); + } + + private static string GetMappingMethodName(MappingBuilderContext ctx) + { + var nonNullableTarget = ctx.Target.NonNullable(); + + if (!nonNullableTarget.IsArrayType(out var arrayType)) + { + return $"To{nonNullableTarget.Name}"; + } + + var nonNullableElementType = arrayType.ElementType.NonNullable(); + + return $"To{nonNullableElementType.Name}Array"; + } +} diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertStaticMethodMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertStaticMethodMappingBuilder.cs new file mode 100644 index 0000000000..04e60d5804 --- /dev/null +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertStaticMethodMappingBuilder.cs @@ -0,0 +1,204 @@ +using Microsoft.CodeAnalysis; +using Riok.Mapperly.Abstractions; +using Riok.Mapperly.Descriptors.Mappings; +using Riok.Mapperly.Helpers; + +namespace Riok.Mapperly.Descriptors.MappingBuilders; + +public static class ConvertStaticMethodMappingBuilder +{ + public static StaticMethodMapping? TryBuildMapping(MappingBuilderContext ctx) + { + //checks for backward compatibility + + if (IsDateTimeToDateOnlyConversion(ctx)) + { + if (!ctx.IsConversionEnabled(MappingConversionType.DateTimeToDateOnly)) + return null; + } + else if (IsDateTimeToTimeOnlyConversion(ctx)) + { + if (!ctx.IsConversionEnabled(MappingConversionType.DateTimeToTimeOnly)) + return null; + } + else + { + if (!ctx.IsConversionEnabled(MappingConversionType.StaticConvertMethods)) + return null; + } + + var targetIsNullable = ctx.Target.NonNullable(out var nonNullableTarget); + + var allTargetMethods = ctx.SymbolAccessor.GetAllMethods(nonNullableTarget).ToArray(); + + var isTargetMapping = TryGetStaticMethodMapping( + allTargetMethods, + GetTargetStaticMethodNames(ctx), + ctx.Source, + ctx.Target, + nonNullableTarget, + targetIsNullable, + out var mapping + ); + + if (isTargetMapping) + { + return mapping; + } + + var allSourceMethods = ctx.SymbolAccessor.GetAllMethods(ctx.Source); + + if (ctx.Source.IsArrayType(out var arrayTypeSymbol)) + { + allSourceMethods = allSourceMethods.Concat(ctx.SymbolAccessor.GetAllMethods(arrayTypeSymbol.ElementType)); + } + else + { + if (ctx.Source is INamedTypeSymbol { TypeArguments.Length: 1 } namedTypeSymbol) + { + allSourceMethods = allTargetMethods.Concat(ctx.SymbolAccessor.GetAllMethods(namedTypeSymbol.TypeArguments[0])); + } + } + + return TryGetStaticMethodMapping( + allSourceMethods.ToArray(), + GetSourceStaticMethodNames(ctx), + ctx.Source, + ctx.Target, + nonNullableTarget, + targetIsNullable, + out mapping + ) + ? mapping + : null; + } + + private static bool IsDateTimeToDateOnlyConversion(MappingBuilderContext ctx) + { + return ctx.Source.SpecialType == SpecialType.System_DateTime + && ctx.Types.DateOnly != null + && ctx.Target is INamedTypeSymbol namedSymbol + && SymbolEqualityComparer.Default.Equals(namedSymbol, ctx.Types.DateOnly) + || true; + } + + private static bool IsDateTimeToTimeOnlyConversion(MappingBuilderContext ctx) + { + return ctx.Source.SpecialType == SpecialType.System_DateTime + && ctx.Types.TimeOnly != null + && ctx.Target is INamedTypeSymbol namedSymbol + && SymbolEqualityComparer.Default.Equals(namedSymbol, ctx.Types.TimeOnly) + || true; + } + + private static bool TryGetStaticMethodMapping( + ICollection allMethods, + IEnumerable methodNames, + ITypeSymbol sourceType, + ITypeSymbol targetType, + ITypeSymbol nonNullableTargetType, + bool targetIsNullable, + out StaticMethodMapping? mapping + ) + { + foreach (var methodName in methodNames) + { + var candidates = GetMethodCandidates(allMethods, methodName, sourceType); + + // try to find method with equal nullability return type + var method = candidates.FirstOrDefault(x => SymbolEqualityComparer.IncludeNullability.Equals(x.ReturnType, targetType)); + + if (method != null) + { + mapping = new StaticMethodMapping(method); + return true; + } + + if (!targetIsNullable) + continue; + + // otherwise try to find method ignoring the nullability + method = candidates.FirstOrDefault(x => SymbolEqualityComparer.Default.Equals(x.ReturnType, nonNullableTargetType)); + + if (method == null) + { + continue; + } + + mapping = new StaticMethodMapping(method); + return true; + } + + mapping = null; + return false; + } + + private static IMethodSymbol[] GetMethodCandidates(ICollection allMethods, string methodName, ITypeSymbol parameterType) + { + return allMethods + .Where(m => + string.Equals(m.Name, methodName, StringComparison.Ordinal) + && m is { IsStatic: true, ReturnsVoid: false, IsAsync: false, Parameters.Length: 1 } + && FilterParameterType(m.Parameters[0], parameterType) + ) + .ToArray(); + } + + private static bool FilterParameterType(IParameterSymbol parameter, ITypeSymbol targetType) + { + if (SymbolEqualityComparer.Default.Equals(parameter.Type, targetType)) + return true; + + if (!parameter.IsParams) + return false; + + var targetIsArray = targetType.IsArrayType(out var targetArrayTypeSymbol); + + var targetIsEnumerable = targetType.AllInterfaces.Any(x => + x.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T + ); + + if (parameter.Type.IsArrayType(out var arrayTypeSymbol)) + { + return SymbolEqualityComparer.Default.Equals( + arrayTypeSymbol.ElementType, + targetIsArray ? targetArrayTypeSymbol!.ElementType : targetType + ); + } + + if (!parameter.IsParamsCollection) + return false; + + return targetIsEnumerable + ? parameter.Type.AllInterfaces.Intersect(targetType.AllInterfaces, SymbolEqualityComparer.Default).Any() + : SymbolEqualityComparer.Default.Equals(((INamedTypeSymbol)parameter.Type)?.TypeArguments[0], targetType); + } + + private static IEnumerable GetTargetStaticMethodNames(MappingBuilderContext ctx) + { + const string create = "Create"; + const string from = "From"; + + yield return create; + yield return $"{create}{from}"; + + if (!ctx.Source.IsArrayType(out var arrayType)) + yield return $"{from}{ctx.Source.Name}"; + else + yield return $"{from}{arrayType.ElementType.Name}Array"; + } + + private static IEnumerable GetSourceStaticMethodNames(MappingBuilderContext ctx) + { + var nonNullableTarget = ctx.Target.NonNullable(); + + yield return $"To{nonNullableTarget.Name}"; + + if (!nonNullableTarget.IsArrayType(out var arrayTypeSymbol)) + yield break; + + var nonNullableElementType = arrayTypeSymbol.ElementType.NonNullable(); + + yield return $"To{nonNullableElementType.Name}Array"; + } +} diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToDateOnlyMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToDateOnlyMappingBuilder.cs deleted file mode 100644 index bbe44462f6..0000000000 --- a/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToDateOnlyMappingBuilder.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.CodeAnalysis; -using Riok.Mapperly.Abstractions; -using Riok.Mapperly.Descriptors.Mappings; - -namespace Riok.Mapperly.Descriptors.MappingBuilders; - -public static class DateTimeToDateOnlyMappingBuilder -{ - private const string FromDateTimeMethodName = "FromDateTime"; - - public static StaticMethodMapping? TryBuildMapping(MappingBuilderContext ctx) - { - if (!ctx.IsConversionEnabled(MappingConversionType.DateTimeToDateOnly) || ctx.Types.DateOnly == null) - return null; - - if (ctx.Source.SpecialType != SpecialType.System_DateTime) - return null; - - if (ctx.Target is not INamedTypeSymbol namedSymbol || !SymbolEqualityComparer.Default.Equals(namedSymbol, ctx.Types.DateOnly)) - return null; - - var fromDateTimeMethod = ResolveFromDateTimeMethod(ctx); - if (fromDateTimeMethod is null) - return null; - - return new StaticMethodMapping(fromDateTimeMethod); - } - - private static IMethodSymbol? ResolveFromDateTimeMethod(MappingBuilderContext ctx) - { - return ctx.Types.DateOnly?.GetMembers(FromDateTimeMethodName).OfType().FirstOrDefault(m => m.IsStatic); - } -} diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToTimeOnlyMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToTimeOnlyMappingBuilder.cs deleted file mode 100644 index 686532d96d..0000000000 --- a/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToTimeOnlyMappingBuilder.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.CodeAnalysis; -using Riok.Mapperly.Abstractions; -using Riok.Mapperly.Descriptors.Mappings; - -namespace Riok.Mapperly.Descriptors.MappingBuilders; - -public static class DateTimeToTimeOnlyMappingBuilder -{ - private const string FromDateTimeMethodName = "FromDateTime"; - - public static StaticMethodMapping? TryBuildMapping(MappingBuilderContext ctx) - { - if (!ctx.IsConversionEnabled(MappingConversionType.DateTimeToTimeOnly) || ctx.Types.TimeOnly == null) - return null; - - if (ctx.Source.SpecialType != SpecialType.System_DateTime) - return null; - - if (ctx.Target is not INamedTypeSymbol namedSymbol || !SymbolEqualityComparer.Default.Equals(namedSymbol, ctx.Types.TimeOnly)) - return null; - - var fromDateTimeMethod = ResolveFromDateTimeMethod(ctx); - if (fromDateTimeMethod is null) - return null; - - return new StaticMethodMapping(fromDateTimeMethod); - } - - private static IMethodSymbol? ResolveFromDateTimeMethod(MappingBuilderContext ctx) - { - return ctx.Types.TimeOnly?.GetMembers(FromDateTimeMethodName).OfType().FirstOrDefault(m => m.IsStatic); - } -} diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilders/MappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/MappingBuilder.cs index 1045ccc1d8..4ad5704c1a 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBuilders/MappingBuilder.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/MappingBuilder.cs @@ -27,10 +27,10 @@ public class MappingBuilder(MappingCollection mappings, MapperDeclaration mapper StringToEnumMappingBuilder.TryBuildMapping, EnumToStringMappingBuilder.TryBuildMapping, EnumToEnumMappingBuilder.TryBuildMapping, - DateTimeToDateOnlyMappingBuilder.TryBuildMapping, - DateTimeToTimeOnlyMappingBuilder.TryBuildMapping, ExplicitCastMappingBuilder.TryBuildMapping, ToStringMappingBuilder.TryBuildMapping, + ConvertInstanceMethodMappingBuilder.TryBuildMapping, + ConvertStaticMethodMappingBuilder.TryBuildMapping, NewInstanceObjectMemberMappingBuilder.TryBuildMapping, ]; diff --git a/src/Riok.Mapperly/Descriptors/Mappings/ToStringMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/ToStringMapping.cs index 0b57982a69..13c24bdf94 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/ToStringMapping.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/ToStringMapping.cs @@ -12,8 +12,8 @@ namespace Riok.Mapperly.Descriptors.Mappings; /// target = source.ToString(); /// /// -/// When true, null parameters are not emitted, -/// when false, null parameters are emitted as null literals.. +/// When true, null parameters are not emitted, +/// when false, null parameters are emitted as null literals. /// public class ToStringMapping( ITypeSymbol sourceType, diff --git a/test/Riok.Mapperly.Abstractions.Tests/_snapshots/PublicApiTest.PublicApiHasNotChanged.verified.cs b/test/Riok.Mapperly.Abstractions.Tests/_snapshots/PublicApiTest.PublicApiHasNotChanged.verified.cs index 74a69614d5..2fd0c41640 100644 --- a/test/Riok.Mapperly.Abstractions.Tests/_snapshots/PublicApiTest.PublicApiHasNotChanged.verified.cs +++ b/test/Riok.Mapperly.Abstractions.Tests/_snapshots/PublicApiTest.PublicApiHasNotChanged.verified.cs @@ -220,6 +220,10 @@ public enum MappingConversionType Memory = 16384, Tuple = 32768, EnumUnderlyingType = 65536, + ToTargetMethod = 131072, + AllToTargetMethods = 131088, + StaticConvertMethods = 262144, + AllStaticMethods = 262912, All = -1, } [System.AttributeUsage(System.AttributeTargets.Parameter)] diff --git a/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs b/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs index d34bf0e1c0..02eed2c0d6 100644 --- a/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs +++ b/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs @@ -88,6 +88,14 @@ public static TestObject NewTestObj() DateTimeValue = new DateTime(2020, 1, 3, 15, 10, 5, DateTimeKind.Utc), DateTimeValueTargetDateOnly = new DateTime(2020, 1, 3, 15, 10, 5, DateTimeKind.Utc), DateTimeValueTargetTimeOnly = new DateTime(2020, 1, 3, 15, 10, 5, DateTimeKind.Utc), + ToByteArrayWithInstanceMethod = new Guid(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), + WithCreateMethod = ConvertWithStaticMethodObject.Create(10), + WithCreateFromMethod = ConvertWithStaticMethodObject.Create(20), + WithFromSingleMethod = ConvertWithStaticMethodObject.Create(30), + WithCreateParamsMethod = ConvertWithStaticMethodObject.Create(40), + WithCreateFromParamsMethod = ConvertWithStaticMethodObject.Create(50), + WithFromShortParamsMethod = ConvertWithStaticMethodObject.Create(60), + WithToDecimalMethod = ConvertWithStaticMethodObject.Create(70), IgnoredStringValue = "ignored", RenamedStringValue = "fooBar2", StringNullableTargetNotNullable = "fooBar3", diff --git a/test/Riok.Mapperly.IntegrationTests/Dto/TestObjectDto.cs b/test/Riok.Mapperly.IntegrationTests/Dto/TestObjectDto.cs index 62ee40f98e..851ff33243 100644 --- a/test/Riok.Mapperly.IntegrationTests/Dto/TestObjectDto.cs +++ b/test/Riok.Mapperly.IntegrationTests/Dto/TestObjectDto.cs @@ -131,6 +131,22 @@ public TestObjectDto(int ctorValue, int unknownValue = 10, int ctorValue2 = 100) public TimeOnly DateTimeValueTargetTimeOnly { get; set; } + public byte[] ToByteArrayWithInstanceMethod { get; set; } + + public int WithCreateMethod { get; set; } + + public byte WithCreateFromMethod { get; set; } + + public float WithFromSingleMethod { get; set; } + + public double WithCreateParamsMethod { get; set; } + + public uint WithCreateFromParamsMethod { get; set; } + + public short WithFromShortParamsMethod { get; set; } + + public decimal WithToDecimalMethod { get; set; } + public string FormattedIntValue { get; set; } = string.Empty; public string FormattedDateValue { get; set; } = string.Empty; diff --git a/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs b/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs index fa3c552f9e..633f390f16 100644 --- a/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs +++ b/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs @@ -51,6 +51,8 @@ public TestMapper() public partial DateTime DirectDateTime(DateTime dateTime); + public partial byte[] ConvertWithInstanceMethod(Guid id); + public partial IEnumerable MapAllDtos(IEnumerable objects); [UserMapping(Default = true)] diff --git a/test/Riok.Mapperly.IntegrationTests/Models/ConvertWithStaticMethodObject.cs b/test/Riok.Mapperly.IntegrationTests/Models/ConvertWithStaticMethodObject.cs new file mode 100644 index 0000000000..89d7faf59a --- /dev/null +++ b/test/Riok.Mapperly.IntegrationTests/Models/ConvertWithStaticMethodObject.cs @@ -0,0 +1,74 @@ +using System; + +namespace Riok.Mapperly.IntegrationTests.Models +{ + public class ConvertWithStaticMethodObject + { + public int Value { get; private set; } + + public static ConvertWithStaticMethodObject Create(int value) + { + return new ConvertWithStaticMethodObject { Value = value }; + } + + public static ConvertWithStaticMethodObject CreateFrom(byte value) + { + return new ConvertWithStaticMethodObject { Value = value }; + } + + public static ConvertWithStaticMethodObject FromSingle(float value) + { + return new ConvertWithStaticMethodObject { Value = Convert.ToInt32(value) }; + } + + public static ConvertWithStaticMethodObject Create(params double[] value) + { + return new ConvertWithStaticMethodObject { Value = Convert.ToInt32(value[0]) }; + } + + public static ConvertWithStaticMethodObject CreateFrom(params uint[] value) + { + return new ConvertWithStaticMethodObject { Value = Convert.ToInt32(value[0]) }; + } + + public static ConvertWithStaticMethodObject FromInt16(params short[] value) + { + return new ConvertWithStaticMethodObject { Value = Convert.ToInt32(value[0]) }; + } + + public static int ToInt32(ConvertWithStaticMethodObject obj) + { + return obj.Value; + } + + public static decimal ToDecimal(ConvertWithStaticMethodObject obj) + { + return obj.Value; + } + + public static byte ToByte(ConvertWithStaticMethodObject obj) + { + return Convert.ToByte(obj.Value); + } + + public static float ToSingle(ConvertWithStaticMethodObject obj) + { + return Convert.ToSingle(obj.Value); + } + + public static double ToDouble(ConvertWithStaticMethodObject obj) + { + return Convert.ToDouble(obj.Value); + } + + public static uint ToUInt32(ConvertWithStaticMethodObject obj) + { + return Convert.ToUInt32(obj.Value); + } + + public static short ToInt16(ConvertWithStaticMethodObject obj) + { + return Convert.ToInt16(obj.Value); + } + } +} diff --git a/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs b/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs index c64e425061..b5e8be145b 100644 --- a/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs +++ b/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs @@ -124,11 +124,28 @@ public TestObject(int ctorValue, int unknownValue = 10, int ctorValue2 = 100) public DateTime DateTimeValueTargetTimeOnly { get; set; } + public Guid ToByteArrayWithInstanceMethod { get; set; } + + public ConvertWithStaticMethodObject? WithCreateMethod { get; set; } + + public ConvertWithStaticMethodObject? WithCreateFromMethod { get; set; } + + public ConvertWithStaticMethodObject? WithFromSingleMethod { get; set; } + + public ConvertWithStaticMethodObject? WithCreateParamsMethod { get; set; } + + public ConvertWithStaticMethodObject? WithCreateFromParamsMethod { get; set; } + + public ConvertWithStaticMethodObject? WithFromShortParamsMethod { get; set; } + + public ConvertWithStaticMethodObject? WithToDecimalMethod { get; set; } + public int ExposePrivateValue => PrivateValue; private int PrivateValue { get; set; } public int SumComponent1 { get; set; } + public int SumComponent2 { get; set; } } } diff --git a/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj b/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj index af4515971f..ea072d9672 100644 --- a/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj +++ b/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj @@ -35,7 +35,7 @@ - + diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.RunMappingShouldWork_NET6_0.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.RunMappingShouldWork_NET6_0.verified.txt index f5e55e3aae..75fc61536e 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.RunMappingShouldWork_NET6_0.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.RunMappingShouldWork_NET6_0.verified.txt @@ -183,6 +183,14 @@ DateTimeValue: 2020-01-03 15:10:05 Utc, DateTimeValueTargetDateOnly: 2020-01-03 15:10:05 Utc, DateTimeValueTargetTimeOnly: 2020-01-03 15:10:05 Utc, + ToByteArrayWithInstanceMethod: Guid_1, + WithCreateMethod: {}, + WithCreateFromMethod: {}, + WithFromSingleMethod: {}, + WithCreateParamsMethod: {}, + WithCreateFromParamsMethod: {}, + WithFromShortParamsMethod: {}, + WithToDecimalMethod: {}, ExposePrivateValue: 18, SumComponent1: 32, SumComponent2: 64 diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs index 8f39a4d0ab..56a1c11a0e 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs @@ -140,6 +140,63 @@ public static partial class DeepCloningMapper target.DateTimeValue = src.DateTimeValue; target.DateTimeValueTargetDateOnly = src.DateTimeValueTargetDateOnly; target.DateTimeValueTargetTimeOnly = src.DateTimeValueTargetTimeOnly; + target.ToByteArrayWithInstanceMethod = src.ToByteArrayWithInstanceMethod; + if (src.WithCreateMethod != null) + { + target.WithCreateMethod = MapToConvertWithStaticMethodObject(src.WithCreateMethod); + } + else + { + target.WithCreateMethod = null; + } + if (src.WithCreateFromMethod != null) + { + target.WithCreateFromMethod = MapToConvertWithStaticMethodObject(src.WithCreateFromMethod); + } + else + { + target.WithCreateFromMethod = null; + } + if (src.WithFromSingleMethod != null) + { + target.WithFromSingleMethod = MapToConvertWithStaticMethodObject(src.WithFromSingleMethod); + } + else + { + target.WithFromSingleMethod = null; + } + if (src.WithCreateParamsMethod != null) + { + target.WithCreateParamsMethod = MapToConvertWithStaticMethodObject(src.WithCreateParamsMethod); + } + else + { + target.WithCreateParamsMethod = null; + } + if (src.WithCreateFromParamsMethod != null) + { + target.WithCreateFromParamsMethod = MapToConvertWithStaticMethodObject(src.WithCreateFromParamsMethod); + } + else + { + target.WithCreateFromParamsMethod = null; + } + if (src.WithFromShortParamsMethod != null) + { + target.WithFromShortParamsMethod = MapToConvertWithStaticMethodObject(src.WithFromShortParamsMethod); + } + else + { + target.WithFromShortParamsMethod = null; + } + if (src.WithToDecimalMethod != null) + { + target.WithToDecimalMethod = MapToConvertWithStaticMethodObject(src.WithToDecimalMethod); + } + else + { + target.WithToDecimalMethod = null; + } target.SumComponent1 = src.SumComponent1; target.SumComponent2 = src.SumComponent2; return target; @@ -197,5 +254,12 @@ private static (string A, string) MapToValueTupleOfStringAndString((string A, st target.BaseIntValue = source.BaseIntValue; return target; } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private static global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject MapToConvertWithStaticMethodObject(global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject source) + { + var target = new global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject(); + return target; + } } } \ No newline at end of file diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork_NET8_0.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork_NET8_0.verified.txt index 2693574ec3..6ec7aeb86f 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork_NET8_0.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork_NET8_0.verified.txt @@ -50,6 +50,7 @@ EnumName: Value30, EnumStringValue: 0, EnumReverseStringValue: DtoValue3, + ToByteArrayWithInstanceMethod: AAAAAAAAAAAAAAAAAAAAAA==, FormattedIntValue: CHF 0.00, FormattedDateValue: Monday, January 1, 0001, ExposePrivateValue: 16 @@ -194,6 +195,14 @@ }, DateTimeValueTargetDateOnly: 2020-01-03, DateTimeValueTargetTimeOnly: 3:10 PM, + ToByteArrayWithInstanceMethod: AQAAAAIAAwAEBQYHCAkKCw==, + WithCreateMethod: 10, + WithCreateFromMethod: 20, + WithFromSingleMethod: 30.0, + WithCreateParamsMethod: 40.0, + WithCreateFromParamsMethod: 50, + WithFromShortParamsMethod: 60, + WithToDecimalMethod: 70.0, FormattedIntValue: CHF 10.00, FormattedDateValue: Friday, January 3, 2020, ExposePrivateValue: 18, diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource_NET8_0.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource_NET8_0.verified.cs index 76e1d2007c..4145fb3809 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource_NET8_0.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource_NET8_0.verified.cs @@ -46,6 +46,12 @@ public partial int ParseableInt(string value) return dateTime; } + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + public partial byte[] ConvertWithInstanceMethod(global::System.Guid id) + { + return id.ToByteArray(); + } + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] public partial global::System.Collections.Generic.IEnumerable MapAllDtos(global::System.Collections.Generic.IEnumerable objects) { @@ -213,6 +219,35 @@ public partial int ParseableInt(string value) } target.DateTimeValueTargetDateOnly = global::System.DateOnly.FromDateTime(testObject.DateTimeValueTargetDateOnly); target.DateTimeValueTargetTimeOnly = global::System.TimeOnly.FromDateTime(testObject.DateTimeValueTargetTimeOnly); + target.ToByteArrayWithInstanceMethod = ConvertWithInstanceMethod(testObject.ToByteArrayWithInstanceMethod); + if (testObject.WithCreateMethod != null) + { + target.WithCreateMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt32(testObject.WithCreateMethod); + } + if (testObject.WithCreateFromMethod != null) + { + target.WithCreateFromMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToByte(testObject.WithCreateFromMethod); + } + if (testObject.WithFromSingleMethod != null) + { + target.WithFromSingleMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToSingle(testObject.WithFromSingleMethod); + } + if (testObject.WithCreateParamsMethod != null) + { + target.WithCreateParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDouble(testObject.WithCreateParamsMethod); + } + if (testObject.WithCreateFromParamsMethod != null) + { + target.WithCreateFromParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToUInt32(testObject.WithCreateFromParamsMethod); + } + if (testObject.WithFromShortParamsMethod != null) + { + target.WithFromShortParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt16(testObject.WithFromShortParamsMethod); + } + if (testObject.WithToDecimalMethod != null) + { + target.WithToDecimalMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDecimal(testObject.WithToDecimalMethod); + } target.FormattedIntValue = testObject.IntValue.ToString("C", _formatDeCh); target.FormattedDateValue = testObject.DateTimeValue.ToString("D", _formatEnUs); target.SetPrivateValue(DirectInt(testObject.GetPrivateValue())); @@ -353,6 +388,14 @@ public partial int ParseableInt(string value) { target.SubObject = null; } + target.ToByteArrayWithInstanceMethod = new global::System.Guid(dto.ToByteArrayWithInstanceMethod); + target.WithCreateMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.Create(dto.WithCreateMethod); + target.WithCreateFromMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.CreateFrom(dto.WithCreateFromMethod); + target.WithFromSingleMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.FromSingle(dto.WithFromSingleMethod); + target.WithCreateParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.Create(dto.WithCreateParamsMethod); + target.WithCreateFromParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.CreateFrom(dto.WithCreateFromParamsMethod); + target.WithFromShortParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.FromInt16(dto.WithFromShortParamsMethod); + target.WithToDecimalMethod = MapToConvertWithStaticMethodObject(dto.WithToDecimalMethod); target.SetPrivateValue1(DirectInt(dto.GetPrivateValue1())); return target; } @@ -497,6 +540,35 @@ public partial void UpdateDto(global::Riok.Mapperly.IntegrationTests.Models.Test } target.DateTimeValueTargetDateOnly = global::System.DateOnly.FromDateTime(source.DateTimeValueTargetDateOnly); target.DateTimeValueTargetTimeOnly = global::System.TimeOnly.FromDateTime(source.DateTimeValueTargetTimeOnly); + target.ToByteArrayWithInstanceMethod = ConvertWithInstanceMethod(source.ToByteArrayWithInstanceMethod); + if (source.WithCreateMethod != null) + { + target.WithCreateMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt32(source.WithCreateMethod); + } + if (source.WithCreateFromMethod != null) + { + target.WithCreateFromMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToByte(source.WithCreateFromMethod); + } + if (source.WithFromSingleMethod != null) + { + target.WithFromSingleMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToSingle(source.WithFromSingleMethod); + } + if (source.WithCreateParamsMethod != null) + { + target.WithCreateParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDouble(source.WithCreateParamsMethod); + } + if (source.WithCreateFromParamsMethod != null) + { + target.WithCreateFromParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToUInt32(source.WithCreateFromParamsMethod); + } + if (source.WithFromShortParamsMethod != null) + { + target.WithFromShortParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt16(source.WithFromShortParamsMethod); + } + if (source.WithToDecimalMethod != null) + { + target.WithToDecimalMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDecimal(source.WithToDecimalMethod); + } target.SetPrivateValue(DirectInt(source.GetPrivateValue())); } @@ -685,6 +757,13 @@ private string MapToString1(global::Riok.Mapperly.IntegrationTests.Dto.TestEnumD target.BaseIntValue = DirectInt(source.BaseIntValue); return target; } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject MapToConvertWithStaticMethodObject(decimal source) + { + var target = new global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject(); + return target; + } } [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork_NET6_0.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork_NET6_0.verified.txt index c583b44bf9..d6f13dbed0 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork_NET6_0.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork_NET6_0.verified.txt @@ -43,6 +43,7 @@ EnumName: Value30, EnumStringValue: 0, EnumReverseStringValue: DtoValue3, + ToByteArrayWithInstanceMethod: AAAAAAAAAAAAAAAAAAAAAA==, FormattedIntValue: , FormattedDateValue: , ExposePrivateValue: 26 @@ -187,6 +188,14 @@ }, DateTimeValueTargetDateOnly: 2020-01-03, DateTimeValueTargetTimeOnly: 3:10 PM, + ToByteArrayWithInstanceMethod: AQAAAAIAAwAEBQYHCAkKCw==, + WithCreateMethod: 10, + WithCreateFromMethod: 20, + WithFromSingleMethod: 30.0, + WithCreateParamsMethod: 40.0, + WithCreateFromParamsMethod: 50, + WithFromShortParamsMethod: 60, + WithToDecimalMethod: 70.0, FormattedIntValue: , FormattedDateValue: , ExposePrivateValue: 28 diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork_NET6_0.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork_NET6_0.verified.txt index 9926bfe3a4..612b51e1b9 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork_NET6_0.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork_NET6_0.verified.txt @@ -48,6 +48,7 @@ EnumName: Value30, EnumStringValue: 0, EnumReverseStringValue: DtoValue3, + ToByteArrayWithInstanceMethod: AAAAAAAAAAAAAAAAAAAAAA==, FormattedIntValue: , FormattedDateValue: , ExposePrivateValue: 26 @@ -192,6 +193,14 @@ }, DateTimeValueTargetDateOnly: 2020-01-03, DateTimeValueTargetTimeOnly: 3:10 PM, + ToByteArrayWithInstanceMethod: AQAAAAIAAwAEBQYHCAkKCw==, + WithCreateMethod: 10, + WithCreateFromMethod: 20, + WithFromSingleMethod: 30.0, + WithCreateParamsMethod: 40.0, + WithCreateFromParamsMethod: 50, + WithFromShortParamsMethod: 60, + WithToDecimalMethod: 70.0, FormattedIntValue: , FormattedDateValue: , ExposePrivateValue: 28 diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs index e923b69b17..c6cf9ab150 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs @@ -220,6 +220,35 @@ public static partial void MapIdTargetFirst(global::Riok.Mapperly.IntegrationTes } target.DateTimeValueTargetDateOnly = global::System.DateOnly.FromDateTime(src.DateTimeValueTargetDateOnly); target.DateTimeValueTargetTimeOnly = global::System.TimeOnly.FromDateTime(src.DateTimeValueTargetTimeOnly); + target.ToByteArrayWithInstanceMethod = src.ToByteArrayWithInstanceMethod.ToByteArray(); + if (src.WithCreateMethod != null) + { + target.WithCreateMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt32(src.WithCreateMethod); + } + if (src.WithCreateFromMethod != null) + { + target.WithCreateFromMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToByte(src.WithCreateFromMethod); + } + if (src.WithFromSingleMethod != null) + { + target.WithFromSingleMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToSingle(src.WithFromSingleMethod); + } + if (src.WithCreateParamsMethod != null) + { + target.WithCreateParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDouble(src.WithCreateParamsMethod); + } + if (src.WithCreateFromParamsMethod != null) + { + target.WithCreateFromParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToUInt32(src.WithCreateFromParamsMethod); + } + if (src.WithFromShortParamsMethod != null) + { + target.WithFromShortParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt16(src.WithFromShortParamsMethod); + } + if (src.WithToDecimalMethod != null) + { + target.WithToDecimalMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDecimal(src.WithToDecimalMethod); + } return target; } @@ -372,6 +401,35 @@ public static partial void MapIdTargetFirst(global::Riok.Mapperly.IntegrationTes } target.DateTimeValueTargetDateOnly = global::System.DateOnly.FromDateTime(testObject.DateTimeValueTargetDateOnly); target.DateTimeValueTargetTimeOnly = global::System.TimeOnly.FromDateTime(testObject.DateTimeValueTargetTimeOnly); + target.ToByteArrayWithInstanceMethod = testObject.ToByteArrayWithInstanceMethod.ToByteArray(); + if (testObject.WithCreateMethod != null) + { + target.WithCreateMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt32(testObject.WithCreateMethod); + } + if (testObject.WithCreateFromMethod != null) + { + target.WithCreateFromMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToByte(testObject.WithCreateFromMethod); + } + if (testObject.WithFromSingleMethod != null) + { + target.WithFromSingleMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToSingle(testObject.WithFromSingleMethod); + } + if (testObject.WithCreateParamsMethod != null) + { + target.WithCreateParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDouble(testObject.WithCreateParamsMethod); + } + if (testObject.WithCreateFromParamsMethod != null) + { + target.WithCreateFromParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToUInt32(testObject.WithCreateFromParamsMethod); + } + if (testObject.WithFromShortParamsMethod != null) + { + target.WithFromShortParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt16(testObject.WithFromShortParamsMethod); + } + if (testObject.WithToDecimalMethod != null) + { + target.WithToDecimalMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDecimal(testObject.WithToDecimalMethod); + } return target; } @@ -504,6 +562,14 @@ public static partial void MapIdTargetFirst(global::Riok.Mapperly.IntegrationTes { target.SubObject = null; } + target.ToByteArrayWithInstanceMethod = new global::System.Guid(dto.ToByteArrayWithInstanceMethod); + target.WithCreateMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.Create(dto.WithCreateMethod); + target.WithCreateFromMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.CreateFrom(dto.WithCreateFromMethod); + target.WithFromSingleMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.FromSingle(dto.WithFromSingleMethod); + target.WithCreateParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.Create(dto.WithCreateParamsMethod); + target.WithCreateFromParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.CreateFrom(dto.WithCreateFromParamsMethod); + target.WithFromShortParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.FromInt16(dto.WithFromShortParamsMethod); + target.WithToDecimalMethod = MapToConvertWithStaticMethodObject(dto.WithToDecimalMethod); return target; } @@ -643,6 +709,35 @@ public static partial void UpdateDto(global::Riok.Mapperly.IntegrationTests.Mode } target.DateTimeValueTargetDateOnly = global::System.DateOnly.FromDateTime(source.DateTimeValueTargetDateOnly); target.DateTimeValueTargetTimeOnly = global::System.TimeOnly.FromDateTime(source.DateTimeValueTargetTimeOnly); + target.ToByteArrayWithInstanceMethod = source.ToByteArrayWithInstanceMethod.ToByteArray(); + if (source.WithCreateMethod != null) + { + target.WithCreateMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt32(source.WithCreateMethod); + } + if (source.WithCreateFromMethod != null) + { + target.WithCreateFromMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToByte(source.WithCreateFromMethod); + } + if (source.WithFromSingleMethod != null) + { + target.WithFromSingleMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToSingle(source.WithFromSingleMethod); + } + if (source.WithCreateParamsMethod != null) + { + target.WithCreateParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDouble(source.WithCreateParamsMethod); + } + if (source.WithCreateFromParamsMethod != null) + { + target.WithCreateFromParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToUInt32(source.WithCreateFromParamsMethod); + } + if (source.WithFromShortParamsMethod != null) + { + target.WithFromShortParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToInt16(source.WithFromShortParamsMethod); + } + if (source.WithToDecimalMethod != null) + { + target.WithToDecimalMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToDecimal(source.WithToDecimalMethod); + } } [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] @@ -1035,5 +1130,12 @@ private static string MapToString1(global::Riok.Mapperly.IntegrationTests.Dto.Te target.BaseIntValue = DirectInt(source.BaseIntValue); return target; } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private static global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject MapToConvertWithStaticMethodObject(decimal source) + { + var target = new global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject(); + return target; + } } } \ No newline at end of file diff --git a/test/Riok.Mapperly.Tests/Mapping/DateTimeTest.cs b/test/Riok.Mapperly.Tests/Mapping/DateTimeTest.cs deleted file mode 100644 index 26dcb7f3a6..0000000000 --- a/test/Riok.Mapperly.Tests/Mapping/DateTimeTest.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Riok.Mapperly.Tests.Mapping; - -public class DateTimeTest -{ - [Fact] - public void DateTimeToDateOnly() - { - var source = TestSourceBuilder.Mapping("DateTime", "DateOnly"); - TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::System.DateOnly.FromDateTime(source);"); - } - - [Fact] - public void DateTimeToTimeOnly() - { - var source = TestSourceBuilder.Mapping("DateTime", "TimeOnly"); - TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::System.TimeOnly.FromDateTime(source);"); - } -} diff --git a/test/Riok.Mapperly.Tests/Mapping/StaticMethodConversionTest.cs b/test/Riok.Mapperly.Tests/Mapping/StaticMethodConversionTest.cs new file mode 100644 index 0000000000..2908fa3bfb --- /dev/null +++ b/test/Riok.Mapperly.Tests/Mapping/StaticMethodConversionTest.cs @@ -0,0 +1,373 @@ +using Riok.Mapperly.Abstractions; +using Riok.Mapperly.Diagnostics; + +namespace Riok.Mapperly.Tests.Mapping; + +public class StaticMethodConversionTest +{ + #region CreateMethod + + [Fact] + public void CustomClassWithStaticCreateMethod() + { + var source = TestSourceBuilder.Mapping("int", "A", "class A { public static A Create(int source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateMethod() + { + var source = TestSourceBuilder.Mapping("int", "A", "struct A { public static A Create(int source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateMethodWithParamsArrayArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "class A { public static A Create(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateMethodWithParamsArrayArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "struct A { public static A Create(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateMethodWithParamsArrayArgument2() + { + var source = TestSourceBuilder.Mapping("int[]", "A", "class A { public static A Create(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateMethodWithParamsArrayArgument2() + { + var source = TestSourceBuilder.Mapping("int[]", "A", "struct A { public static A Create(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateMethodWithParamsCollectionArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "class A { public static A Create(params IList source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateMethodWithParamsCollectionArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "struct A { public static A Create(params IEnumerable source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateMethodWithParamsCollectionArgument2() + { + var source = TestSourceBuilder.Mapping("int[]", "A", "class A { public static A Create(params IList source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateMethodWithParamsCollectionArgument2() + { + var source = TestSourceBuilder.Mapping( + "int[]", + "A", + "struct A { public static A Create(params IEnumerable source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + #endregion + + #region CreateFromMethod + + [Fact] + public void CustomClassWithStaticCreateFromMethod() + { + var source = TestSourceBuilder.Mapping("int", "A", "class A { public static A CreateFrom(int source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateFromMethod() + { + var source = TestSourceBuilder.Mapping("int", "A", "struct A { public static A CreateFrom(int source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateFromMethodWithParamsArrayArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "class A { public static A CreateFrom(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateFromMethodWithParamsArrayArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "struct A { public static A CreateFrom(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateFromMethodWithParamsArrayArgument2() + { + var source = TestSourceBuilder.Mapping("int[]", "A", "class A { public static A CreateFrom(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateFromMethodWithParamsArrayArgument2() + { + var source = TestSourceBuilder.Mapping("int[]", "A", "struct A { public static A CreateFrom(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateFromMethodWithParamsCollectionArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "class A { public static A CreateFrom(params IList source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateFromMethodWithParamsCollectionArgument1() + { + var source = TestSourceBuilder.Mapping( + "int", + "A", + "struct A { public static A CreateFrom(params IEnumerable source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateFromMethodWithParamsCollectionArgument2() + { + var source = TestSourceBuilder.Mapping("int[]", "A", "class A { public static A CreateFrom(params IList source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateFromMethodWithParamsCollectionArgument2() + { + var source = TestSourceBuilder.Mapping( + "int[]", + "A", + "struct A { public static A CreateFrom(params IEnumerable source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + #endregion + + #region FromTSourceMethod + + [Fact] + public void CustomClassWithStaticFromSourceMethod() + { + var source = TestSourceBuilder.Mapping("DateTime", "A", "class A { public static A FromDateTime(DateTime source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromDateTime(source);"); + } + + [Fact] + public void CustomStructWithStaticFromSourceMethod() + { + var source = TestSourceBuilder.Mapping("DateTime", "A", "struct A { public static A FromDateTime(DateTime source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromDateTime(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateFromSourceWithParamsArrayArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "class A { public static A FromInt32(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromInt32(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateFromSourceWithParamsArrayArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "struct A { public static A FromInt32(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromInt32(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateFromSourceWithParamsArrayArgument2() + { + var source = TestSourceBuilder.Mapping("int[]", "A", "class A { public static A FromInt32Array(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromInt32Array(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateFromSourceWithParamsArrayArgument2() + { + var source = TestSourceBuilder.Mapping("int[]", "A", "struct A { public static A FromInt32Array(params int[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromInt32Array(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateFromSourceWithParamsCollectionArgument1() + { + var source = TestSourceBuilder.Mapping("int", "A", "class A { public static A FromInt32(params IList source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromInt32(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateFromSourceWithParamsCollectionArgument1() + { + var source = TestSourceBuilder.Mapping( + "int", + "A", + "struct A { public static A FromInt32(params IEnumerable source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromInt32(source);"); + } + + [Fact] + public void CustomClassWithStaticCreateFromSourceWithParamsCollectionArgument2() + { + var source = TestSourceBuilder.Mapping( + "int[]", + "A", + "class A { public static A FromInt32Array(params IList source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromInt32Array(source);"); + } + + [Fact] + public void CustomStructWithStaticCreateFromSourceWithParamsCollectionArgument2() + { + var source = TestSourceBuilder.Mapping( + "int[]", + "A", + "struct A { public static A FromInt32Array(params IEnumerable source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromInt32Array(source);"); + } + + #endregion + + #region ToTTargetMethod + + [Fact] + public void CustomClassWithStaticToTargetMethod() + { + var source = TestSourceBuilder.Mapping("A", "DateTime", "class A { public static DateTime ToDateTime(A source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToDateTime(source);"); + } + + [Fact] + public void CustomStructWithStaticToTargetMethod() + { + var source = TestSourceBuilder.Mapping("A", "DateTime", "struct A { public static DateTime ToDateTime(A source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToDateTime(source);"); + } + + [Fact] + public void CustomClassWithStaticToTargetWithParamsArrayArgument1() + { + var source = TestSourceBuilder.Mapping("A", "int[]", "class A { public static int[] ToInt32Array(params A[] source) => []; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32Array(source);"); + } + + [Fact] + public void CustomStructWithStaticToTargetWithParamsArrayArgument1() + { + var source = TestSourceBuilder.Mapping("A", "int", "struct A { public static int ToInt32(params A[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32(source);"); + } + + [Fact] + public void CustomClassWithStaticToTargetWithParamsArrayArgument2() + { + var source = TestSourceBuilder.Mapping("A[]", "int[]", "class A { public static int[] ToInt32Array(params A[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32Array(source);"); + } + + [Fact] + public void CustomStructWithStaticToTargetWithParamsArrayArgument2() + { + var source = TestSourceBuilder.Mapping("A[]", "int", "struct A { public static int ToInt32(params A[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32(source);"); + } + + [Fact] + public void CustomClassWithStaticToTargetWithParamsCollectionArgument1() + { + var source = TestSourceBuilder.Mapping("A", "int", "class A { public static int ToInt32(params IList source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32(source);"); + } + + [Fact] + public void CustomStructWithStaticToTargetWithParamsCollectionArgument1() + { + var source = TestSourceBuilder.Mapping( + "A", + "int", + "struct A { public static int ToInt32(params IEnumerable source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32(source);"); + } + + [Fact] + public void CustomClassWithStaticToTargetWithParamsCollectionArgument2() + { + var source = TestSourceBuilder.Mapping("A[]", "int", "class A { public static int ToInt32(params IList source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32(source);"); + } + + [Fact] + public void CustomStructWithStaticToTargetWithParamsCollectionArgument2() + { + var source = TestSourceBuilder.Mapping( + "A", + "int[]", + "struct A { public static int[] ToInt32Array(params IEnumerable source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32Array(source);"); + } + + [Fact] + public void CustomClassWithStaticToTargetWithParamsCollectionArgument3() + { + var source = TestSourceBuilder.Mapping( + "IList", + "int", + "class A { public static int ToInt32(params IList source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32(source);"); + } + + [Fact] + public void CustomStructWithStaticToTargetWithParamsCollectionArgument3() + { + var source = TestSourceBuilder.Mapping( + "IList", + "int[]", + "struct A { public static int[] ToInt32Array(params IEnumerable source) => new(); }" + ); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToInt32Array(source);"); + } + + #endregion + + [Fact] + public void StaticMethodMappingDisabledShouldDiagnostic() + { + var source = TestSourceBuilder.Mapping( + "A", + "int", + TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.StaticConvertMethods), + "class A { public static int ToInt32() => 0; }" + ); + TestHelper + .GenerateMapper(source, TestHelperOptions.AllowDiagnostics) + .Should() + .HaveDiagnostic(DiagnosticDescriptors.CouldNotCreateMapping) + .HaveAssertedAllDiagnostics(); + } +} diff --git a/test/Riok.Mapperly.Tests/Mapping/ToTargetInstanceTest.cs b/test/Riok.Mapperly.Tests/Mapping/ToTargetInstanceTest.cs new file mode 100644 index 0000000000..d311018279 --- /dev/null +++ b/test/Riok.Mapperly.Tests/Mapping/ToTargetInstanceTest.cs @@ -0,0 +1,58 @@ +using Riok.Mapperly.Abstractions; +using Riok.Mapperly.Diagnostics; + +namespace Riok.Mapperly.Tests.Mapping; + +public class ToTargetInstanceTest +{ + [Fact] + public void CustomClassToTarget() + { + var source = TestSourceBuilder.Mapping("A", "int", "class A { public int ToInt32() => 0; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return source.ToInt32();"); + } + + [Fact] + public void CustomStructToTarget() + { + var source = TestSourceBuilder.Mapping("A", "int", "struct A { public int ToInt32() => 0; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return source.ToInt32();"); + } + + [Fact] + public void CustomClassToTargetArray() + { + var source = TestSourceBuilder.Mapping("A", "string[]", "class A { public string[] ToStringArray() => [0]; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return source.ToStringArray();"); + } + + [Fact] + public void CustomStructToTargetArray() + { + var source = TestSourceBuilder.Mapping("A", "float[]", "struct A { public float[] ToSingleArray() => [0]; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return source.ToSingleArray();"); + } + + [Fact] + public void BuiltInStructToTargetArray() + { + var source = TestSourceBuilder.Mapping("Guid", "byte[]"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return source.ToByteArray();"); + } + + [Fact] + public void ToTargetMappingDisabledShouldDiagnostic() + { + var source = TestSourceBuilder.Mapping( + "A", + "int", + TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.ToTargetMethod), + "class A { public int ToInt32() => 0; }" + ); + TestHelper + .GenerateMapper(source, TestHelperOptions.AllowDiagnostics) + .Should() + .HaveDiagnostic(DiagnosticDescriptors.CouldNotCreateMapping) + .HaveAssertedAllDiagnostics(); + } +}