diff --git a/compiler-plugin/src/main/kotlin/tech/mappie/resolving/classes/ExplicitClassMappingCollector.kt b/compiler-plugin/src/main/kotlin/tech/mappie/resolving/classes/ExplicitClassMappingCollector.kt index 0e40f1a..24863de 100644 --- a/compiler-plugin/src/main/kotlin/tech/mappie/resolving/classes/ExplicitClassMappingCollector.kt +++ b/compiler-plugin/src/main/kotlin/tech/mappie/resolving/classes/ExplicitClassMappingCollector.kt @@ -54,7 +54,7 @@ private class ClassMappingStatementCollector(private val context: ResolverContex } IDENTIFIER_FROM_EXPRESSION -> { val target = expression.extensionReceiver!!.accept(TargetNameCollector(context), data) - target to ExpressionMappingSource(expression.valueArguments.first() as IrFunctionExpression) + target to ExpressionMappingSource(expression.valueArguments.first()!!) } IDENTIFIER_VIA -> { expression.dispatchReceiver!!.accept(data).let { (name, source) -> @@ -66,7 +66,13 @@ private class ClassMappingStatementCollector(private val context: ResolverContex IDENTIFIER_TRANSFORM -> { expression.dispatchReceiver!!.accept(data).let { (name, source) -> name to (source as ExplicitPropertyMappingSource).copy( - transformation = PropertyMappingTransformTranformation(expression.valueArguments.first()!! as IrFunctionExpression) + transformation = expression.valueArguments.first().let { + when (it) { + is IrFunctionExpression -> PropertyMappingTransformTranformation(it) + is IrFunctionReference -> PropertyMappingTransformTranformation(it) + else -> throw MappiePanicException("Unexpected expression type: ${expression.dumpKotlinLike()}") + } + } ) } } diff --git a/compiler-plugin/src/main/kotlin/tech/mappie/resolving/classes/sources/ClassMappingSource.kt b/compiler-plugin/src/main/kotlin/tech/mappie/resolving/classes/sources/ClassMappingSource.kt index e964f02..16f019e 100644 --- a/compiler-plugin/src/main/kotlin/tech/mappie/resolving/classes/sources/ClassMappingSource.kt +++ b/compiler-plugin/src/main/kotlin/tech/mappie/resolving/classes/sources/ClassMappingSource.kt @@ -6,6 +6,7 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrValueParameter import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression +import org.jetbrains.kotlin.ir.expressions.IrFunctionReference import org.jetbrains.kotlin.ir.expressions.IrPropertyReference import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.name.Name @@ -85,10 +86,12 @@ sealed interface PropertyMappingTransformation { val type: IrType } -data class PropertyMappingTransformTranformation( - val function: IrFunctionExpression, +data class PropertyMappingTransformTranformation private constructor( + val function: IrExpression, + override val type: IrType, ) : PropertyMappingTransformation { - override val type = function.function.returnType + constructor(functionReference: IrFunctionReference) : this(functionReference, functionReference.symbol.owner.returnType) + constructor(functionExpression: IrFunctionExpression) : this(functionExpression, functionExpression.function.returnType) } data class PropertyMappingViaMapperTransformation( diff --git a/compiler-plugin/src/test/kotlin/tech/mappie/testing/objects/FromExpressionTest.kt b/compiler-plugin/src/test/kotlin/tech/mappie/testing/objects/FromExpressionTest.kt index ec95d77..34ae630 100644 --- a/compiler-plugin/src/test/kotlin/tech/mappie/testing/objects/FromExpressionTest.kt +++ b/compiler-plugin/src/test/kotlin/tech/mappie/testing/objects/FromExpressionTest.kt @@ -42,4 +42,54 @@ class FromExpressionTest { assertThat(mapper.map(Unit)).isEqualTo(Output(Unit::class.simpleName!!)) } } + + @Test + fun `map property fromExpression should succeed with method reference`() { + compile(directory) { + file("Test.kt", + """ + import tech.mappie.api.ObjectMappie + import tech.mappie.testing.objects.FromExpressionTest.* + + class Mapper : ObjectMappie() { + override fun map(from: Int) = mapping { + Output::value fromExpression Int::toString + } + } + """ + ) + } satisfies { + isOk() + hasNoMessages() + + val mapper = classLoader + .loadObjectMappieClass("Mapper") + .constructors + .first() + .call() + + assertThat(mapper.map(101)).isEqualTo(Output("101")) + } + } + + @Test + fun `map property fromExpression should fail with method reference with wrong return type`() { + compile(directory) { + file("Test.kt", + """ + import tech.mappie.api.ObjectMappie + import tech.mappie.testing.objects.FromExpressionTest.* + + class Mapper : ObjectMappie() { + override fun map(from: Int) = mapping { + Output::value fromExpression Int::toInt + } + } + """ + ) + } satisfies { + isCompilationError() + hasErrorMessage(6, "Target Output::value of type String cannot be assigned from expression of type Int") + } + } } \ No newline at end of file diff --git a/compiler-plugin/src/test/kotlin/tech/mappie/testing/objects/ObjectWithDifferentValuesTest.kt b/compiler-plugin/src/test/kotlin/tech/mappie/testing/objects/ObjectWithDifferentValuesTest.kt index 61403c4..85f110a 100644 --- a/compiler-plugin/src/test/kotlin/tech/mappie/testing/objects/ObjectWithDifferentValuesTest.kt +++ b/compiler-plugin/src/test/kotlin/tech/mappie/testing/objects/ObjectWithDifferentValuesTest.kt @@ -6,6 +6,7 @@ import org.junit.jupiter.api.io.TempDir import tech.mappie.testing.compilation.compile import tech.mappie.testing.loadObjectMappieClass import java.io.File +import kotlin.random.Random class ObjectWithDifferentValuesTest { @@ -118,4 +119,57 @@ class ObjectWithDifferentValuesTest { assertThat(mapper.map(Input("Sjon", 58))).isEqualTo(Output("Sjon", 58)) } } + + @Test + fun `map property fromProperty should succeed with method reference transform`() { + compile(directory) { + file("Test.kt", + """ + import tech.mappie.api.ObjectMappie + import tech.mappie.testing.objects.ObjectWithDifferentValuesTest.* + + class Mapper : ObjectMappie() { + override fun map(from: Input) = mapping { + Output::age fromProperty from::firstname transform String::toInt + Output::name fromProperty from::age transform Int::toString + } + } + """ + ) + } satisfies { + isOk() + hasNoMessages() + + val mapper = classLoader + .loadObjectMappieClass("Mapper") + .constructors + .first() + .call() + + assertThat(mapper.map(Input("101", 9))).isEqualTo(Output("9", 101)) + } + } + + @Test + fun `map property fromProperty should fail with method reference with wrong signature`() { + compile(directory) { + file("Test.kt", + """ + import tech.mappie.api.ObjectMappie + import tech.mappie.testing.objects.ObjectWithDifferentValuesTest.* + + class Mapper : ObjectMappie() { + override fun map(from: Input) = mapping { + Output::age fromProperty from::firstname transform String::toString + Output::name fromProperty from::age transform Int::toInt + } + } + """ + ) + } satisfies { + isCompilationError() + hasErrorMessage(6, "Inapplicable candidate(s): fun toString(): String") + hasErrorMessage(7, "Inapplicable candidate(s): fun toInt(): Int") + } + } } \ No newline at end of file diff --git a/website/src/changelog.md b/website/src/changelog.md index 4e6a801..356edce 100644 --- a/website/src/changelog.md +++ b/website/src/changelog.md @@ -2,6 +2,10 @@ title: "Changelog" layout: "layouts/changelog.html" changelog: + - date: "tbd" + title: "v0.11.0" + items: + - "Added support for method references in fromExpression and transform" - date: "tbd" title: "v0.10.0" items: