diff --git a/docs/docs/configuration/conversions.md b/docs/docs/configuration/conversions.md index bde8e45d92..f8ad322248 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 `CreateFromTTarget`, `FromTSource`, `Create`, `CreateFrom`, 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 CreateFromTTarget(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..9d3e6181ca 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,29 @@ 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, + + /// + /// If the source type contains a static `ToTarget` method + /// or the target type contains a static methods + /// `Create(TSource)`, + /// `CreateFrom(TSource)`, + /// `CreateFromTSource(TSource)`, + /// `From(TSource)`, + /// `FromTSource(TSource)` + /// or similar methods with keyword, use it. + /// The exception is conversions, + /// which are enabled by separate options (, ). + /// + StaticConvertMethods = 1 << 18, + /// /// 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..ac11390b47 --- /dev/null +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertInstanceMethodMappingBuilder.cs @@ -0,0 +1,66 @@ +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; + + foreach (var methodName in GetMappingMethodNames(ctx)) + { + var methodCandidates = ctx + .SymbolAccessor.GetAllMethods(ctx.Source) + .Where(m => + string.Equals(m.Name, methodName, StringComparison.OrdinalIgnoreCase) + && m is { IsStatic: false, ReturnsVoid: false, IsAsync: false, Parameters.Length: 0 } + ) + .ToList(); + + // try to find method with equal nullability return type + var method = methodCandidates.Find(x => SymbolEqualityComparer.IncludeNullability.Equals(x.ReturnType, ctx.Target)); + if (method is not null) + return new SourceObjectMethodMapping(ctx.Source, ctx.Target, method.Name); + + if (!targetIsNullable) + continue; + + // otherwise try to find method ignoring the nullability + method = methodCandidates.Find(x => SymbolEqualityComparer.Default.Equals(x.ReturnType, nonNullableTarget)); + + if (method is null) + continue; + + return new SourceObjectMethodMapping(ctx.Source, ctx.Target, method.Name); + } + + return null; + } + + private static IEnumerable GetMappingMethodNames(MappingBuilderContext ctx) + { + var nonNullableTarget = ctx.Target.NonNullable(); + var hasKeyword = nonNullableTarget.HasKeyword(out var keywordName); + if (!nonNullableTarget.IsArrayType(out var arrayType)) + { + var methodName = $"To{nonNullableTarget.Name}"; + return hasKeyword ? [methodName, $"To{keywordName}"] : [methodName]; + } + + var nonNullableElementType = arrayType.ElementType.NonNullable(); + + var arrayMethodName = $"To{nonNullableElementType.Name}Array"; + return hasKeyword ? [arrayMethodName, $"To{keywordName}Array"] : [arrayMethodName]; + } +} diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertStaticMethodMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertStaticMethodMappingBuilder.cs new file mode 100644 index 0000000000..0e720de9c9 --- /dev/null +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/ConvertStaticMethodMappingBuilder.cs @@ -0,0 +1,169 @@ +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) + { + if (!IsConversionEnabled(ctx)) + return null; + + var targetIsNullable = ctx.Target.NonNullable(out var nonNullableTarget); + + var allTargetMethods = ctx.SymbolAccessor.GetAllMethods(nonNullableTarget).ToList(); + + var mapping = TryGetStaticMethodMapping( + ctx.SymbolAccessor, + allTargetMethods, + GetTargetStaticMethodNames(ctx), + ctx.Source, + ctx.Target, + nonNullableTarget, + targetIsNullable + ); + + if (mapping is not null) + { + return mapping; + } + + var allSourceMethods = ctx.SymbolAccessor.GetAllMethods(ctx.Source); + + // collect also methods from source type generic argument, for example `TTarget ToTarget(List source)` + if (ctx.Source is INamedTypeSymbol { TypeArguments.Length: 1 } namedTypeSymbol) + { + allSourceMethods = allSourceMethods.Concat(ctx.SymbolAccessor.GetAllMethods(namedTypeSymbol.TypeArguments[0])); + } + + return TryGetStaticMethodMapping( + ctx.SymbolAccessor, + allSourceMethods.ToList(), + GetSourceStaticMethodNames(ctx), + ctx.Source, + ctx.Target, + nonNullableTarget, + targetIsNullable + ); + } + + private static bool IsConversionEnabled(MappingBuilderContext ctx) + { + //checks DateTime type for backward compatibility + return ctx.IsConversionEnabled( + IsDateTimeToDateOnlyConversion(ctx) ? MappingConversionType.DateTimeToDateOnly + : IsDateTimeToTimeOnlyConversion(ctx) ? MappingConversionType.DateTimeToTimeOnly + : MappingConversionType.StaticConvertMethods + ); + } + + 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); + } + + 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); + } + + private static StaticMethodMapping? TryGetStaticMethodMapping( + SymbolAccessor symbolAccessor, + List allMethods, + IEnumerable methodNames, + ITypeSymbol sourceType, + ITypeSymbol targetType, + ITypeSymbol nonNullableTargetType, + bool targetIsNullable + ) + { + // Get all methods with a single parameter whose type is suitable for assignment to the source type, group them by name, + // and convert them to a dictionary whose key is the method name. + // The keys in the dictionary are compared case-insensitively to handle possible `Uint` vs `UInt` cases, etc. + var allMethodCandidates = allMethods + .Where(m => m is { IsStatic: true, ReturnsVoid: false, IsAsync: false, Parameters.Length: 1 }) + .GroupBy(x => x.Name, x => x, StringComparer.OrdinalIgnoreCase) + .ToDictionary(x => x.Key, x => x.ToList(), StringComparer.OrdinalIgnoreCase); + + foreach (var methodName in methodNames) + { + if (!allMethodCandidates.TryGetValue(methodName, out var candidates)) + continue; + + if (targetIsNullable) + { + continue; + } + + var method = candidates.Find(x => symbolAccessor.ValidateSignature(x, nonNullableTargetType, sourceType)); + + if (method != null) + return new StaticMethodMapping(method); + } + + return null; + } + + private static IEnumerable GetTargetStaticMethodNames(MappingBuilderContext ctx) + { + if (ctx.Source.IsArrayType(out var arrayType)) + { + yield return $"CreateFrom{arrayType.ElementType.Name}Array"; + yield return $"From{arrayType.ElementType.Name}Array"; + + if (!arrayType.ElementType.HasKeyword(out var keywordName)) + yield break; + + yield return $"CreateFrom{keywordName}Array"; + + yield return $"From{keywordName}Array"; + + yield return "FromArray"; + yield return "CreateFromArray"; + yield return "CreateFrom"; + yield return "Create"; + yield break; + } + + yield return $"CreateFrom{ctx.Source.Name}"; + yield return $"From{ctx.Source.Name}"; + + if (ctx.Source.HasKeyword(out var sourceKeyword)) + { + yield return $"CreateFrom{sourceKeyword}"; + yield return $"From{sourceKeyword}"; + } + + yield return "CreateFrom"; + yield return "Create"; + } + + private static IEnumerable GetSourceStaticMethodNames(MappingBuilderContext ctx) + { + var nonNullableTarget = ctx.Target.NonNullable(); + + yield return $"To{nonNullableTarget.Name}"; + + if (nonNullableTarget.HasKeyword(out var keywordName)) + yield return $"To{keywordName}"; + + if (!nonNullableTarget.IsArrayType(out var arrayTypeSymbol)) + yield break; + + var nonNullableElementType = arrayTypeSymbol.ElementType.NonNullable(); + + yield return $"To{nonNullableElementType.Name}Array"; + + if (nonNullableElementType.HasKeyword(out var elementTypeName)) + yield return $"To{elementTypeName}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/MappingBuilders/ParseMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/ParseMappingBuilder.cs index 003467bc24..d308eb83d8 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBuilders/ParseMappingBuilder.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/ParseMappingBuilder.cs @@ -22,18 +22,14 @@ public static class ParseMappingBuilder var parseMethodCandidates = ctx .SymbolAccessor.GetAllMethods(nonNullableTarget, ParseMethodName) .Where(m => - m.IsStatic - && !m.ReturnsVoid - && !m.IsAsync - && m.Parameters.Length == 1 + m is { IsStatic: true, ReturnsVoid: false, IsAsync: false, Parameters.Length: 1 } && SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, ctx.Source) ) .ToList(); // try to find parse method with equal nullability return type - var parseMethod = parseMethodCandidates.FirstOrDefault(x => - SymbolEqualityComparer.IncludeNullability.Equals(x.ReturnType, ctx.Target) - ); + var parseMethod = parseMethodCandidates.Find(x => SymbolEqualityComparer.IncludeNullability.Equals(x.ReturnType, ctx.Target)); + if (parseMethod != null) return new StaticMethodMapping(parseMethod); diff --git a/src/Riok.Mapperly/Descriptors/Mappings/ToStringMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/ToStringMapping.cs index 0b57982a69..6b005e88af 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 , parameters are not emitted, +/// when , parameters are emitted as literals. /// public class ToStringMapping( ITypeSymbol sourceType, diff --git a/src/Riok.Mapperly/Descriptors/SymbolAccessor.cs b/src/Riok.Mapperly/Descriptors/SymbolAccessor.cs index 93dadee2e6..cd39a2344f 100644 --- a/src/Riok.Mapperly/Descriptors/SymbolAccessor.cs +++ b/src/Riok.Mapperly/Descriptors/SymbolAccessor.cs @@ -35,6 +35,11 @@ public class SymbolAccessor(CompilationContext compilationContext, INamedTypeSym private Compilation Compilation => compilationContext.Compilation; + private readonly Lazy _lazyEnumerableType = new( + () => compilationContext.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T) + ); + private INamedTypeSymbol EnumerableTypeSymbol => _lazyEnumerableType.Value; + internal void SetMemberVisibility(MemberVisibility visibility) => _memberVisibility = visibility; internal void SetConstructorVisibility(MemberVisibility visibility) => _constructorVisibility = visibility; @@ -280,11 +285,11 @@ internal bool TryFindMemberPath( foundPath.Clear(); foundPath.Add(member); - if (pathCandidate.Path.Count == 1 || TryFindPath(member.Type, pathCandidate.SkipRoot(), ignoreCase, foundPath)) - { - memberPath = new NonEmptyMemberPath(member.Type, foundPath); - return true; - } + if (pathCandidate.Path.Count != 1 && !TryFindPath(member.Type, pathCandidate.SkipRoot(), ignoreCase, foundPath)) + continue; + + memberPath = new NonEmptyMemberPath(member.Type, foundPath); + return true; } memberPath = null; @@ -370,6 +375,61 @@ internal bool TryFindMemberPath(ITypeSymbol type, StringMemberPath path, [NotNul return false; } + /// + /// Checks that the specified method returns a type that can be assigned to the specified result type, + /// and that the specified arguments can be passed to the method. + /// The check takes into account the possibility of assignment to a method with an argument marked with the keyword + /// + /// Method for validate + /// Target return type + /// Target method arguments + /// + internal bool ValidateSignature(IMethodSymbol method, ITypeSymbol returnType, params ITypeSymbol[] argTypes) + { + return CanAssign(method.ReturnType, returnType) && Enumerable.Range(0, method.Parameters.Length).All(IsValidParameter); + + bool IsValidParameter(int i) + { + if (method.Parameters[i] is not { IsParams: true } isParamsParameter) + { + return CanAssign(argTypes[i], method.Parameters[i].Type); + } + + // see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/method-parameters#params-modifier + + var argsToEnd = argTypes.AsSpan(i); + + // for empty args aka Call(params X[]) as Call() + if (argsToEnd.IsEmpty) + { + return true; + } + + var elementType = isParamsParameter.Type.ImplementsGeneric(EnumerableTypeSymbol, out var impl) + // for assignable to IEnumerable + ? impl.TypeArguments.First() + // for Span and ReadOnlySpan + : ((INamedTypeSymbol)method.Parameters[i].Type).TypeArguments.First(); + + //for single arg aka Call(X[]) or Call(X) + if (argsToEnd.Length == 1) + { + return CanAssign(argsToEnd[0], method.Parameters[i].Type) || CanAssign(argsToEnd[0], elementType); + } + + // for multiple args + foreach (var typeSymbol in argsToEnd) + { + if (!CanAssign(typeSymbol, elementType)) + { + return false; + } + } + + return true; + } + } + private bool TryFindPath(ITypeSymbol type, StringMemberPath path, bool ignoreCase, ICollection foundPath) { foreach (var name in path.Path) diff --git a/src/Riok.Mapperly/Helpers/SymbolExtensions.cs b/src/Riok.Mapperly/Helpers/SymbolExtensions.cs index 6891ff9765..88ba442330 100644 --- a/src/Riok.Mapperly/Helpers/SymbolExtensions.cs +++ b/src/Riok.Mapperly/Helpers/SymbolExtensions.cs @@ -245,4 +245,24 @@ internal static bool IsInRootNamespace(this ISymbol symbol, string ns) return namespaceSymbol != null && string.Equals(namespaceSymbol.Name, ns, StringComparison.Ordinal); } + + /// + /// Returns the keyword if the type can be named by it. + /// For example, for symbol with special type + /// or for symbol with special type + /// + /// The type + /// The keyword + /// if the type can be named by keyword, otherwise + internal static bool HasKeyword(this ITypeSymbol typeSymbol, [NotNullWhen(true)] out string? keywordName) + { + if (typeSymbol.SpecialType is >= SpecialType.System_Boolean and <= SpecialType.System_String) + { + keywordName = typeSymbol.ToDisplayString(); + return true; + } + + keywordName = null; + return false; + } } 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..a5691af321 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,8 @@ public enum MappingConversionType Memory = 16384, Tuple = 32768, EnumUnderlyingType = 65536, + ToTargetMethod = 131072, + StaticConvertMethods = 262144, 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..a4f221bdde 100644 --- a/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs +++ b/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs @@ -88,6 +88,16 @@ 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), +#if NET5_0_OR_GREATER + ToByteArrayWithInstanceMethod = new Guid(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), +#endif + 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..98d8cd6c39 100644 --- a/test/Riok.Mapperly.IntegrationTests/Dto/TestObjectDto.cs +++ b/test/Riok.Mapperly.IntegrationTests/Dto/TestObjectDto.cs @@ -99,9 +99,9 @@ public TestObjectDto(int ctorValue, int unknownValue = 10, int ctorValue2 = 100) public IReadOnlySet IReadOnlySet { get; set; } = new HashSet(); #endif - public HashSet HashSet { get; set; } = new HashSet(); + public HashSet HashSet { get; set; } = new(); - public SortedSet SortedSet { get; set; } = new SortedSet(); + public SortedSet SortedSet { get; set; } = new(); public TestEnumDtoByValue EnumValue { get; set; } @@ -131,6 +131,24 @@ public TestObjectDto(int ctorValue, int unknownValue = 10, int ctorValue2 = 100) public TimeOnly DateTimeValueTargetTimeOnly { get; set; } +#if NET5_0_OR_GREATER + public byte[]? ToByteArrayWithInstanceMethod { get; set; } +#endif + + 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..5d44cf4af7 100644 --- a/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs +++ b/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs @@ -51,6 +51,10 @@ public TestMapper() public partial DateTime DirectDateTime(DateTime dateTime); +#if NET5_0_OR_GREATER + public partial byte[] ConvertWithInstanceMethod(Guid id); +#endif + 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..8a1769ac6a --- /dev/null +++ b/test/Riok.Mapperly.IntegrationTests/Models/ConvertWithStaticMethodObject.cs @@ -0,0 +1,84 @@ +using System; + +namespace Riok.Mapperly.IntegrationTests.Models +{ + public class ConvertWithStaticMethodObject + { + public int Value { get; private set; } + + public static ConvertWithStaticMethodObject ToConvertWithStaticMethodObject(ConvertWithStaticMethodObject value) + { + return new ConvertWithStaticMethodObject { Value = value.Value }; + } + + 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 CreateFromDecimal(decimal value) + { + return new ConvertWithStaticMethodObject { Value = Convert.ToInt32(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..7891fec375 100644 --- a/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs +++ b/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs @@ -124,11 +124,30 @@ public TestObject(int ctorValue, int unknownValue = 10, int ctorValue2 = 100) public DateTime DateTimeValueTargetTimeOnly { get; set; } +#if NET5_0_OR_GREATER + public Guid ToByteArrayWithInstanceMethod { get; set; } +#endif + + 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.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.RunMappingShouldWork.verified.txt index fc65886df2..1415e97a73 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.RunMappingShouldWork.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.RunMappingShouldWork.verified.txt @@ -178,6 +178,27 @@ DateTimeValue: 2020-01-03 15:10:05 Utc, DateTimeValueTargetDateOnly: 2020-01-03 15:10:05 Utc, DateTimeValueTargetTimeOnly: 2020-01-03 15:10:05 Utc, + WithCreateMethod: { + Value: 10 + }, + WithCreateFromMethod: { + Value: 20 + }, + WithFromSingleMethod: { + Value: 30 + }, + WithCreateParamsMethod: { + Value: 40 + }, + WithCreateFromParamsMethod: { + Value: 50 + }, + WithFromShortParamsMethod: { + Value: 60 + }, + WithToDecimalMethod: { + Value: 70 + }, ExposePrivateValue: 18, SumComponent1: 32, SumComponent2: 64 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..cc223a7f10 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,28 @@ 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: { + Value: 10 + }, + WithCreateFromMethod: { + Value: 20 + }, + WithFromSingleMethod: { + Value: 30 + }, + WithCreateParamsMethod: { + Value: 40 + }, + WithCreateFromParamsMethod: { + Value: 50 + }, + WithFromShortParamsMethod: { + Value: 60 + }, + WithToDecimalMethod: { + Value: 70 + }, ExposePrivateValue: 18, SumComponent1: 32, SumComponent2: 64 diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.SnapshotGeneratedSource.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.SnapshotGeneratedSource.verified.cs index 23a47d7a6f..3e20c70db4 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.SnapshotGeneratedSource.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/DeepCloningMapperTest.SnapshotGeneratedSource.verified.cs @@ -138,6 +138,62 @@ public static partial class DeepCloningMapper target.DateTimeValue = src.DateTimeValue; target.DateTimeValueTargetDateOnly = src.DateTimeValueTargetDateOnly; target.DateTimeValueTargetTimeOnly = src.DateTimeValueTargetTimeOnly; + if (src.WithCreateMethod != null) + { + target.WithCreateMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithCreateMethod); + } + else + { + target.WithCreateMethod = null; + } + if (src.WithCreateFromMethod != null) + { + target.WithCreateFromMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithCreateFromMethod); + } + else + { + target.WithCreateFromMethod = null; + } + if (src.WithFromSingleMethod != null) + { + target.WithFromSingleMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithFromSingleMethod); + } + else + { + target.WithFromSingleMethod = null; + } + if (src.WithCreateParamsMethod != null) + { + target.WithCreateParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithCreateParamsMethod); + } + else + { + target.WithCreateParamsMethod = null; + } + if (src.WithCreateFromParamsMethod != null) + { + target.WithCreateFromParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithCreateFromParamsMethod); + } + else + { + target.WithCreateFromParamsMethod = null; + } + if (src.WithFromShortParamsMethod != null) + { + target.WithFromShortParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithFromShortParamsMethod); + } + else + { + target.WithFromShortParamsMethod = null; + } + if (src.WithToDecimalMethod != null) + { + target.WithToDecimalMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithToDecimalMethod); + } + else + { + target.WithToDecimalMethod = null; + } target.SumComponent1 = src.SumComponent1; target.SumComponent2 = src.SumComponent2; return target; 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..89bd28f005 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 = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithCreateMethod); + } + else + { + target.WithCreateMethod = null; + } + if (src.WithCreateFromMethod != null) + { + target.WithCreateFromMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithCreateFromMethod); + } + else + { + target.WithCreateFromMethod = null; + } + if (src.WithFromSingleMethod != null) + { + target.WithFromSingleMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithFromSingleMethod); + } + else + { + target.WithFromSingleMethod = null; + } + if (src.WithCreateParamsMethod != null) + { + target.WithCreateParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithCreateParamsMethod); + } + else + { + target.WithCreateParamsMethod = null; + } + if (src.WithCreateFromParamsMethod != null) + { + target.WithCreateFromParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithCreateFromParamsMethod); + } + else + { + target.WithCreateFromParamsMethod = null; + } + if (src.WithFromShortParamsMethod != null) + { + target.WithFromShortParamsMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithFromShortParamsMethod); + } + else + { + target.WithFromShortParamsMethod = null; + } + if (src.WithToDecimalMethod != null) + { + target.WithToDecimalMethod = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.ToConvertWithStaticMethodObject(src.WithToDecimalMethod); + } + else + { + target.WithToDecimalMethod = null; + } target.SumComponent1 = src.SumComponent1; target.SumComponent2 = src.SumComponent2; return target; diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork.verified.txt index b2ff941bad..60b8433150 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork.verified.txt @@ -189,6 +189,13 @@ }, DateTimeValueTargetDateOnly: 2020-01-03, DateTimeValueTargetTimeOnly: 3:10 PM, + 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: 28, diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork_NET6_0.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork_NET6_0.verified.txt index c6ed90f472..c78c4ad162 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork_NET6_0.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork_NET6_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: 26 @@ -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: 28, 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.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource.verified.cs index b2afee05ae..0af30f2565 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource.verified.cs @@ -209,6 +209,34 @@ public partial int ParseableInt(string value) } target.DateTimeValueTargetDateOnly = global::System.DateOnly.FromDateTime(testObject.DateTimeValueTargetDateOnly); target.DateTimeValueTargetTimeOnly = global::System.TimeOnly.FromDateTime(testObject.DateTimeValueTargetTimeOnly); + 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.Sum = ComputeSum(testObject); @@ -344,6 +372,13 @@ public partial int ParseableInt(string value) { target.SubObject = null; } + 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 = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.CreateFromDecimal(dto.WithToDecimalMethod); return target; } @@ -483,6 +518,34 @@ 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); + 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")] diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource_NET6_0.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource_NET6_0.verified.cs index 9594de4ef7..0aa2138956 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource_NET6_0.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource_NET6_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.Sum = ComputeSum(testObject); @@ -352,6 +387,17 @@ public partial int ParseableInt(string value) { target.SubObject = null; } + if (dto.ToByteArrayWithInstanceMethod != 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 = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.CreateFromDecimal(dto.WithToDecimalMethod); return target; } @@ -495,6 +541,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); + } } [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] 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..804f7f7252 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,17 @@ public partial int ParseableInt(string value) { target.SubObject = null; } + if (dto.ToByteArrayWithInstanceMethod != 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 = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.CreateFromDecimal(dto.WithToDecimalMethod); target.SetPrivateValue1(DirectInt(dto.GetPrivateValue1())); return target; } @@ -497,6 +543,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())); } diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt index ba864a7b95..8c073e2dbf 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt @@ -182,6 +182,13 @@ }, DateTimeValueTargetDateOnly: 2020-01-03, DateTimeValueTargetTimeOnly: 3:10 PM, + 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.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.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt index 25ef68cc61..3b0503a8d1 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt @@ -187,6 +187,13 @@ }, DateTimeValueTargetDateOnly: 2020-01-03, DateTimeValueTargetTimeOnly: 3:10 PM, + 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.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs index e91bab3463..d4ccd0ed6d 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs @@ -216,6 +216,34 @@ 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); + 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; } @@ -364,6 +392,34 @@ 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); + 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; } @@ -492,6 +548,13 @@ public static partial void MapIdTargetFirst(global::Riok.Mapperly.IntegrationTes { target.SubObject = null; } + 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 = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.CreateFromDecimal(dto.WithToDecimalMethod); return target; } @@ -627,6 +690,34 @@ 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); + 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")] 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..c4d13f4b0e 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,17 @@ public static partial void MapIdTargetFirst(global::Riok.Mapperly.IntegrationTes { target.SubObject = null; } + if (dto.ToByteArrayWithInstanceMethod != 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 = global::Riok.Mapperly.IntegrationTests.Models.ConvertWithStaticMethodObject.CreateFromDecimal(dto.WithToDecimalMethod); return target; } @@ -643,6 +712,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")] 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..a7bf14b49a --- /dev/null +++ b/test/Riok.Mapperly.Tests/Mapping/StaticMethodConversionTest.cs @@ -0,0 +1,200 @@ +using Riok.Mapperly.Abstractions; +using Riok.Mapperly.Diagnostics; + +namespace Riok.Mapperly.Tests.Mapping; + +public class StaticMethodConversionTest +{ + [Fact] + public void ClassWithStaticToTTargetMethod() + { + var source = TestSourceBuilder.Mapping("A", "byte", "class A { public static byte ToByte(A source) => 0; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToByte(source);"); + } + + [Fact] + public void ClassWithStaticToTTargetArrayMethod() + { + var source = TestSourceBuilder.Mapping("A", "byte[]", "class A { public static byte[] ToByteArray(A source) => []; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToByteArray(source);"); + } + + [Fact] + public void ClassWithStaticToTTargetMethodWhereTTargetHasKeyword() + { + var source = TestSourceBuilder.Mapping("A", "float", "class A { public static float ToFloat(A source) => 0; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToFloat(source);"); + } + + [Fact] + public void ClassWithStaticToTTargetArrayMethodWhereTTargetHasKeyword() + { + var source = TestSourceBuilder.Mapping("A", "float[]", "class A { public static float[] ToFloatArray(A source) => []; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToFloatArray(source);"); + } + + [Fact] + public void ClassWithStaticCreateMethod() + { + var source = TestSourceBuilder.Mapping("byte", "A", "class A { public static A Create(byte source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void ClassWithStaticCreateMethodForArraySource() + { + var source = TestSourceBuilder.Mapping("byte[]", "A", "class A { public static A Create(byte[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.Create(source);"); + } + + [Fact] + public void ClassWithStaticCreateFromMethod() + { + var source = TestSourceBuilder.Mapping("byte", "A", "class A { public static A CreateFrom(byte source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void ClassWithStaticCreateFromMethodForArraySource() + { + var source = TestSourceBuilder.Mapping("byte[]", "A", "class A { public static A CreateFrom(byte[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFrom(source);"); + } + + [Fact] + public void ClassWithStaticCreateFromArrayMethod() + { + var source = TestSourceBuilder.Mapping("byte[]", "A", "class A { public static A CreateFromArray(byte[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFromArray(source);"); + } + + [Fact] + public void ClassWithStaticCreateFromTTargetArrayMethod() + { + var source = TestSourceBuilder.Mapping("byte[]", "A", "class A { public static A CreateFromByteArray(byte[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFromByteArray(source);"); + } + + [Fact] + public void ClassWithStaticCreateFromTTargetMethod() + { + var source = TestSourceBuilder.Mapping("byte", "A", "class A { public static A CreateFromByte(byte source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFromByte(source);"); + } + + [Fact] + public void ClassWithStaticCreateFromTTargetWhereTTargetIsKeywordMethod() + { + var source = TestSourceBuilder.Mapping("float", "A", "class A { public static A CreateFromFloat(float source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.CreateFromFloat(source);"); + } + + [Fact] + public void ClassWithStaticFromTTargetMethod() + { + var source = TestSourceBuilder.Mapping("byte", "A", "class A { public static A FromByte(byte source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromByte(source);"); + } + + [Fact] + public void ClassWithStaticFromTTargetArrayMethod() + { + var source = TestSourceBuilder.Mapping("byte[]", "A", "class A { public static A FromByteArray(byte[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromByteArray(source);"); + } + + [Fact] + public void MethodNameCaseInsensitive() + { + var source = TestSourceBuilder.Mapping("byte[]", "A", "class A { public static A frombytearray(byte[] source) => new(); }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.frombytearray(source);"); + } + + [Fact(Skip = "RMG090: Mapping the nullable source of type B? to target of type A which is not nullable")] + public void MapNullableReferenceTypeToNonNullableReferenceType() + { + var source = TestSourceBuilder.Mapping("B?", "A", "class A { public static A FromB(B? source) => new(); }; class B {}"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromB(source);"); + } + + [Fact] + public void MapNonNullableReferenceTypeToNullableReferenceType() + { + var source = TestSourceBuilder.Mapping("B", "A?", "class A { public static A FromB(B source) => new(); }; class B {}"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromB(source);"); + } + + [Fact(Skip = "RMG090: Mapping the nullable source of type B? to target of type A which is not nullable")] + public void MapNullableValueTypeToNonNullableValueType() + { + var source = TestSourceBuilder.Mapping("B?", "A", "struct A { public static A FromB(B? source) => new(); }; struct B {}"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromB(source);"); + } + + [Fact] + public void MapNonNullableValueTypeToNullableValueType() + { + var source = TestSourceBuilder.Mapping("B", "A?", "struct A { public static A FromB(B source) => new(); }; struct B {}"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return (global::A?)global::A.FromB(source);"); + } + + [Fact] + public void MapNonNullableValueTypeToNullableValueTypeWithNullableMethodParameter() + { + var source = TestSourceBuilder.Mapping("B", "A?", "struct A { public static A FromB(B? source) => new(); }; struct B {}"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.FromB(source);"); + } + + [Fact] + public void CustomTypeWithStaticToTargetGenericMethod() + { + var source = TestSourceBuilder.Mapping("A", "List", "class A { public static List ToList(A source) => []; }"); + TestHelper.GenerateMapper(source).Should().HaveSingleMethodBody("return global::A.ToList(source);"); + } + + [Fact] + public void DateTimeToDateOnlyMappingDisabledShouldDiagnostic() + { + var source = TestSourceBuilder.Mapping( + "DateTime", + "DateOnly", + TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.DateTimeToDateOnly) + ); + TestHelper + .GenerateMapper(source, TestHelperOptions.AllowDiagnostics) + .Should() + .HaveDiagnostic(DiagnosticDescriptors.SourceMemberNotMapped) + .HaveAssertedAllDiagnostics(); + } + + [Fact] + public void DateTimeToTimeOnlyMappingDisabledShouldDiagnostic() + { + var source = TestSourceBuilder.Mapping( + "DateTime", + "TimeOnly", + TestSourceBuilderOptions.WithDisabledMappingConversion(MappingConversionType.DateTimeToTimeOnly) + ); + TestHelper + .GenerateMapper(source, TestHelperOptions.AllowDiagnostics) + .Should() + .HaveDiagnostic(DiagnosticDescriptors.SourceMemberNotMapped) + .HaveAssertedAllDiagnostics(); + } + + [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(); + } +}