Skip to content

Commit

Permalink
Added the to target type alias
Browse files Browse the repository at this point in the history
  • Loading branch information
stefankoppier committed Jul 2, 2024
1 parent 99e0c6a commit a647620
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package tech.mappie

import tech.mappie.generation.IrTransformer
import tech.mappie.generation.MappieIrTransformer
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
Expand All @@ -15,7 +15,7 @@ class MappieIrRegistrar(
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
context = MappiePluginContext(messageCollector, configuration, pluginContext)
val symbols = moduleFragment.accept(AllMappieDefinitionsCollector(), Unit)
moduleFragment.accept(IrTransformer(symbols), null)
moduleFragment.accept(MappieIrTransformer(symbols), null)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import tech.mappie.resolving.enums.ExplicitEnumMappingTarget
import tech.mappie.resolving.enums.ResolvedEnumMappingTarget
import tech.mappie.resolving.enums.ThrowingEnumMappingTarget

class IrTransformer(private val symbols: List<MappieDefinition>) : IrElementTransformerVoidWithContext() {
class MappieIrTransformer(private val symbols: List<MappieDefinition>) : IrElementTransformerVoidWithContext() {

override fun visitClassNew(declaration: IrClass): IrStatement {
declaration.declarations.filterIsInstance<IrClass>().forEach { inner ->
inner.transform(IrTransformer(symbols), null)
inner.transform(MappieIrTransformer(symbols), null)
}

if (declaration.accept(ShouldTransformCollector(), Unit)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,39 @@ class ObjectWithDifferentValuesTest {
}
}

@Test
fun `map two data classes using to with the different values from KProperty0 should succeed`() {
KotlinCompilation(directory).apply {
sources = buildList {
add(
kotlin("Test.kt",
"""
import tech.mappie.api.ObjectMappie
import tech.mappie.testing.ObjectWithDifferentValuesTest.*
class Mapper : ObjectMappie<Input, Output>() {
override fun map(from: Input) = mapping {
to::name fromProperty from::firstname
}
}
"""
)
)
}
}.compile {
assertThat(exitCode).isEqualTo(ExitCode.OK)
assertThat(messages).isEmpty()

val mapper = classLoader
.loadObjectMappieClass<Input, Output>("Mapper")
.constructors
.first()
.call()

assertThat(mapper.map(Input("Stefan", 30))).isEqualTo(Output("Stefan", 30))
}
}

@Test
fun `map two data classes with the different values from KProperty1 should succeed`() {
KotlinCompilation(directory).apply {
Expand Down
27 changes: 22 additions & 5 deletions mappie-api/src/commonMain/kotlin/tech/mappie/api/ObjectMappie.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package tech.mappie.api

import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.KProperty1

Expand All @@ -22,6 +23,22 @@ import kotlin.reflect.KProperty1
*/
public abstract class ObjectMappie<FROM, TO> : Mappie<FROM, TO>() {

/**
* Alias for the target type [TO] to simply property references.
* For example, in the following code
* ```kotlin
* object Mapper : ObjectMappie<Person, PersonDto>() {
* override fun map(from: Person) = mapping {
* to::name fromProperty PersonDto::fullName
* }
* }
* ```
* the line `to::name fromProperty PersonDto::fullName` is equivalent to
* `Person::name fromProperty PersonDto::fullName`.
*/
protected val to: TO
get() = error("The to property should only be used in the context of `to::property fromX y`.")

/**
* A mapper for [List] to be used in [TransformableValue.via].
*/
Expand All @@ -43,7 +60,7 @@ public abstract class ObjectMappie<FROM, TO> : Mappie<FROM, TO>() {
* ```
* will generate an explicit mapping, setting constructor parameter `Person.name` to `PersonDto.fullName`.
*/
protected infix fun <TO_TYPE, FROM_TYPE> KProperty1<TO, TO_TYPE>.fromProperty(source: KProperty0<FROM_TYPE>): TransformableValue<FROM_TYPE, TO_TYPE> =
protected infix fun <TO_TYPE, FROM_TYPE> KProperty<TO_TYPE>.fromProperty(source: KProperty0<FROM_TYPE>): TransformableValue<FROM_TYPE, TO_TYPE> =
generated()

/**
Expand All @@ -55,7 +72,7 @@ public abstract class ObjectMappie<FROM, TO> : Mappie<FROM, TO>() {
* ```
* will generate an explicit mapping, setting constructor parameter `Person.name` to `PersonDto.fullName`.
*/
protected infix fun <TO_TYPE, FROM_TYPE> KProperty1<TO, TO_TYPE>.fromProperty(source: KProperty1<FROM, FROM_TYPE>): TransformableValue<FROM_TYPE, TO_TYPE> =
protected infix fun <TO_TYPE, FROM_TYPE> KProperty<TO_TYPE>.fromProperty(source: KProperty1<FROM, FROM_TYPE>): TransformableValue<FROM_TYPE, TO_TYPE> =
generated()

/**
Expand All @@ -68,7 +85,7 @@ public abstract class ObjectMappie<FROM, TO> : Mappie<FROM, TO>() {
* will generate an explicit mapping, setting constructor parameter `Person.name` to `"John Doe"`.
*/
@Deprecated("This function is unnecessarily limiting.", replaceWith = ReplaceWith("this fromValue value"))
protected infix fun <TO_TYPE> KProperty1<TO, TO_TYPE>.fromConstant(value: TO_TYPE): Unit =
protected infix fun <TO_TYPE> KProperty<TO_TYPE>.fromConstant(value: TO_TYPE): Unit =
generated()

/**
Expand All @@ -80,7 +97,7 @@ public abstract class ObjectMappie<FROM, TO> : Mappie<FROM, TO>() {
* ```
* will generate an explicit mapping, setting constructor parameter `Person.name` to `"John Doe"`.
*/
protected infix fun <TO_TYPE> KProperty1<TO, TO_TYPE>.fromValue(value: TO_TYPE): Unit =
protected infix fun <TO_TYPE> KProperty<TO_TYPE>.fromValue(value: TO_TYPE): Unit =
generated()

/**
Expand All @@ -93,7 +110,7 @@ public abstract class ObjectMappie<FROM, TO> : Mappie<FROM, TO>() {
* will generate an explicit mapping, setting constructor parameter `Person.name` to `"John Doe (full)"`,
* assuming `personDto.fullName == "John Doe"`.
*/
protected infix fun <FROM_TYPE, TO_TYPE> KProperty1<TO, TO_TYPE>.fromExpression(function: (FROM) -> FROM_TYPE): Unit =
protected infix fun <FROM_TYPE, TO_TYPE> KProperty<TO_TYPE>.fromExpression(function: (FROM) -> FROM_TYPE): Unit =
generated()

/**
Expand Down
1 change: 1 addition & 0 deletions website/src/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ changelog:
- "[#31](https://github.com/Mr-Mappie/mappie/issues/31) added the explicit mapping method `thrownByEnumEntry` to throw an exception as a result when mapping an enum entry."
- "[#28](https://github.com/Mr-Mappie/mappie/issues/28) added implicit mapping inference of mappers with the same name but a different type, but a mapper for those types are defined."
- "[#42](https://github.com/Mr-Mappie/mappie/issues/42) added a configuration option to disable resolving via default arguments."
- "[#40](https://github.com/Mr-Mappie/mappie/issues/40) added `to` alias to refer to for target properties as an alternative to the fully written out `TO` type."
- "Several other bug fixes."
- date: "2024-06-27"
title: "v0.2.0"
Expand Down
2 changes: 1 addition & 1 deletion website/src/posts/getting-started/posts/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ eleventyNavigation:
Mappie can be configured via Gradle. The following global configuration options are available
```kotlin
mappie {
warningsAsErrors = true // Enable reporting warnings as errors]
warningsAsErrors = true // Enable reporting warnings as errors
useDefaultArguments = false // Disable using default arguments as sources.
strictness {
visibility = true // Allow calling constructors not visible from the calling scope
Expand Down
24 changes: 21 additions & 3 deletions website/src/posts/object-mapping/posts/resolving.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ eleventyNavigation:
order: 5
---

Not all data classes you want to map are equivalent. Suppose we have a data class `Person`
Not all data classes you want to map are equivalent. Mappie supports defining explicit mapping for those which cannot
be resolved automatically. This can be done via properties, expressions, or expressions as described in the coming
sections.

Suppose we have a data class `Person`
```kotlin
data class Person(val name: String, val age: Int)
```
Expand Down Expand Up @@ -80,7 +84,7 @@ object PersonMapper : ObjectMappie<Person, PersonDto>() {
```
will set `PersonDto.description` to `"Description: ${from.name}"`.

## Targeting a constructor parameter without a backing property
## Constructor Parameters without a Backing Property
It is possible that a constructor parameter is declared without a backing property. We can handle those constructor
parameters via the function `parameter`.

Expand All @@ -100,4 +104,18 @@ object PersonMapper : ObjectMappie<Person, PersonDto>() {
parameter("description") fromValue "a constant"
}
}
```
```

## The Target Type Alias
We can access the target properties via the target type of the mapper. This can clutter the mapping definition when
many explicit mappings are defined. Mappie defines a special `to` property which can be used instead of the target type.

For example, we can use `to` refer to the property `streetname` of `PersonDto`
```kotlin
object PersonMapper : ObjectMappie<Person, PersonDto>() {
override fun map(from: Person): PersonDto = mapping {
to::streetname fromProperty from.address::street
}
}
```
where `to::streetname` is equivalent to `PersonDto::streetname`.
2 changes: 1 addition & 1 deletion website/src/posts/object-mapping/posts/transforming.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object PersonMapper : ObjectMappie<Person, PersonDto>() {
}
```

## Handling nullability
## Handling Nullability
The `transform` operator can also come in handy when mapping nullable values to non-nullable values.

Suppose we have the data class `Dog` having the property `name` which can be `null`
Expand Down

0 comments on commit a647620

Please sign in to comment.