Skip to content

Add reverse mapping support#2106

Open
Kataane wants to merge 3 commits intoriok:mainfrom
Kataane:feature/reverse-mapping
Open

Add reverse mapping support#2106
Kataane wants to merge 3 commits intoriok:mainfrom
Kataane:feature/reverse-mapping

Conversation

@Kataane
Copy link
Contributor

@Kataane Kataane commented Jan 9, 2026

Add Reverse Mapping Support

Description

Implements reverse mapping functionality for Mapperly with a new Reverse property on IncludeMappingConfigurationAttribute. Automatically swaps source/target types and reverses member configurations for bidirectional mapping.

Key changes:

  • Added IReversible<T> interface pattern
  • Enhanced configuration reader with reverse logic
  • Comprehensive test coverage (9 scenarios)
  • Maintains backward compatibility

Fixes # (issue)

Checklist

  • Code style followed
  • Commit message follows guidelines
  • Self-review completed
  • Code commented where needed
  • Documentation updated
  • Unit tests added/updated
  • Integration tests added/updated

Example

[MapProperty(nameof(Product.Price), nameof(ProductDTO.PriceInEuro))]
public static partial ProductDTO ToDTO(this Product source);

[IncludeMappingConfiguration(nameof(ToDTO), Reverse = true)]
public static partial Product ToProduct(this ProductDTO source);

#1250

@Kataane
Copy link
Contributor Author

Kataane commented Jan 9, 2026

For some reason, the test failed after I merged the main branch to my branch :(

Update: this configuration no longer works.

[MapProperty(nameof(Product.Discounts.EndUsers), nameof(ProductDTO.Discount))]

@latonz latonz added the enhancement New feature or request label Jan 20, 2026
/// <see cref="TData"/> needs to have an accessible ctor with the parameters 0 to n-1 to be of type <see cref="ITypeSymbol"/>.
/// <see cref="TData"/> needs to have exactly the same constructors as <see cref="TAttribute"/> with additional type arguments.
/// </summary>
/// <param name="attributes">The attributes data.</param>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the new param in the xml docs.

}

internal static TData Access<TAttribute, TData>(AttributeData attrData, SymbolAccessor? symbolAccessor = null)
internal static TData Access<TAttribute, TData>(AttributeData attrData, SymbolAccessor? symbolAccessor = null, bool rever = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type rever should probably be reverse...

public class ReverseMappingTest
{
[Fact]
public Task BasicReverseMappingShouldWork()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add an integration test.

public class ReverseMappingTest
{
[Fact]
public Task BasicReverseMappingShouldWork()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this test can be removed as it doesn't add real value to ReverseMappingWithMultiplePropertiesShouldWork

}

[Fact]
public Task ReverseMappingWithUseConverterShouldWork()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we use the term Converter

Comment on lines +119 to +144
[Fact(Skip = "Didn't add a check")]
public Task ReverseMappingShouldReportCircularReference()
{
var source = TestSourceBuilder.MapperWithBodyAndTypes(
"""
[MapProperty(nameof(Product.Price), nameof(ProductDTO.PriceInEuro))]
[IncludeMappingConfiguration(nameof(ToProduct), Reverse = true)]
public static partial ProductDTO ToDTO(this Product source);

[IncludeMappingConfiguration(nameof(ToDTO), Reverse = true)]
public static partial void ToProduct(this ProductDTO source, Product target);
""",
"class Product { public decimal Price { get; set; } }",
"class ProductDTO { public decimal PriceInEuro { get; set; } }"
);

return TestHelper.VerifyGenerator(source);
}

[Fact(
Skip = "It should work similarly to mapping nested properties. However, the MapNestedProperties class only works with the source."
)]
// ProductDTO.Id = Product.Info.Id
// Reverse
// Product.Info.Id = ProductDTO.Id
public Task ReverseMappingWithMapNestedPropertiesShouldWork()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you provide more info here? Why are these ignored?

}

[Fact]
// Reverse Target to Source to calm down report
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does calm down report mean?

);
}

private EnumMappingConfiguration BuildEnumConfig(MappingConfigurationReference configRef)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific reason for reordering the members? It creates a lot of noise in the diff, making it difficult to review the actual logic changes. I haven't reviewed this file yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants