Skip to content

Commit

Permalink
fix: fixes using the wrong object factory when multiple factories wit…
Browse files Browse the repository at this point in the history
…h the same target type but different source types are used (#1313)
  • Loading branch information
latonz authored Jun 3, 2024
1 parent 51187a0 commit 51aa1da
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ namespace Riok.Mapperly.Descriptors.ObjectFactories;

public class ObjectFactoryCollection(IReadOnlyCollection<ObjectFactory> objectFactories)
{
private readonly Dictionary<ITypeSymbol, ObjectFactory> _concreteObjectFactories = new(SymbolEqualityComparer.IncludeNullability);
private readonly Dictionary<TypeMappingKey, ObjectFactory> _concreteObjectFactories = new();

public bool TryFindObjectFactory(ITypeSymbol sourceType, ITypeSymbol targetType, [NotNullWhen(true)] out ObjectFactory? objectFactory)
{
if (_concreteObjectFactories.TryGetValue(targetType, out objectFactory))
var key = new TypeMappingKey(sourceType, targetType);
if (_concreteObjectFactories.TryGetValue(key, out objectFactory))
return true;

objectFactory = objectFactories.FirstOrDefault(f => f.CanCreateType(sourceType, targetType));
if (objectFactory == null)
return false;

_concreteObjectFactories[targetType] = objectFactory;
_concreteObjectFactories[key] = objectFactory;
return true;
}
}
57 changes: 53 additions & 4 deletions test/Riok.Mapperly.Tests/Mapping/ObjectFactoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,19 @@ public void ShouldUseGenericSourceObjectFactory()
public void ShouldUseFirstMatchingObjectFactory()
{
var source = TestSourceBuilder.MapperWithBodyAndTypes(
"[ObjectFactory] A CreateA() => new A();"
+ "[ObjectFactory] B CreateB() => new B();"
+ "[ObjectFactory] T Create<T>() where T : new() => new T();"
+ "partial B Map(A a);",
"""
[ObjectFactory]
A CreateA() => new A();
[ObjectFactory]
B CreateB() => new B();
[ObjectFactory]
T Create<T>() where T : new()
=> new T();
partial B Map(A a);
""",
"class A { public string StringValue { get; set; } }",
"class B { public string StringValue { get; set; } }"
);
Expand Down Expand Up @@ -372,6 +381,46 @@ public void ShouldUseGenericObjectFactoryAndThrowIfNoParameterlessCtor()
);
}

[Fact]
public void MultipleObjectFactoriesMultipleMappingsShouldUseCorrect()
{
var source = TestSourceBuilder.MapperWithBodyAndTypes(
"""
[ObjectFactory]
private C CreateCFromA(A source)
=> new C();
[ObjectFactory]
private C CreateCFromB(B source)
=> new C();
partial C MapA(A source);
partial C MapB(B source);
""",
"record A;",
"record B;",
"record C;"
);

TestHelper
.GenerateMapper(source)
.Should()
.HaveMethodBody(
"MapA",
"""
var target = CreateCFromA(source);
return target;
"""
)
.HaveMethodBody(
"MapB",
"""
var target = CreateCFromB(source);
return target;
"""
);
}

[Fact]
public void ShouldUseObjectFactoryWithRecursiveTypeParameter()
{
Expand Down

0 comments on commit 51aa1da

Please sign in to comment.