Skip to content

Commit 1911ef7

Browse files
committed
Support for configuring multiple enum pairings in one call / Erroring if invalid or conflicting multiple enum pairings specified
1 parent 13a8ff5 commit 1911ef7

File tree

3 files changed

+202
-18
lines changed

3 files changed

+202
-18
lines changed

AgileMapper.UnitTests/Configuration/WhenConfiguringEnumMapping.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,84 @@ public void ShouldErrorIfSameEnumTypesSpecified()
8787

8888
enumMappingEx.Message.ShouldContain("different enum types");
8989
}
90+
91+
[Fact]
92+
public void ShouldErrorIfNoSourceEnumMembersSpecified()
93+
{
94+
var enumMappingEx = Should.Throw<MappingConfigurationException>(() =>
95+
{
96+
using (var mapper = Mapper.CreateNew())
97+
{
98+
mapper.WhenMapping
99+
.PairEnums<PaymentTypeUk>()
100+
.With(PaymentTypeUs.Check);
101+
}
102+
});
103+
104+
enumMappingEx.Message.ShouldContain("Source enum members");
105+
}
106+
107+
[Fact]
108+
public void ShouldErrorIfNoTargetEnumMembersSpecified()
109+
{
110+
var enumMappingEx = Should.Throw<MappingConfigurationException>(() =>
111+
{
112+
using (var mapper = Mapper.CreateNew())
113+
{
114+
mapper.WhenMapping
115+
.PairEnums(PaymentTypeUk.Cheque)
116+
.With<PaymentTypeUs>();
117+
}
118+
});
119+
120+
enumMappingEx.Message.ShouldContain("Target enum members");
121+
}
122+
123+
[Fact]
124+
public void ShouldErrorIfDifferentNumbersOfEnumMembersSpecified()
125+
{
126+
var enumMappingEx = Should.Throw<MappingConfigurationException>(() =>
127+
{
128+
using (var mapper = Mapper.CreateNew())
129+
{
130+
mapper.WhenMapping
131+
.PairEnums(PaymentTypeUk.Cheque, PaymentTypeUk.Card, PaymentTypeUk.Cash)
132+
.With(PaymentTypeUs.Check, PaymentTypeUs.Cash);
133+
}
134+
});
135+
136+
enumMappingEx.Message.ShouldContain("same number of first and second enum values");
137+
}
138+
139+
[Fact]
140+
public void ShouldErrorIfSourceEnumMemberConflictingPairSpecified()
141+
{
142+
var enumMappingEx = Should.Throw<MappingConfigurationException>(() =>
143+
{
144+
using (var mapper = Mapper.CreateNew())
145+
{
146+
mapper.WhenMapping.PairEnum(PaymentTypeUk.Cheque).With(PaymentTypeUs.Check);
147+
mapper.WhenMapping.PairEnum(PaymentTypeUk.Cheque).With(PaymentTypeUs.Card);
148+
}
149+
});
150+
151+
enumMappingEx.Message.ShouldContain("Cheque is already paired with PaymentTypeUs.Check");
152+
}
153+
154+
[Fact]
155+
public void ShouldErrorIfTargetEnumMemberConflictingPairSpecified()
156+
{
157+
var enumMappingEx = Should.Throw<MappingConfigurationException>(() =>
158+
{
159+
using (var mapper = Mapper.CreateNew())
160+
{
161+
mapper.WhenMapping
162+
.PairEnums(PaymentTypeUk.Cheque, PaymentTypeUk.Cash)
163+
.With(PaymentTypeUs.Check, PaymentTypeUs.Check);
164+
}
165+
});
166+
167+
enumMappingEx.Message.ShouldContain("Check is already paired with PaymentTypeUk.Cheque");
168+
}
90169
}
91170
}
Lines changed: 107 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
namespace AgileObjects.AgileMapper.Api.Configuration
22
{
3+
using System.Collections.Generic;
4+
using System.Globalization;
5+
using System.Linq;
36
using AgileMapper.Configuration;
7+
using Extensions;
48
using NetStandardPolyfills;
59
using ReadableExpressions.Extensions;
610

@@ -11,25 +15,26 @@
1115
public class EnumPairSpecifier<TFirstEnum>
1216
{
1317
private readonly MappingConfigInfo _configInfo;
14-
private readonly TFirstEnum _firstEnumMember;
18+
private readonly TFirstEnum[] _firstEnumMembers;
1519

1620
private EnumPairSpecifier(
1721
MapperContext mapperContext,
18-
TFirstEnum firstEnumMember)
22+
TFirstEnum[] firstEnumMembers)
1923
{
2024
_configInfo = MappingConfigInfo.AllRuleSetsSourceTypesAndTargetTypes(mapperContext);
21-
_firstEnumMember = firstEnumMember;
25+
_firstEnumMembers = firstEnumMembers;
2226
}
2327

2428
#region Factory Method
2529

2630
internal static EnumPairSpecifier<TFirstEnum> For(
2731
MapperContext mapperContext,
28-
TFirstEnum firstEnumMember)
32+
TFirstEnum[] firstEnumMembers)
2933
{
3034
ThrowIfNotEnumType<TFirstEnum>();
35+
ThrowIfEmpty(firstEnumMembers);
3136

32-
return new EnumPairSpecifier<TFirstEnum>(mapperContext, firstEnumMember);
37+
return new EnumPairSpecifier<TFirstEnum>(mapperContext, firstEnumMembers);
3338
}
3439

3540
private static void ThrowIfNotEnumType<T>()
@@ -41,30 +46,53 @@ private static void ThrowIfNotEnumType<T>()
4146
}
4247
}
4348

49+
private static void ThrowIfEmpty(ICollection<TFirstEnum> firstEnumMembers)
50+
{
51+
if (firstEnumMembers.None())
52+
{
53+
throw new MappingConfigurationException("Source enum members must be provided.");
54+
}
55+
}
56+
4457
#endregion
4558

4659
/// <summary>
4760
/// Configure this mapper to map the specified first enum member to the given <paramref name="secondEnumMember"/>.
4861
/// </summary>
4962
/// <typeparam name="TSecondEnum">The type of the second enum being paired.</typeparam>
5063
/// <param name="secondEnumMember">The second enum member in the pair.</param>
51-
/// <returns>A MappingConfigContinuation to enable further global mapping configuration.</returns>
52-
public MappingConfigContinuation<object, object> With<TSecondEnum>(TSecondEnum secondEnumMember)
53-
where TSecondEnum : struct
64+
public void With<TSecondEnum>(TSecondEnum secondEnumMember) where TSecondEnum : struct
65+
=> With(new[] { secondEnumMember });
66+
67+
/// <summary>
68+
/// Configure this mapper to map the previously-specified set of enum members to the given
69+
/// <paramref name="secondEnumMembers"/>.
70+
/// </summary>
71+
/// <typeparam name="TSecondEnum">The type of the second enum being paired.</typeparam>
72+
/// <param name="secondEnumMembers">The second set of enum members in the pairs.</param>
73+
public void With<TSecondEnum>(params TSecondEnum[] secondEnumMembers) where TSecondEnum : struct
5474
{
5575
ThrowIfNotEnumType<TSecondEnum>();
5676
ThrowIfSameTypes<TSecondEnum>();
77+
ThrowIfEmpty(secondEnumMembers);
78+
ThrowIfDifferingNumbers(secondEnumMembers);
5779

58-
var firstToSecondPairing = EnumMemberPair.For(_firstEnumMember, secondEnumMember);
59-
var secondToFirstPairing = EnumMemberPair.For(secondEnumMember, _firstEnumMember);
80+
for (var i = 0; i < _firstEnumMembers.Length; i++)
81+
{
82+
var firstEnumMember = _firstEnumMembers[i];
83+
var secondEnumMember = secondEnumMembers[i];
6084

61-
_configInfo.MapperContext.ValueConverters.Add(firstToSecondPairing.ValueConverter);
62-
_configInfo.MapperContext.ValueConverters.Add(secondToFirstPairing.ValueConverter);
85+
ThrowIfAlreadyPaired(firstEnumMember, secondEnumMember);
6386

64-
_configInfo.MapperContext.UserConfigurations.Add(firstToSecondPairing);
65-
_configInfo.MapperContext.UserConfigurations.Add(secondToFirstPairing);
87+
var firstToSecondPairing = EnumMemberPair.For(firstEnumMember, secondEnumMember);
88+
var secondToFirstPairing = EnumMemberPair.For(secondEnumMember, firstEnumMember);
6689

67-
return new MappingConfigContinuation<object, object>(_configInfo);
90+
_configInfo.MapperContext.ValueConverters.Add(firstToSecondPairing.ValueConverter);
91+
_configInfo.MapperContext.ValueConverters.Add(secondToFirstPairing.ValueConverter);
92+
93+
_configInfo.MapperContext.UserConfigurations.Add(firstToSecondPairing);
94+
_configInfo.MapperContext.UserConfigurations.Add(secondToFirstPairing);
95+
}
6896
}
6997

7098
private static void ThrowIfSameTypes<TSecondEnum>()
@@ -75,5 +103,69 @@ private static void ThrowIfSameTypes<TSecondEnum>()
75103
"Enum pairing can only be configured between different enum types.");
76104
}
77105
}
106+
107+
private static void ThrowIfEmpty<TSecondEnum>(ICollection<TSecondEnum> secondEnumMembers)
108+
{
109+
if (secondEnumMembers.None())
110+
{
111+
throw new MappingConfigurationException("Target enum members must be provided.");
112+
}
113+
}
114+
115+
// ReSharper disable once UnusedParameter.Local
116+
private void ThrowIfDifferingNumbers<TSecondEnum>(ICollection<TSecondEnum> secondEnumMembers)
117+
{
118+
if (_firstEnumMembers.Length != secondEnumMembers.Count)
119+
{
120+
throw new MappingConfigurationException(
121+
"The same number of first and second enum values must be provided.");
122+
}
123+
}
124+
125+
private void ThrowIfAlreadyPaired<TSecondEnum>(TFirstEnum firstEnumMember, TSecondEnum secondEnumMember)
126+
{
127+
var firstEnumMemberName = firstEnumMember.ToString();
128+
var secondEnumMemberName = secondEnumMember.ToString();
129+
130+
var relevantPairings = _configInfo
131+
.MapperContext
132+
.UserConfigurations
133+
.EnumParings
134+
.Where(ep => ep.IsFor(typeof(TFirstEnum), typeof(TSecondEnum)))
135+
.ToArray();
136+
137+
if (relevantPairings.None())
138+
{
139+
return;
140+
}
141+
142+
var confictingPairing = relevantPairings
143+
.FirstOrDefault(ep => ep.FirstEnumMemberName == firstEnumMemberName);
144+
145+
if (confictingPairing != null)
146+
{
147+
throw new MappingConfigurationException(string.Format(
148+
CultureInfo.InvariantCulture,
149+
"{0}.{1} is already paired with {2}.{3}",
150+
typeof(TFirstEnum).Name,
151+
firstEnumMemberName,
152+
typeof(TSecondEnum).Name,
153+
confictingPairing.SecondEnumMemberName));
154+
}
155+
156+
confictingPairing = relevantPairings
157+
.FirstOrDefault(ep => ep.SecondEnumMemberName == secondEnumMemberName);
158+
159+
if (confictingPairing != null)
160+
{
161+
throw new MappingConfigurationException(string.Format(
162+
CultureInfo.InvariantCulture,
163+
"{0}.{1} is already paired with {2}.{3}",
164+
typeof(TSecondEnum).Name,
165+
secondEnumMemberName,
166+
typeof(TFirstEnum).Name,
167+
confictingPairing.FirstEnumMemberName));
168+
}
169+
}
78170
}
79171
}

AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,17 +188,30 @@ public InstanceConfigurator<TObject> InstancesOf<TObject>() where TObject : clas
188188
=> new InstanceConfigurator<TObject>(_mapperContext);
189189

190190
/// <summary>
191-
/// Configure this mapper to pair the given <paramref name="enumMember"/> with a member of another enum.
191+
/// Configure this mapper to pair the given <paramref name="enumMember"/> with a member of another enum Type.
192192
/// This pairing will apply to mappings between all types, irrespective of the MappingRuleSet used.
193193
/// </summary>
194194
/// <typeparam name="TFirstEnum">The type of the first enum being paired.</typeparam>
195195
/// <param name="enumMember">The first enum member in the pair.</param>
196196
/// <returns>
197-
/// An EnumPairSpecifier with which to specify the enum member to which the give <paramref name="enumMember"/>
197+
/// An EnumPairSpecifier with which to specify the enum member to which the given <paramref name="enumMember"/>
198198
/// should be paired.
199199
/// </returns>
200200
public EnumPairSpecifier<TFirstEnum> PairEnum<TFirstEnum>(TFirstEnum enumMember) where TFirstEnum : struct
201-
=> EnumPairSpecifier<TFirstEnum>.For(_mapperContext, enumMember);
201+
=> PairEnums(enumMember);
202+
203+
/// <summary>
204+
/// Configure this mapper to pair the given <paramref name="enumMembers"/> with members of another enum Type.
205+
/// Pairings will apply to mappings between all types, irrespective of the MappingRuleSet used.
206+
/// </summary>
207+
/// <typeparam name="TFirstEnum">The type of the first set of enum members being paired.</typeparam>
208+
/// <param name="enumMembers">The first set of enum members to pair.</param>
209+
/// <returns>
210+
/// An EnumPairSpecifier with which to specify the set of enum members to which the given <paramref name="enumMembers"/>
211+
/// should be paired.
212+
/// </returns>
213+
public EnumPairSpecifier<TFirstEnum> PairEnums<TFirstEnum>(params TFirstEnum[] enumMembers) where TFirstEnum : struct
214+
=> EnumPairSpecifier<TFirstEnum>.For(_mapperContext, enumMembers);
202215

203216
/// <summary>
204217
/// Configure how this mapper performs mappings from the source type specified by the given

0 commit comments

Comments
 (0)