diff --git a/docs/docs/02-configuration/02-static-mappers.md b/docs/docs/02-configuration/02-static-mappers.md index b3031a2660..939fba9871 100644 --- a/docs/docs/02-configuration/02-static-mappers.md +++ b/docs/docs/02-configuration/02-static-mappers.md @@ -8,7 +8,7 @@ public static partial class CarMapper { public static partial CarDto CarToCarDto(this Car car); - private static DateOnly DateTimeToDateOnly(DateTime dt) => DateOnly.FromDateTime(dt); + private static int TimeSpanToHours(TimeSpan t) => t.Hours; } ``` diff --git a/docs/docs/02-configuration/06-user-implemented-methods.md b/docs/docs/02-configuration/06-user-implemented-methods.md index 5935595507..1d69f51b14 100644 --- a/docs/docs/02-configuration/06-user-implemented-methods.md +++ b/docs/docs/02-configuration/06-user-implemented-methods.md @@ -8,8 +8,8 @@ public partial class CarMapper { public partial CarDto CarToCarDto(Car car); - private DateOnly DateTimeToDateOnly(DateTime dt) => DateOnly.FromDateTime(dt); + private int TimeSpanToHours(TimeSpan t) => t.Hours; } ``` -Whenever Mapperly needs a mapping from `DateTime` to `DateOnly` inside the `CarMapper` implementation, it will use the provided implementation. +Whenever Mapperly needs a mapping from `TimeSpan` to `int` inside the `CarMapper` implementation, it will use the provided implementation. diff --git a/docs/docs/02-configuration/11-conversions.md b/docs/docs/02-configuration/11-conversions.md index 705428153e..25746e1058 100644 --- a/docs/docs/02-configuration/11-conversions.md +++ b/docs/docs/02-configuration/11-conversions.md @@ -2,20 +2,22 @@ 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` | -| 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<>` | -| 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 source 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` | -| 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` | +| 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<>` | +| 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 source 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 | ## Disable all automatic conversions diff --git a/src/Riok.Mapperly.Abstractions/MappingConversionType.cs b/src/Riok.Mapperly.Abstractions/MappingConversionType.cs index 17b4da9b02..4c7839e233 100644 --- a/src/Riok.Mapperly.Abstractions/MappingConversionType.cs +++ b/src/Riok.Mapperly.Abstractions/MappingConversionType.cs @@ -62,6 +62,20 @@ public enum MappingConversionType /// EnumToEnum = 1 << 7, + /// + /// If the source is a + /// 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 + /// uses the `FromDateTime` method on the target type with the source as single parameter. + /// + DateTimeToTimeOnly = 1 << 9, + /// /// Enables all supported conversions. /// diff --git a/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt b/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt index c9377fa143..a310d7a666 100644 --- a/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt +++ b/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt @@ -50,6 +50,8 @@ Riok.Mapperly.Abstractions.PropertyNameMappingStrategy.CaseSensitive = 0 -> Riok Riok.Mapperly.Abstractions.MappingConversionType Riok.Mapperly.Abstractions.MappingConversionType.All = -1 -> Riok.Mapperly.Abstractions.MappingConversionType Riok.Mapperly.Abstractions.MappingConversionType.Constructor = 1 -> Riok.Mapperly.Abstractions.MappingConversionType +Riok.Mapperly.Abstractions.MappingConversionType.DateTimeToDateOnly = 256 -> Riok.Mapperly.Abstractions.MappingConversionType +Riok.Mapperly.Abstractions.MappingConversionType.DateTimeToTimeOnly = 512 -> Riok.Mapperly.Abstractions.MappingConversionType Riok.Mapperly.Abstractions.MappingConversionType.EnumToEnum = 128 -> Riok.Mapperly.Abstractions.MappingConversionType Riok.Mapperly.Abstractions.MappingConversionType.EnumToString = 64 -> Riok.Mapperly.Abstractions.MappingConversionType Riok.Mapperly.Abstractions.MappingConversionType.ExplicitCast = 4 -> Riok.Mapperly.Abstractions.MappingConversionType diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToDateOnlyMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToDateOnlyMappingBuilder.cs new file mode 100644 index 0000000000..d015e1bb82 --- /dev/null +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToDateOnlyMappingBuilder.cs @@ -0,0 +1,36 @@ +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 new file mode 100644 index 0000000000..4092aaeaf2 --- /dev/null +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/DateTimeToTimeOnlyMappingBuilder.cs @@ -0,0 +1,36 @@ +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 de88df2aa8..d7f75345b6 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBuilders/MappingBuilder.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/MappingBuilder.cs @@ -20,6 +20,8 @@ public class MappingBuilder StringToEnumMappingBuilder.TryBuildMapping, EnumToStringMappingBuilder.TryBuildMapping, EnumMappingBuilder.TryBuildMapping, + DateTimeToDateOnlyMappingBuilder.TryBuildMapping, + DateTimeToTimeOnlyMappingBuilder.TryBuildMapping, ExplicitCastMappingBuilder.TryBuildMapping, ToStringMappingBuilder.TryBuildMapping, NewInstanceObjectPropertyMappingBuilder.TryBuildMapping, diff --git a/src/Riok.Mapperly/Descriptors/WellKnownTypes.cs b/src/Riok.Mapperly/Descriptors/WellKnownTypes.cs index 4c27d30790..3c3ab48166 100644 --- a/src/Riok.Mapperly/Descriptors/WellKnownTypes.cs +++ b/src/Riok.Mapperly/Descriptors/WellKnownTypes.cs @@ -30,6 +30,9 @@ public class WellKnownTypes private INamedTypeSymbol? _keyValuePair; private INamedTypeSymbol? _dictionary; + private INamedTypeSymbol? _dateOnly; + private INamedTypeSymbol? _timeOnly; + internal WellKnownTypes(Compilation compilation) { _compilation = compilation; @@ -52,8 +55,13 @@ internal WellKnownTypes(Compilation compilation) public INamedTypeSymbol IReadOnlyList => _iReadOnlyList ??= GetTypeSymbol(typeof(IReadOnlyList<>)); public INamedTypeSymbol KeyValuePair => _keyValuePair ??= GetTypeSymbol(typeof(KeyValuePair<,>)); public INamedTypeSymbol Dictionary => _dictionary ??= GetTypeSymbol(typeof(Dictionary<,>)); + public INamedTypeSymbol? DateOnly => _dateOnly ??= GetTypeSymbol("System.DateOnly"); + public INamedTypeSymbol? TimeOnly => _timeOnly ??= GetTypeSymbol("System.TimeOnly"); private INamedTypeSymbol GetTypeSymbol(Type type) => _compilation.GetTypeByMetadataName(type.FullName ?? throw new InvalidOperationException("Could not get name of type " + type)) ?? throw new InvalidOperationException("Could not get type " + type.FullName); + + private INamedTypeSymbol? GetTypeSymbol(string typeFullName) + => _compilation.GetTypeByMetadataName(typeFullName); } diff --git a/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs b/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs index f44fa69e5d..75d853b779 100644 --- a/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs +++ b/test/Riok.Mapperly.IntegrationTests/BaseMapperTest.cs @@ -1,6 +1,10 @@ +using System; using System.IO; using System.Runtime.CompilerServices; using Riok.Mapperly.IntegrationTests.Dto; +#if !NET6_0_OR_GREATER +using Riok.Mapperly.IntegrationTests.Helpers; +#endif using Riok.Mapperly.IntegrationTests.Models; using VerifyTests; using VerifyXunit; @@ -11,6 +15,16 @@ public abstract class BaseMapperTest { static BaseMapperTest() { +#if !NET6_0_OR_GREATER + VerifierSettings.AddExtraSettings(settings => + { + settings.Converters.Add(new PortableDateOnlyConverter()); + settings.Converters.Add(new PortableTimeOnlyConverter()); + }); +#endif + + VerifierSettings.DontScrubDateTimes(); + Verifier.DerivePathInfo((file, _, type, method) => new PathInfo(Path.Combine(Path.GetDirectoryName(file)!, "_snapshots"), type.Name, method.Name)); } @@ -40,6 +54,8 @@ protected TestObject NewTestObj() SubObject = new InheritanceSubObject { BaseIntValue = 1, SubIntValue = 2, }, EnumRawValue = TestEnum.Value20, EnumStringValue = TestEnum.Value30, + DateTimeValueTargetDateOnly = new DateTime(2020, 1, 3, 15, 10, 5, DateTimeKind.Utc), + DateTimeValueTargetTimeOnly = new DateTime(2020, 1, 3, 15, 10, 5, DateTimeKind.Utc), 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 945c3c5d6b..2ab27c58f6 100644 --- a/test/Riok.Mapperly.IntegrationTests/Dto/TestObjectDto.cs +++ b/test/Riok.Mapperly.IntegrationTests/Dto/TestObjectDto.cs @@ -1,3 +1,4 @@ +using System; using Riok.Mapperly.IntegrationTests.Models; namespace Riok.Mapperly.IntegrationTests.Dto @@ -64,5 +65,9 @@ public TestObjectDto(int ctorValue, int unknownValue = 10, int ctorValue2 = 100) public string? IgnoredStringValue { get; set; } public int IgnoredIntValue { get; set; } + + public DateOnly DateTimeValueTargetDateOnly { get; set; } + + public TimeOnly DateTimeValueTargetTimeOnly { get; set; } } } diff --git a/test/Riok.Mapperly.IntegrationTests/Helpers/PortableDateOnlyConverter.cs b/test/Riok.Mapperly.IntegrationTests/Helpers/PortableDateOnlyConverter.cs new file mode 100644 index 0000000000..d7ea8afa98 --- /dev/null +++ b/test/Riok.Mapperly.IntegrationTests/Helpers/PortableDateOnlyConverter.cs @@ -0,0 +1,19 @@ +#if !NET6_0_OR_GREATER +using System; +using System.Globalization; +using VerifyTests; + +namespace Riok.Mapperly.IntegrationTests.Helpers +{ + /// + /// Portable converter for VerifyTests to ensure consistent json output across different net versions. + /// Is necessary to handle net framework tests which include per nuget package. + /// + public class PortableDateOnlyConverter : WriteOnlyJsonConverter + { + public override void Write(VerifyJsonWriter writer, DateOnly value) => + // copied format from https://github.com/VerifyTests/Verify/blob/19.7.1/src/Verify/Serialization/VerifierSettings.cs#L58 + writer.WriteRawValue(value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); + } +} +#endif diff --git a/test/Riok.Mapperly.IntegrationTests/Helpers/PortableTimeOnlyConverter.cs b/test/Riok.Mapperly.IntegrationTests/Helpers/PortableTimeOnlyConverter.cs new file mode 100644 index 0000000000..11c079e207 --- /dev/null +++ b/test/Riok.Mapperly.IntegrationTests/Helpers/PortableTimeOnlyConverter.cs @@ -0,0 +1,20 @@ +#if !NET6_0_OR_GREATER +using System; +using System.Globalization; +using VerifyTests; + + +namespace Riok.Mapperly.IntegrationTests.Helpers +{ + /// + /// Portable converter for VerifyTests to ensure consistent json output across different net versions. + /// Is necessary to handle net framework tests which include per nuget package. + /// + public class PortableTimeOnlyConverter : WriteOnlyJsonConverter + { + public override void Write(VerifyJsonWriter writer, TimeOnly value) => + // copied format from https://github.com/VerifyTests/Verify/blob/19.7.1/src/Verify/Serialization/VerifierSettings.cs#L65 + writer.WriteRawValue(value.ToString("h:mm tt", CultureInfo.InvariantCulture)); + } +} +#endif diff --git a/test/Riok.Mapperly.IntegrationTests/Mapper/StaticTestMapper.cs b/test/Riok.Mapperly.IntegrationTests/Mapper/StaticTestMapper.cs index b3c9ac741e..891cd2d54a 100644 --- a/test/Riok.Mapperly.IntegrationTests/Mapper/StaticTestMapper.cs +++ b/test/Riok.Mapperly.IntegrationTests/Mapper/StaticTestMapper.cs @@ -53,6 +53,8 @@ public static TestObjectDto MapToDto(TestObject src) // disable obsolete warning, as the obsolete attribute should still be tested. #pragma warning disable CS0618 [MapperIgnore(nameof(TestObject.IgnoredStringValue))] + [MapperIgnore(nameof(TestObjectDto.DateTimeValueTargetDateOnly))] + [MapperIgnore(nameof(TestObjectDto.DateTimeValueTargetTimeOnly))] #pragma warning restore CS0618 [MapperIgnoreTarget(nameof(TestObject.IgnoredIntValue))] [MapperIgnoreSource(nameof(TestObjectDto.IgnoredIntValue))] diff --git a/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs b/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs index e834f425a6..d2c9d6c114 100644 --- a/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs +++ b/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs @@ -50,6 +50,8 @@ public TestObjectDto MapToDto(TestObject src) // disable obsolete warning, as the obsolete attribute should still be tested. #pragma warning disable CS0618 [MapperIgnore(nameof(TestObject.IgnoredStringValue))] + [MapperIgnore(nameof(TestObjectDto.DateTimeValueTargetDateOnly))] + [MapperIgnore(nameof(TestObjectDto.DateTimeValueTargetTimeOnly))] #pragma warning restore CS0618 [MapperIgnoreTarget(nameof(TestObject.IgnoredIntValue))] [MapperIgnoreSource(nameof(TestObjectDto.IgnoredIntValue))] diff --git a/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs b/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs index 8953f9c25f..b3d934fb4b 100644 --- a/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs +++ b/test/Riok.Mapperly.IntegrationTests/Models/TestObject.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace Riok.Mapperly.IntegrationTests.Models @@ -63,5 +64,9 @@ public TestObject(int ctorValue, int unknownValue = 10, int ctorValue2 = 100) public string? IgnoredStringValue { get; set; } public int IgnoredIntValue { get; set; } + + public DateTime DateTimeValueTargetDateOnly { get; set; } + + public DateTime DateTimeValueTargetTimeOnly { get; set; } } } diff --git a/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj b/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj index 3b87301cfc..39c1c1acff 100644 --- a/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj +++ b/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj @@ -29,4 +29,8 @@ + + + + diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork.verified.txt index 075341533d..9618e0d2cc 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.RunMappingShouldWork.verified.txt @@ -60,5 +60,7 @@ SubObject: { SubIntValue: 2, BaseIntValue: 1 - } + }, + DateTimeValueTargetDateOnly: 2020-01-03, + DateTimeValueTargetTimeOnly: 3:10 PM } \ No newline at end of file diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource.verified.cs index dad94a5082..88beea0ff4 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/MapperTest.SnapshotGeneratedSource.verified.cs @@ -100,6 +100,8 @@ private partial Riok.Mapperly.IntegrationTests.Dto.TestObjectDto MapToDtoInterna target.EnumRawValue = (byte)testObject.EnumRawValue; target.EnumStringValue = MapToString(testObject.EnumStringValue); target.EnumReverseStringValue = MapToTestEnumDtoByValue(testObject.EnumReverseStringValue); + target.DateTimeValueTargetDateOnly = System.DateOnly.FromDateTime(testObject.DateTimeValueTargetDateOnly); + target.DateTimeValueTargetTimeOnly = System.TimeOnly.FromDateTime(testObject.DateTimeValueTargetTimeOnly); return target; } @@ -207,6 +209,8 @@ public partial void UpdateDto(Riok.Mapperly.IntegrationTests.Models.TestObject s target.EnumStringValue = MapToString(source.EnumStringValue); target.EnumReverseStringValue = MapToTestEnumDtoByValue(source.EnumReverseStringValue); target.IgnoredStringValue = source.IgnoredStringValue; + target.DateTimeValueTargetDateOnly = System.DateOnly.FromDateTime(source.DateTimeValueTargetDateOnly); + target.DateTimeValueTargetTimeOnly = System.TimeOnly.FromDateTime(source.DateTimeValueTargetTimeOnly); } private partial int PrivateDirectInt(int value) diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt index e468822e05..3d4586103b 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt @@ -56,5 +56,7 @@ SubIntValue: 2, BaseIntValue: 1 }, - IgnoredStringValue: ignored + IgnoredStringValue: ignored, + DateTimeValueTargetDateOnly: 2020-01-03, + DateTimeValueTargetTimeOnly: 3:10 PM } \ No newline at end of file diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt index 67f71f36f7..e31744e1f7 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt @@ -60,5 +60,7 @@ SubObject: { SubIntValue: 2, BaseIntValue: 1 - } + }, + DateTimeValueTargetDateOnly: 2020-01-03, + DateTimeValueTargetTimeOnly: 3:10 PM } \ No newline at end of file diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs index d9f95c293e..a6ce7549a3 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs @@ -93,6 +93,8 @@ public static partial Riok.Mapperly.IntegrationTests.Dto.TestObjectDto MapToDtoE target.EnumStringValue = MapToString(src.EnumStringValue); target.EnumReverseStringValue = MapToTestEnumDtoByValue(src.EnumReverseStringValue); target.IgnoredStringValue = src.IgnoredStringValue; + target.DateTimeValueTargetDateOnly = System.DateOnly.FromDateTime(src.DateTimeValueTargetDateOnly); + target.DateTimeValueTargetTimeOnly = System.TimeOnly.FromDateTime(src.DateTimeValueTargetTimeOnly); return target; } @@ -153,6 +155,8 @@ private static partial Riok.Mapperly.IntegrationTests.Dto.TestObjectDto MapToDto target.EnumRawValue = (byte)testObject.EnumRawValue; target.EnumStringValue = MapToString(testObject.EnumStringValue); target.EnumReverseStringValue = MapToTestEnumDtoByValue(testObject.EnumReverseStringValue); + target.DateTimeValueTargetDateOnly = System.DateOnly.FromDateTime(testObject.DateTimeValueTargetDateOnly); + target.DateTimeValueTargetTimeOnly = System.TimeOnly.FromDateTime(testObject.DateTimeValueTargetTimeOnly); return target; } @@ -260,6 +264,8 @@ public static partial void UpdateDto(Riok.Mapperly.IntegrationTests.Models.TestO target.EnumStringValue = MapToString(source.EnumStringValue); target.EnumReverseStringValue = MapToTestEnumDtoByValue(source.EnumReverseStringValue); target.IgnoredStringValue = source.IgnoredStringValue; + target.DateTimeValueTargetDateOnly = System.DateOnly.FromDateTime(source.DateTimeValueTargetDateOnly); + target.DateTimeValueTargetTimeOnly = System.TimeOnly.FromDateTime(source.DateTimeValueTargetTimeOnly); } private static partial int PrivateDirectInt(int value) diff --git a/test/Riok.Mapperly.Tests/Mapping/DateTimeTest.cs b/test/Riok.Mapperly.Tests/Mapping/DateTimeTest.cs new file mode 100644 index 0000000000..eb4692eb25 --- /dev/null +++ b/test/Riok.Mapperly.Tests/Mapping/DateTimeTest.cs @@ -0,0 +1,27 @@ +namespace Riok.Mapperly.Tests.Mapping; + +[UsesVerify] +public class DateTimeTest +{ + [Fact] + public void DateTimeToDateOnly() + { + var source = TestSourceBuilder.Mapping( + "DateTime", + "DateOnly"); + TestHelper.GenerateMapper(source) + .Should() + .HaveSingleMethodBody("return System.DateOnly.FromDateTime(source);"); + } + + [Fact] + public void DateTimeToTimeOnly() + { + var source = TestSourceBuilder.Mapping( + "DateTime", + "TimeOnly"); + TestHelper.GenerateMapper(source) + .Should() + .HaveSingleMethodBody("return System.TimeOnly.FromDateTime(source);"); + } +}