Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the to target type alias #44

Merged
merged 2 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@file:Suppress("unused", "UNUSED_PARAMETER")
@file:Suppress("UNUSED_PARAMETER")

package tech.mappie.api

Expand Down
2 changes: 1 addition & 1 deletion mappie-api/src/commonMain/kotlin/tech/mappie/api/Mappie.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@file:Suppress("unused", "UNUSED_PARAMETER")
@file:Suppress("UNUSED_PARAMETER")

package tech.mappie.api

Expand Down
25 changes: 19 additions & 6 deletions mappie-api/src/commonMain/kotlin/tech/mappie/api/ObjectMappie.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
@file:Suppress("unused", "UNUSED_PARAMETER", "SameParameterValue")
@file:Suppress("UNUSED_PARAMETER", "SameParameterValue")

package tech.mappie.api

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

Expand All @@ -22,6 +23,18 @@ 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, suppose we are constructing a mapper with target type `Person`
* ```kotlin
* 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 +56,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 +68,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 +81,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 +93,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 +106,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