diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/RuntimeTargetTypeMappingBodyBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/RuntimeTargetTypeMappingBodyBuilder.cs index 400dea53fb..c7bc8dc93c 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/RuntimeTargetTypeMappingBodyBuilder.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/RuntimeTargetTypeMappingBodyBuilder.cs @@ -50,21 +50,8 @@ public static void BuildMappingBody(MappingBuilderContext ctx, UserDefinedNewIns BuildMappingBody(ctx, mapping, mappings); } - private static IEnumerable GetUserMappingCandidates(MappingBuilderContext ctx) - { - foreach (var userMapping in ctx.UserMappings) - { - // exclude runtime target type - if (userMapping is UserDefinedNewInstanceRuntimeTargetTypeMapping) - continue; - - if (userMapping.CallableByOtherMappings) - yield return userMapping; - - if (userMapping is IDelegateUserMapping { DelegateMapping.CallableByOtherMappings: true } delegateUserMapping) - yield return delegateUserMapping.DelegateMapping; - } - } + private static IEnumerable GetUserMappingCandidates(MappingBuilderContext ctx) => + ctx.UserMappings.Where(x => x is not UserDefinedNewInstanceRuntimeTargetTypeMapping); private static void BuildMappingBody( MappingBuilderContext ctx, diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/UserMethodMappingBodyBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/UserMethodMappingBodyBuilder.cs index 0ab71eec89..2ed9bdb82e 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/UserMethodMappingBodyBuilder.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/UserMethodMappingBodyBuilder.cs @@ -28,10 +28,10 @@ public static void BuildMappingBody(MappingBuilderContext ctx, UserDefinedNewIns { var options = MappingBuildingOptions.KeepUserSymbol; - // this this mapping is not callable by others - // the delegate mapping is probably callable by others - // and therefore reusable - if (!mapping.CallableByOtherMappings) + // the delegate mapping is not embedded + // and is therefore reusable + // if embedded, only the original mapping is callable by others + if (mapping.InternalReferenceHandlingEnabled) { options |= MappingBuildingOptions.MarkAsReusable; } diff --git a/src/Riok.Mapperly/Descriptors/MappingCollection.cs b/src/Riok.Mapperly/Descriptors/MappingCollection.cs index 19bf58203b..56fd142c03 100644 --- a/src/Riok.Mapperly/Descriptors/MappingCollection.cs +++ b/src/Riok.Mapperly/Descriptors/MappingCollection.cs @@ -29,7 +29,7 @@ public class MappingCollection /// /// Queue of mappings which don't have the body built yet /// - private readonly PriorityQueue<(IMapping, MappingBuilderContext), MappingBodyBuildingPriority> _mappingsToBuildBody = new(); + private readonly Queue<(IMapping, MappingBuilderContext)> _mappingsToBuildBody = new(); /// /// All new instance mappings @@ -62,8 +62,7 @@ public class MappingCollection public IEnumerable<(IMapping, MappingBuilderContext)> DequeueMappingsToBuildBody() => _mappingsToBuildBody.DequeueAll(); - public void EnqueueToBuildBody(ITypeMapping mapping, MappingBuilderContext ctx) => - _mappingsToBuildBody.Enqueue((mapping, ctx), mapping.BodyBuildingPriority); + public void EnqueueToBuildBody(ITypeMapping mapping, MappingBuilderContext ctx) => _mappingsToBuildBody.Enqueue((mapping, ctx)); public MappingCollectionAddResult AddUserMapping(IUserMapping userMapping, bool ignoreDuplicates, string? name) { @@ -138,7 +137,8 @@ private class MappingCollectionInstance where TUserMapping : T, IUserMapping { /// - /// Callable mapping of each type pair + config. + /// Default mappings of each type pair + config. + /// A default mappings is the mapping Mapperly should use to convert from one type to another. /// Contains mappings to build and already built mappings. /// private readonly Dictionary _defaultMappings = new(); @@ -228,9 +228,6 @@ public void AddNamedUserMapping(string? name, TUserMapping mapping) public MappingCollectionAddResult TryAddAsDefault(T mapping, TypeMappingConfiguration config) { - if (!mapping.CallableByOtherMappings) - return MappingCollectionAddResult.NotAddedIgnored; - var mappingKey = new TypeMappingKey(mapping, config); if (_defaultMappings.ContainsKey(mappingKey)) return MappingCollectionAddResult.NotAddedDuplicated; @@ -241,9 +238,6 @@ public MappingCollectionAddResult TryAddAsDefault(T mapping, TypeMappingConfigur public MappingCollectionAddResult AddUserMapping(TUserMapping mapping, bool ignoreDuplicates, bool? isDefault, string? name) { - if (!mapping.CallableByOtherMappings) - return MappingCollectionAddResult.NotAddedIgnored; - AddNamedUserMapping(name, mapping); return isDefault switch diff --git a/src/Riok.Mapperly/Descriptors/Mappings/ExistingTarget/ExistingTargetMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/ExistingTarget/ExistingTargetMapping.cs index 4f2eeb6021..448286889a 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/ExistingTarget/ExistingTargetMapping.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/ExistingTarget/ExistingTargetMapping.cs @@ -24,11 +24,7 @@ protected ExistingTargetMapping(ITypeSymbol sourceType, ITypeSymbol targetType) public ITypeSymbol TargetType { get; } - public virtual bool CallableByOtherMappings => true; - public virtual bool IsSynthetic => false; - public MappingBodyBuildingPriority BodyBuildingPriority => MappingBodyBuildingPriority.Default; - public abstract IEnumerable Build(TypeMappingBuildContext ctx, ExpressionSyntax target); } diff --git a/src/Riok.Mapperly/Descriptors/Mappings/ExistingTarget/ObjectMemberExistingTargetMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/ExistingTarget/ObjectMemberExistingTargetMapping.cs index 2c66c4da64..6f6a2420ba 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/ExistingTarget/ObjectMemberExistingTargetMapping.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/ExistingTarget/ObjectMemberExistingTargetMapping.cs @@ -18,9 +18,5 @@ public class ObjectMemberExistingTargetMapping(ITypeSymbol sourceType, ITypeSymb public ITypeSymbol TargetType { get; } = targetType; - public bool CallableByOtherMappings => true; - public bool IsSynthetic => false; - - public MappingBodyBuildingPriority BodyBuildingPriority => MappingBodyBuildingPriority.Default; } diff --git a/src/Riok.Mapperly/Descriptors/Mappings/ITypeMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/ITypeMapping.cs index db81c08dbe..65506ac17a 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/ITypeMapping.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/ITypeMapping.cs @@ -5,16 +5,8 @@ namespace Riok.Mapperly.Descriptors.Mappings; /// public interface ITypeMapping : IMapping { - /// - /// Gets a value indicating if this mapping can be called / built by another mapping. - /// This should be true for most mappings. - /// - bool CallableByOtherMappings { get; } - /// /// Gets a value indicating whether this mapping produces any code or can be omitted completely (eg. direct assignments or delegate mappings). /// bool IsSynthetic { get; } - - MappingBodyBuildingPriority BodyBuildingPriority { get; } } diff --git a/src/Riok.Mapperly/Descriptors/Mappings/MappingBodyBuildingPriority.cs b/src/Riok.Mapperly/Descriptors/Mappings/MappingBodyBuildingPriority.cs deleted file mode 100644 index 0909605bc4..0000000000 --- a/src/Riok.Mapperly/Descriptors/Mappings/MappingBodyBuildingPriority.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Riok.Mapperly.Descriptors.Mappings; - -/// -/// When to build the body of this mapping. -/// The body of mappings with a higher priority -/// is built before the bodies of mappings with a lower priority. -/// -public enum MappingBodyBuildingPriority -{ - /// - /// Default mapping priority. - /// - Default, - - /// - /// Priority for mappings which require the body of user mappings to be built. - /// (Depend on the user mapping bodies). - /// - AfterUserMappings, -} diff --git a/src/Riok.Mapperly/Descriptors/Mappings/MethodMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/MethodMapping.cs index 0253330edf..4794796644 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/MethodMapping.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/MethodMapping.cs @@ -61,7 +61,7 @@ ITypeSymbol targetType protected bool IsExtensionMethod { get; } - private string MethodName => _methodName ?? throw new InvalidOperationException(); + protected string MethodName => _methodName ?? throw new InvalidOperationException(); protected MethodParameter SourceParameter { get; } @@ -71,12 +71,8 @@ ITypeSymbol targetType public ITypeSymbol TargetType { get; } - public virtual bool CallableByOtherMappings => true; - public bool IsSynthetic => false; - public virtual MappingBodyBuildingPriority BodyBuildingPriority => MappingBodyBuildingPriority.Default; - public virtual ExpressionSyntax Build(TypeMappingBuildContext ctx) => Invocation(MethodName, SourceParameter.WithArgument(ctx.Source), ReferenceHandlerParameter?.WithArgument(ctx.ReferenceHandler)); diff --git a/src/Riok.Mapperly/Descriptors/Mappings/NewInstanceMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/NewInstanceMapping.cs index 5030774871..4bcb6925f3 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/NewInstanceMapping.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/NewInstanceMapping.cs @@ -22,11 +22,6 @@ protected NewInstanceMapping(ITypeSymbol sourceType, ITypeSymbol targetType) public ITypeSymbol TargetType { get; } - public virtual MappingBodyBuildingPriority BodyBuildingPriority => MappingBodyBuildingPriority.Default; - - /// - public virtual bool CallableByOtherMappings => true; - /// public virtual bool IsSynthetic => false; diff --git a/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/IDelegateUserMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/IDelegateUserMapping.cs deleted file mode 100644 index 4533eb3c97..0000000000 --- a/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/IDelegateUserMapping.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Riok.Mapperly.Descriptors.Mappings.UserMappings; - -/// -/// A delegated user mapping. -/// -public interface IDelegateUserMapping : IUserMapping -{ - /// - /// Gets the delegate mapping or null if none is set (yet). - /// - INewInstanceMapping? DelegateMapping { get; } -} diff --git a/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedExistingTargetMethodMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedExistingTargetMethodMapping.cs index 02b0b31c58..48e9d20e67 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedExistingTargetMethodMapping.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedExistingTargetMethodMapping.cs @@ -18,29 +18,38 @@ public class UserDefinedExistingTargetMethodMapping( MethodParameter targetParameter, MethodParameter? referenceHandlerParameter, bool enableReferenceHandling -) : NewInstanceMethodMapping(method, sourceParameter, referenceHandlerParameter, targetParameter.Type), IExistingTargetUserMapping +) : MethodMapping(method, sourceParameter, referenceHandlerParameter, targetParameter.Type), IExistingTargetUserMapping { private IExistingTargetMapping? _delegateMapping; public IMethodSymbol Method { get; } = method; - /// - /// Always false, since is false - /// this can never be the default. - /// public bool? Default => false; private MethodParameter TargetParameter { get; } = targetParameter; - public override bool CallableByOtherMappings => false; + /// + /// The reference handling is enabled but is only internal to this method. + /// No reference handler parameter is passed. + /// + private bool InternalReferenceHandlingEnabled => enableReferenceHandling && ReferenceHandlerParameter == null; public void SetDelegateMapping(IExistingTargetMapping delegateMapping) => _delegateMapping = delegateMapping; public override ExpressionSyntax Build(TypeMappingBuildContext ctx) => throw new InvalidOperationException($"{nameof(UserDefinedExistingTargetMethodMapping)} does not support {nameof(Build)}"); - public IEnumerable Build(TypeMappingBuildContext ctx, ExpressionSyntax target) => - throw new InvalidOperationException($"{nameof(UserDefinedExistingTargetMethodMapping)} does not support {nameof(Build)}"); + public IEnumerable Build(TypeMappingBuildContext ctx, ExpressionSyntax target) + { + return ctx.SyntaxFactory.SingleStatement( + Invocation( + MethodName, + SourceParameter.WithArgument(ctx.Source), + TargetParameter.WithArgument(target), + ReferenceHandlerParameter?.WithArgument(ctx.ReferenceHandler) + ) + ); + } public override IEnumerable BuildBody(TypeMappingBuildContext ctx) { @@ -60,7 +69,7 @@ public override IEnumerable BuildBody(TypeMappingBuildContext c // if reference handling is enabled and no reference handler parameter is declared // a new reference handler is instantiated and used. - if (enableReferenceHandling && ReferenceHandlerParameter == null) + if (InternalReferenceHandlingEnabled) { // var refHandler = new RefHandler(); var referenceHandlerName = ctx.NameBuilder.New(DefaultReferenceHandlerParameterName); diff --git a/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedNewInstanceMethodMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedNewInstanceMethodMapping.cs index 4ac7a085bf..681e947234 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedNewInstanceMethodMapping.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedNewInstanceMethodMapping.cs @@ -15,7 +15,7 @@ public class UserDefinedNewInstanceMethodMapping( MethodParameter? referenceHandlerParameter, ITypeSymbol targetType, bool enableReferenceHandling -) : NewInstanceMethodMapping(method, sourceParameter, referenceHandlerParameter, targetType), IDelegateUserMapping, INewInstanceUserMapping +) : NewInstanceMethodMapping(method, sourceParameter, referenceHandlerParameter, targetType), INewInstanceUserMapping { public IMethodSymbol Method { get; } = method; @@ -23,19 +23,29 @@ bool enableReferenceHandling public INewInstanceMapping? DelegateMapping { get; private set; } + /// + /// The reference handling is enabled but is only internal to this method. + /// No reference handler parameter is passed. + /// + public bool InternalReferenceHandlingEnabled => enableReferenceHandling && ReferenceHandlerParameter == null; + public void SetDelegateMapping(INewInstanceMapping mapping) => DelegateMapping = mapping; + public override ExpressionSyntax Build(TypeMappingBuildContext ctx) + { + return InternalReferenceHandlingEnabled ? DelegateMapping?.Build(ctx) ?? base.Build(ctx) : base.Build(ctx); + } + public override IEnumerable BuildBody(TypeMappingBuildContext ctx) { if (DelegateMapping == null) { - return new[] { ctx.SyntaxFactory.ExpressionStatement(ctx.SyntaxFactory.ThrowMappingNotImplementedExceptionStatement()), }; + return new[] { ctx.SyntaxFactory.ExpressionStatement(ctx.SyntaxFactory.ThrowMappingNotImplementedExceptionStatement()) }; } - // if reference handling is enabled and no reference handler parameter is declared // the generated mapping method is called with a new reference handler instance // otherwise the generated method is embedded - if (enableReferenceHandling && ReferenceHandlerParameter == null) + if (InternalReferenceHandlingEnabled) { // new RefHandler(); var createRefHandler = ctx.SyntaxFactory.CreateInstance(); @@ -49,12 +59,6 @@ public override IEnumerable BuildBody(TypeMappingBuildContext c return new[] { ctx.SyntaxFactory.Return(DelegateMapping.Build(ctx)) }; } - /// - /// A is callable by other mappings - /// if either reference handling is not activated, or the user defined a reference handler parameter. - /// - public override bool CallableByOtherMappings => !enableReferenceHandling || ReferenceHandlerParameter != null; - internal override void EnableReferenceHandling(INamedTypeSymbol iReferenceHandlerType) { // the parameters of user defined methods should not be manipulated diff --git a/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedNewInstanceRuntimeTargetTypeMapping.cs b/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedNewInstanceRuntimeTargetTypeMapping.cs index febf526377..a27bbdc6ea 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedNewInstanceRuntimeTargetTypeMapping.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/UserMappings/UserDefinedNewInstanceRuntimeTargetTypeMapping.cs @@ -28,22 +28,19 @@ ITypeSymbol objectType private readonly List _mappings = new(); - // requires the user mapping bodies - // as the delegate mapping of user mappings is only set after bodies are built - // and if reference handling is enabled, - // but the user mapping does not have a reference handling parameter, - // only the delegate mapping is callable by other mappings. - public override MappingBodyBuildingPriority BodyBuildingPriority => MappingBodyBuildingPriority.AfterUserMappings; - public IMethodSymbol Method { get; } = method; /// - /// Always false, since is false + /// Always false, as this cannot be called by other mappings, /// this can never be the default. /// public bool? Default => false; - public override bool CallableByOtherMappings => false; + /// + /// The reference handling is enabled but is only internal to this method. + /// No reference handler parameter is passed. + /// + private bool InternalReferenceHandlingEnabled => enableReferenceHandling && ReferenceHandlerParameter == null; public void AddMappings(IEnumerable mappings) => _mappings.AddRange(mappings); @@ -51,7 +48,7 @@ public override IEnumerable BuildBody(TypeMappingBuildContext c { // if reference handling is enabled and no reference handler parameter is declared // a new reference handler is instantiated and used. - if (enableReferenceHandling && ReferenceHandlerParameter == null) + if (InternalReferenceHandlingEnabled) { // var refHandler = new RefHandler(); var referenceHandlerName = ctx.NameBuilder.New(DefaultReferenceHandlerParameterName); diff --git a/src/Riok.Mapperly/Helpers/PriorityQueueExtensions.cs b/src/Riok.Mapperly/Helpers/PriorityQueueExtensions.cs deleted file mode 100644 index 197fbdd28d..0000000000 --- a/src/Riok.Mapperly/Helpers/PriorityQueueExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Riok.Mapperly.Helpers; - -internal static class PriorityQueueExtensions -{ - /// - /// Dequeues all nodes. - /// The nodes with the highest priority are returned first ordered in a FIFO fashion. - /// Items added while this operation is in progress are also considered. - /// - /// An enumerable with all items. - public static IEnumerable DequeueAll(this PriorityQueue queue) - { - while (queue.TryDequeue(out var element, out _)) - { - yield return element; - } - } -} diff --git a/src/Riok.Mapperly/Helpers/QueueExtensions.cs b/src/Riok.Mapperly/Helpers/QueueExtensions.cs new file mode 100644 index 0000000000..cc8b59c22f --- /dev/null +++ b/src/Riok.Mapperly/Helpers/QueueExtensions.cs @@ -0,0 +1,17 @@ +namespace Riok.Mapperly.Helpers; + +internal static class QueueExtensions +{ + /// + /// Dequeues all nodes. + /// Items added while this operation is in progress are also considered. + /// + /// An enumerable with all items. + public static IEnumerable DequeueAll(this Queue queue) + { + while (queue.TryDequeue(out var element)) + { + yield return element; + } + } +} diff --git a/test/Riok.Mapperly.Tests/Mapping/ReferenceHandlingTest.cs b/test/Riok.Mapperly.Tests/Mapping/ReferenceHandlingTest.cs index d27dd5cc58..b3bcce68be 100644 --- a/test/Riok.Mapperly.Tests/Mapping/ReferenceHandlingTest.cs +++ b/test/Riok.Mapperly.Tests/Mapping/ReferenceHandlingTest.cs @@ -25,8 +25,10 @@ public Task ShouldWork() public Task ManuallyMappedPropertiesShouldWork() { var source = TestSourceBuilder.MapperWithBodyAndTypes( - "[MapProperty(\"Value\", \"MyValue\")] private partial B MapToB(A source);" - + "[MapProperty(\"Value\", \"MyValue2\")] private partial B MapToB1(A source);", + """ + [MapProperty("Value", "MyValue")] private partial B MapToB(A source); + [MapProperty("Value", "MyValue2")] private partial B MapToB1(A source); + """, TestSourceBuilderOptions.WithReferenceHandling, "class A { public A Parent { get; set; } public C Value { get; set; } }", "class B { public B Parent { get; set; } public D MyValue { get; set; } public D MyValue2 { get; set; } }", @@ -278,4 +280,26 @@ public Task UserImplementedWithReferenceHandlerShouldWork() return TestHelper.VerifyGenerator(source); } + + [Fact] + public Task MultipleUserDefinedWithSpecifiedDefault() + { + var source = TestSourceBuilder.MapperWithBodyAndTypes( + """ + public partial B Map(A a); + private partial D MapD(C source); + [MapperIgnoreSource("IntValue")] + [MapperIgnoreTarget("IntValue")] + [UserMapping(Default = true)] + private partial D MapDIgnore(C source); + """, + TestSourceBuilderOptions.WithReferenceHandling, + "record A(C Value);", + "record B(D Value);", + "record C(string StringValue, int IntValue);", + "record D(string StringValue) { public int IntValue { get; set; } };" + ); + + return TestHelper.VerifyGenerator(source); + } } diff --git a/test/Riok.Mapperly.Tests/Mapping/UserMethodTest.cs b/test/Riok.Mapperly.Tests/Mapping/UserMethodTest.cs index 0ee7eccd68..7359f9a8ee 100644 --- a/test/Riok.Mapperly.Tests/Mapping/UserMethodTest.cs +++ b/test/Riok.Mapperly.Tests/Mapping/UserMethodTest.cs @@ -817,6 +817,4 @@ public static D BaseMapping(C source) ); return TestHelper.VerifyGenerator(source); } - - // TODO test interface methods should not be discovered } diff --git a/test/Riok.Mapperly.Tests/_snapshots/DictionaryTest.DictionaryShouldReuseForReadOnlyDictionaryImplementorsButDifferentForIDictionary#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/DictionaryTest.DictionaryShouldReuseForReadOnlyDictionaryImplementorsButDifferentForIDictionary#Mapper.g.verified.cs index 92de358abb..0fb07a68d9 100644 --- a/test/Riok.Mapperly.Tests/_snapshots/DictionaryTest.DictionaryShouldReuseForReadOnlyDictionaryImplementorsButDifferentForIDictionary#Mapper.g.verified.cs +++ b/test/Riok.Mapperly.Tests/_snapshots/DictionaryTest.DictionaryShouldReuseForReadOnlyDictionaryImplementorsButDifferentForIDictionary#Mapper.g.verified.cs @@ -27,7 +27,7 @@ public partial class Mapper [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] public partial global::BCustomDictionary Map(global::ADictionary source) { - var target = new global::BCustomDictionary(MapToCustomDictionary1(source.Values)); + var target = new global::BCustomDictionary(MapToCustomDictionary(source.Values)); return target; } @@ -48,7 +48,7 @@ public partial class Mapper [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] public partial global::BCustomDictionary Map(global::ACustomReadOnlyDictionary source) { - var target = new global::BCustomDictionary(MapToCustomDictionary1(source.Values)); + var target = new global::BCustomDictionary(MapToCustomDictionary(source.Values)); return target; } @@ -62,7 +62,7 @@ public partial class Mapper [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] public partial global::BCustomDictionary Map(global::ACustomDictionary source) { - var target = new global::BCustomDictionary(MapToCustomDictionary(source.Values)); + var target = new global::BCustomDictionary(MapToCustomDictionary1(source.Values)); return target; } @@ -85,7 +85,7 @@ public partial class Mapper } [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] - private global::CustomDictionary MapToCustomDictionary(global::System.Collections.Generic.IDictionary source) + private global::CustomDictionary MapToCustomDictionary(global::System.Collections.Generic.IReadOnlyDictionary source) { var target = new global::CustomDictionary(); var targetDict = (global::System.Collections.Generic.IDictionary)target; @@ -97,7 +97,7 @@ public partial class Mapper } [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] - private global::CustomDictionary MapToCustomDictionary1(global::System.Collections.Generic.IReadOnlyDictionary source) + private global::CustomDictionary MapToCustomDictionary1(global::System.Collections.Generic.IDictionary source) { var target = new global::CustomDictionary(); var targetDict = (global::System.Collections.Generic.IDictionary)target; diff --git a/test/Riok.Mapperly.Tests/_snapshots/EnumerableTest.EnumerableShouldReuseForReadOnlyCollectionImplementorsButDifferentForICollection#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/EnumerableTest.EnumerableShouldReuseForReadOnlyCollectionImplementorsButDifferentForICollection#Mapper.g.verified.cs index 4a87e7cbff..837405e431 100644 --- a/test/Riok.Mapperly.Tests/_snapshots/EnumerableTest.EnumerableShouldReuseForReadOnlyCollectionImplementorsButDifferentForICollection#Mapper.g.verified.cs +++ b/test/Riok.Mapperly.Tests/_snapshots/EnumerableTest.EnumerableShouldReuseForReadOnlyCollectionImplementorsButDifferentForICollection#Mapper.g.verified.cs @@ -27,7 +27,7 @@ public partial class Mapper [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] public partial global::BCustomCollection Map(global::AList source) { - var target = new global::BCustomCollection(MapToCustomCollection1(source.Values)); + var target = new global::BCustomCollection(MapToCustomCollection(source.Values)); return target; } @@ -48,7 +48,7 @@ public partial class Mapper [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] public partial global::BCustomCollection Map(global::ACustomReadOnlyCollection source) { - var target = new global::BCustomCollection(MapToCustomCollection1(source.Values)); + var target = new global::BCustomCollection(MapToCustomCollection(source.Values)); return target; } @@ -62,7 +62,7 @@ public partial class Mapper [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] public partial global::BCustomCollection Map(global::ACustomCollection source) { - var target = new global::BCustomCollection(MapToCustomCollection(source.Values)); + var target = new global::BCustomCollection(MapToCustomCollection1(source.Values)); return target; } @@ -85,20 +85,22 @@ public partial class Mapper } [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] - private global::CustomCollection MapToCustomCollection(global::System.Collections.Generic.ICollection source) + private global::D[] MapToDArray(global::System.Collections.Generic.IReadOnlyCollection source) { - var target = new global::CustomCollection(); + var target = new global::D[source.Count]; + var i = 0; foreach (var item in source) { - target.Add(MapToD(item)); + target[i] = MapToD(item); + i++; } return target; } [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] - private global::System.Collections.Generic.List MapToList1(global::System.Collections.Generic.ICollection source) + private global::CustomCollection MapToCustomCollection(global::System.Collections.Generic.IReadOnlyCollection source) { - var target = new global::System.Collections.Generic.List(source.Count); + var target = new global::CustomCollection(); foreach (var item in source) { target.Add(MapToD(item)); @@ -107,9 +109,9 @@ public partial class Mapper } [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] - private global::CustomCollection MapToCustomCollection1(global::System.Collections.Generic.IReadOnlyCollection source) + private global::System.Collections.Generic.List MapToList1(global::System.Collections.Generic.ICollection source) { - var target = new global::CustomCollection(); + var target = new global::System.Collections.Generic.List(source.Count); foreach (var item in source) { target.Add(MapToD(item)); @@ -118,14 +120,12 @@ public partial class Mapper } [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] - private global::D[] MapToDArray(global::System.Collections.Generic.IReadOnlyCollection source) + private global::CustomCollection MapToCustomCollection1(global::System.Collections.Generic.ICollection source) { - var target = new global::D[source.Count]; - var i = 0; + var target = new global::CustomCollection(); foreach (var item in source) { - target[i] = MapToD(item); - i++; + target.Add(MapToD(item)); } return target; } diff --git a/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.ManuallyMappedPropertiesShouldWork.verified.txt b/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.ManuallyMappedPropertiesShouldWork.verified.txt index 7b52ee5146..53e9d021c2 100644 --- a/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.ManuallyMappedPropertiesShouldWork.verified.txt +++ b/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.ManuallyMappedPropertiesShouldWork.verified.txt @@ -15,10 +15,20 @@ Title: Source member was not found for target member, Severity: Info, WarningLevel: 1, - Location: : (11,73)-(11,144), + Location: : (12,0)-(12,71), MessageFormat: The member {0} on the mapping target type {1} was not found on the mapping source type {2}, Message: The member MyValue on the mapping target type B was not found on the mapping source type A, Category: Mapper + }, + { + Id: RMG060, + Title: Multiple user mappings discovered without specifying an explicit default, + Severity: Info, + WarningLevel: 1, + Location: : (12,0)-(12,71), + MessageFormat: Multiple user mappings discovered for the mapping from {0} to {1} without specifying an explicit default, + Message: Multiple user mappings discovered for the mapping from A to B without specifying an explicit default, + Category: Mapper } ] } \ No newline at end of file diff --git a/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.MultipleUserDefinedWithSpecifiedDefault#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.MultipleUserDefinedWithSpecifiedDefault#Mapper.g.verified.cs new file mode 100644 index 0000000000..9d24622ee5 --- /dev/null +++ b/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.MultipleUserDefinedWithSpecifiedDefault#Mapper.g.verified.cs @@ -0,0 +1,54 @@ +//HintName: Mapper.g.cs +// +#nullable enable +public partial class Mapper +{ + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + public partial global::B Map(global::A a) + { + return MapToB(a, new global::Riok.Mapperly.Abstractions.ReferenceHandling.PreserveReferenceHandler()); + } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private partial global::D MapD(global::C source) + { + return MapToD(source, new global::Riok.Mapperly.Abstractions.ReferenceHandling.PreserveReferenceHandler()); + } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private partial global::D MapDIgnore(global::C source) + { + return MapToD1(source, new global::Riok.Mapperly.Abstractions.ReferenceHandling.PreserveReferenceHandler()); + } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private global::B MapToB(global::A source, global::Riok.Mapperly.Abstractions.ReferenceHandling.IReferenceHandler refHandler) + { + if (refHandler.TryGetReference(source, out var existingTargetReference)) + return existingTargetReference; + var target = new global::B(MapToD1(source.Value, refHandler)); + refHandler.SetReference(source, target); + return target; + } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private global::D MapToD(global::C source, global::Riok.Mapperly.Abstractions.ReferenceHandling.IReferenceHandler refHandler) + { + if (refHandler.TryGetReference(source, out var existingTargetReference)) + return existingTargetReference; + var target = new global::D(source.StringValue); + refHandler.SetReference(source, target); + target.IntValue = source.IntValue; + return target; + } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private global::D MapToD1(global::C source, global::Riok.Mapperly.Abstractions.ReferenceHandling.IReferenceHandler refHandler) + { + if (refHandler.TryGetReference(source, out var existingTargetReference)) + return existingTargetReference; + var target = new global::D(source.StringValue); + refHandler.SetReference(source, target); + return target; + } +} \ No newline at end of file