diff --git a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/CapacityMemberSetter.cs b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/CapacityMemberSetter.cs
deleted file mode 100644
index 1561c00cc4..0000000000
--- a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/CapacityMemberSetter.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Riok.Mapperly.Symbols.Members;
-
-namespace Riok.Mapperly.Descriptors.Enumerables.Capacity;
-
-internal class CapacityMemberSetter(IMappableMember targetCapacityMember, IMemberSetter setter) : ICapacityMemberSetter
-{
- public bool SupportsCoalesceAssignment => setter.SupportsCoalesceAssignment;
-
- public IMappableMember TargetCapacity => targetCapacityMember;
-
- public ExpressionSyntax BuildAssignment(
- ExpressionSyntax? baseAccess,
- ExpressionSyntax valueToAssign,
- bool coalesceAssignment = false
- ) => setter.BuildAssignment(baseAccess, valueToAssign, coalesceAssignment);
-
- public static ICapacityMemberSetter Build(MappingBuilderContext ctx, IMappableMember member) =>
- new CapacityMemberSetter(member, member.BuildSetter(ctx.UnsafeAccessorContext));
-}
diff --git a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/CapacitySetterBuilder.cs b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/CapacitySetterBuilder.cs
index b79e95df62..a30d963c60 100644
--- a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/CapacitySetterBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/CapacitySetterBuilder.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
+using Riok.Mapperly.Symbols.Members;
namespace Riok.Mapperly.Descriptors.Enumerables.Capacity;
@@ -48,7 +49,7 @@ bool includeTargetCount
return new NonEnumeratedCapacitySetter(capacitySetter, targetCount, nonEnumeratedCountMethod);
}
- private static ICapacityMemberSetter? BuildCapacitySetter(MappingBuilderContext ctx, CollectionInfo target)
+ private static IMemberSetter? BuildCapacitySetter(MappingBuilderContext ctx, CollectionInfo target)
{
var ensureCapacityMethod = ctx
.SymbolAccessor.GetAllMethods(target.Type, EnsureCapacityMethodSetter.EnsureCapacityMethodName)
@@ -58,7 +59,7 @@ bool includeTargetCount
var member = ctx.SymbolAccessor.GetMappableMember(target.Type, CapacityMemberName);
if (member is { CanSetDirectly: true, IsInitOnly: false, Type.SpecialType: SpecialType.System_Int32 })
- return CapacityMemberSetter.Build(ctx, member);
+ return member.BuildSetter(ctx.UnsafeAccessorContext);
return null;
}
diff --git a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/EnsureCapacityMethodSetter.cs b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/EnsureCapacityMethodSetter.cs
index b5b9fdd5dd..1214487e7e 100644
--- a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/EnsureCapacityMethodSetter.cs
+++ b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/EnsureCapacityMethodSetter.cs
@@ -7,7 +7,7 @@ namespace Riok.Mapperly.Descriptors.Enumerables.Capacity;
///
/// Ensures the capacity of a collection by calling `EnsureCapacity(int)`
///
-internal class EnsureCapacityMethodSetter : ICapacityMemberSetter
+internal class EnsureCapacityMethodSetter : IMemberSetter
{
public static readonly EnsureCapacityMethodSetter Instance = new();
@@ -17,8 +17,6 @@ private EnsureCapacityMethodSetter() { }
public bool SupportsCoalesceAssignment => false;
- public IMappableMember? TargetCapacity => null;
-
public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax valueToAssign, bool coalesceAssignment = false)
{
if (baseAccess == null)
diff --git a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/ICapacityMemberSetter.cs b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/ICapacityMemberSetter.cs
deleted file mode 100644
index ebc19023ec..0000000000
--- a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/ICapacityMemberSetter.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Riok.Mapperly.Symbols.Members;
-
-namespace Riok.Mapperly.Descriptors.Enumerables.Capacity;
-
-///
-/// Sets the capacity of a collection to the provided count.
-///
-public interface ICapacityMemberSetter : IMemberSetter
-{
- IMappableMember? TargetCapacity { get; }
-}
diff --git a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/ICapacitySetter.cs b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/ICapacitySetter.cs
index 9b3444ceee..923d2b2465 100644
--- a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/ICapacitySetter.cs
+++ b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/ICapacitySetter.cs
@@ -1,6 +1,5 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Descriptors.Mappings;
-using Riok.Mapperly.Symbols.Members;
namespace Riok.Mapperly.Descriptors.Enumerables.Capacity;
@@ -9,7 +8,5 @@ namespace Riok.Mapperly.Descriptors.Enumerables.Capacity;
///
public interface ICapacitySetter
{
- IMappableMember? CapacityTargetMember { get; }
-
StatementSyntax Build(TypeMappingBuildContext ctx, ExpressionSyntax target);
}
diff --git a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/NonEnumeratedCapacitySetter.cs b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/NonEnumeratedCapacitySetter.cs
index dc83e0a897..2c136c7ee8 100644
--- a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/NonEnumeratedCapacitySetter.cs
+++ b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/NonEnumeratedCapacitySetter.cs
@@ -18,16 +18,11 @@ namespace Riok.Mapperly.Descriptors.Enumerables.Capacity;
/// target.EnsureCapacity(sourceCount + target.Count);
///
///
-public class NonEnumeratedCapacitySetter(
- ICapacityMemberSetter capacitySetter,
- IMemberGetter? targetAccessor,
- IMethodSymbol getNonEnumeratedMethod
-) : ICapacitySetter
+public class NonEnumeratedCapacitySetter(IMemberSetter capacitySetter, IMemberGetter? targetAccessor, IMethodSymbol getNonEnumeratedMethod)
+ : ICapacitySetter
{
private const string SourceCountVariableName = "sourceCount";
- public IMappableMember? CapacityTargetMember => capacitySetter.TargetCapacity;
-
public StatementSyntax Build(TypeMappingBuildContext ctx, ExpressionSyntax target)
{
var sourceCountName = ctx.NameBuilder.New(SourceCountVariableName);
diff --git a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/SimpleCapacitySetter.cs b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/SimpleCapacitySetter.cs
index b536b9c1eb..18b5bd68e7 100644
--- a/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/SimpleCapacitySetter.cs
+++ b/src/Riok.Mapperly/Descriptors/Enumerables/Capacity/SimpleCapacitySetter.cs
@@ -15,11 +15,9 @@ namespace Riok.Mapperly.Descriptors.Enumerables.Capacity;
/// target.Capacity = source.Length + target.Count;
///
///
-public class SimpleCapacitySetter(ICapacityMemberSetter capacitySetter, IMemberGetter? targetAccessor, IMemberGetter sourceAccessor)
+public class SimpleCapacitySetter(IMemberSetter capacitySetter, IMemberGetter? targetAccessor, IMemberGetter sourceAccessor)
: ICapacitySetter
{
- public IMappableMember? CapacityTargetMember => capacitySetter.TargetCapacity;
-
public StatementSyntax Build(TypeMappingBuildContext ctx, ExpressionSyntax target)
{
var count = sourceAccessor.BuildAccess(ctx.Source);
diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/IMembersBuilderContext.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/IMembersBuilderContext.cs
index 2a10b78fd2..e3695e689e 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/IMembersBuilderContext.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/IMembersBuilderContext.cs
@@ -16,6 +16,7 @@ public interface IMembersBuilderContext
MappingBuilderContext BuilderContext { get; }
void IgnoreMembers(IMappableMember member);
+ void IgnoreMembers(string memberName);
void SetMembersMapped(MemberMappingInfo members);
diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingBuilderContext.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingBuilderContext.cs
index bc84bdc6e2..1d650e6f73 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingBuilderContext.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingBuilderContext.cs
@@ -45,6 +45,8 @@ protected void SetTargetMemberMapped(string targetMemberName, bool ignoreCase =
public void IgnoreMembers(IMappableMember member) => _state.IgnoreMembers(member);
+ public void IgnoreMembers(string memberName) => _state.IgnoreMembers(memberName);
+
public void ConsumeMemberConfigs(MemberMappingInfo members)
{
if (members.Configuration != null)
diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingState.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingState.cs
index f1e54a3526..32c9ba9f6b 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingState.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingState.cs
@@ -91,15 +91,17 @@ public void MappingAdded(MemberMappingInfo info, bool ignoreTargetCasing)
SetMembersMapped(info, ignoreTargetCasing);
}
- public void IgnoreMembers(IMappableMember member)
+ public void IgnoreMembers(IMappableMember member) => IgnoreMembers(member.Name);
+
+ public void IgnoreMembers(string name)
{
- _unmappedSourceMemberNames.Remove(member.Name);
- _unmappedTargetMemberNames.Remove(member.Name);
- ignoredSourceMemberNames.Add(member.Name);
+ _unmappedSourceMemberNames.Remove(name);
+ _unmappedTargetMemberNames.Remove(name);
+ ignoredSourceMemberNames.Add(name);
- if (!HasMemberConfig(member.Name))
+ if (!HasMemberConfig(name))
{
- targetMembers.Remove(member.Name);
+ targetMembers.Remove(name);
}
}
diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/EnumerableMappingBodyBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/EnumerableMappingBodyBuilder.cs
index 55e692786f..a39555aab2 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/EnumerableMappingBodyBuilder.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/EnumerableMappingBodyBuilder.cs
@@ -9,6 +9,7 @@ namespace Riok.Mapperly.Descriptors.MappingBodyBuilders;
internal static class EnumerableMappingBodyBuilder
{
private const string SystemNamespaceName = "System";
+ private const string CapacityMemberName = "Capacity";
private static readonly IReadOnlyCollection _sourceCountAlias =
[
@@ -35,13 +36,13 @@ public static void BuildMappingBody(MappingBuilderContext ctx, IEnumerableMappin
// include the target count as the target could already include elements
if (CapacitySetterBuilder.TryBuildCapacitySetter(ctx, mapping.CollectionInfos, true) is { } capacitySetter)
{
- if (capacitySetter.CapacityTargetMember != null)
- {
- mappingCtx.IgnoreMembers(capacitySetter.CapacityTargetMember);
- }
-
+ mappingCtx.IgnoreMembers(CapacityMemberName);
mapping.AddCapacitySetter(capacitySetter);
}
+ else
+ {
+ IgnoreCapacityIfSystemType(mappingCtx);
+ }
ObjectMemberMappingBodyBuilder.BuildMappingBody(mappingCtx);
mappingCtx.AddDiagnostics();
@@ -72,6 +73,15 @@ private static void IgnoreSystemMembers(IMembersBuilderContext ctx, ITypeS
}
}
+ private static void IgnoreCapacityIfSystemType(IMembersBuilderContext ctx)
+ where T : IEnumerableMapping
+ {
+ if (ctx.Mapping.SourceType.IsInRootNamespace(SystemNamespaceName) || ctx.Mapping.TargetType.IsInRootNamespace(SystemNamespaceName))
+ {
+ ctx.IgnoreMembers(CapacityMemberName);
+ }
+ }
+
private static void BuildConstructorMapping(INewInstanceBuilderContext ctx)
{
// allow source count being mapped to a target constructor parameter
@@ -108,12 +118,12 @@ private static void BuildConstructorMapping(INewInstanceBuilderContext WalkTypeHierarchy(this ITypeSymbol symb
internal static bool IsInRootNamespace(this ISymbol symbol, string ns)
{
var namespaceSymbol = symbol.ContainingNamespace;
- while (namespaceSymbol.ContainingNamespace is { IsGlobalNamespace: false })
+ while (namespaceSymbol?.ContainingNamespace is { IsGlobalNamespace: false })
{
namespaceSymbol = namespaceSymbol.ContainingNamespace;
}
- return string.Equals(namespaceSymbol.Name, ns, StringComparison.Ordinal);
+ return namespaceSymbol != null && string.Equals(namespaceSymbol.Name, ns, StringComparison.Ordinal);
}
}
diff --git a/test/Riok.Mapperly.Tests/Mapping/EnumerableCustomTest.cs b/test/Riok.Mapperly.Tests/Mapping/EnumerableCustomTest.cs
index 72d8573655..8e1b71f2f6 100644
--- a/test/Riok.Mapperly.Tests/Mapping/EnumerableCustomTest.cs
+++ b/test/Riok.Mapperly.Tests/Mapping/EnumerableCustomTest.cs
@@ -381,4 +381,57 @@ public void CustomCollectionToCustomCollectionWithObjectFactory()
"""
);
}
+
+ [Fact]
+ public void CustomCollectionToGetOnlyICollection()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "A",
+ "B",
+ "class A { public C Value { get; } }",
+ "class B { public ICollection Value { get; } }",
+ "class C : IEnumerable { public int Capacity { get; } public int Count { get; } }"
+ );
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowAndIncludeAllDiagnostics)
+ .Should()
+ .HaveAssertedAllDiagnostics()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ foreach (var item in source.Value)
+ {
+ target.Value.Add(item);
+ }
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void CustomCollectionToGetOnlyList()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "A",
+ "B",
+ "class A { public C Value { get; } }",
+ "class B { public List Value { get; } }",
+ "class C : IEnumerable { public int Capacity { get; } public int Count { get; } }"
+ );
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowAndIncludeAllDiagnostics)
+ .Should()
+ .HaveAssertedAllDiagnostics()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value.EnsureCapacity(source.Value.Count + target.Value.Count);
+ foreach (var item in source.Value)
+ {
+ target.Value.Add(item);
+ }
+ return target;
+ """
+ );
+ }
}