Skip to content

Commit

Permalink
Changes and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
stefankoppier committed Jul 4, 2024
1 parent 2ad8072 commit fe7c474
Show file tree
Hide file tree
Showing 23 changed files with 244 additions and 465 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import tech.mappie.MappieIrRegistrar
import tech.mappie.resolving.enums.ExplicitEnumMappingTarget
import tech.mappie.resolving.enums.ResolvedEnumMappingTarget
import tech.mappie.resolving.enums.ThrowingEnumMappingTarget
import java.beans.Expression

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

Expand Down Expand Up @@ -135,18 +134,10 @@ fun IrBuilderWithScope.generateResolvedValueArgument(source: ResolvedSource): Ir
} ?: getter
}

fun IrPropertyReference.classType(): IrType {
return when (type.classOrFail) {
context.irBuiltIns.kProperty0Class -> dispatchReceiver!!.type
context.irBuiltIns.kProperty1Class -> (this.type as IrSimpleType).arguments.first().typeOrFail
else -> error("Unknown KProperty ${dumpKotlinLike()}")
}
}

fun IrBuilderWithScope.generatePropertyValueArgument(file: IrFileEntry, source: PropertySource, parameters: List<IrValueParameter>): IrFunctionAccessExpression {
val getter = irCall(source.getter).apply {
dispatchReceiver = source.property.dispatchReceiver
?: irGet(parameters.singleOrNull { it.type == source.property.classType() } ?: run {
?: irGet(parameters.singleOrNull { it.type == source.property.targetType } ?: run {
logError("Could not determine value parameters for property reference. Please use a property reference of an object instead of the class", location(file, source.property))
error("Mappie resolving failed")
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ val IDENTIFIER_THROWN_BY_ENUM_ENTRY = Name.identifier("thrownByEnumEntry")

val IDENTIFIER_FROM_PROPERTY = Name.identifier("fromProperty")

val IDENTIFIER_FROM_CONSTANT = Name.identifier("fromConstant")

val IDENTIFIER_FROM_VALUE = Name.identifier("fromValue")

val IDENTIFIER_FROM_EXPRESSION = Name.identifier("fromExpression")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ClassResolver(private val declaration: IrFunction, private val symbols: Li
val constructor = ObjectMappingsConstructor.of(declaration.returnType, sourceParameters)
.apply { getters.addAll(sourceParameters.flatMap { it.accept(GettersCollector(), it) }) }

declaration.body?.accept(ObjectMappingBodyCollector(declaration.fileEntry, sourceParameters), constructor)
declaration.body?.accept(ObjectMappingBodyCollector(declaration.fileEntry), constructor)

return declaration.accept(ConstructorsCollector(), Unit).map {
ObjectMappingsConstructor.of(constructor).apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,17 @@ import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.declarations.createExpressionBody
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name

class ObjectMappingBodyCollector(
file: IrFileEntry,
private val dispatchReceiverSymbols: List<IrValueParameter>,
) : BaseVisitor<ObjectMappingsConstructor, ObjectMappingsConstructor>(file) {
class ObjectMappingBodyCollector(file: IrFileEntry)
: BaseVisitor<ObjectMappingsConstructor, ObjectMappingsConstructor>(file) {

override fun visitBlockBody(body: IrBlockBody, data: ObjectMappingsConstructor): ObjectMappingsConstructor {
return body.statements.single().accept(data)
Expand Down Expand Up @@ -61,7 +57,7 @@ private class ObjectBodyStatementCollector(

override fun visitCall(expression: IrCall, data: Unit): Pair<Name, ObjectMappingSource>? {
return when (expression.symbol.owner.name) {
IDENTIFIER_FROM_PROPERTY, IDENTIFIER_FROM_CONSTANT -> {
IDENTIFIER_FROM_PROPERTY -> {
val target = expression.extensionReceiver!!.accept(TargetValueCollector(file!!), data)
val source = expression.valueArguments.first()!!.accept(SourceValueCollector(), Unit)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.*
import tech.mappie.BaseVisitor

class PrimitiveBodyCollector(
private val declaration: IrFunction,
) : BaseVisitor<IrExpression, Unit>() {
class PrimitiveBodyCollector(private val declaration: IrFunction) : BaseVisitor<IrExpression, Unit>() {

override fun visitBlockBody(body: IrBlockBody, data: Unit): IrExpression {
return body.statements.single().accept(data)
Expand Down
8 changes: 8 additions & 0 deletions compiler-plugin/src/main/kotlin/tech/mappie/util/Ir.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
Expand Down Expand Up @@ -35,6 +36,13 @@ fun IrType.isAssignableFrom(other: IrType, ignoreFlexibleNullability: Boolean =
fun IrType.isFlexibleNullable(): Boolean =
hasAnnotation(FlexibleNullability)

val IrPropertyReference.targetType: IrType
get() = when (type.classOrFail) {
context.irBuiltIns.kProperty0Class -> dispatchReceiver!!.type
context.irBuiltIns.kProperty1Class -> (this.type as IrSimpleType).arguments.first().typeOrFail
else -> error("Unknown KProperty ${dumpKotlinLike()}")
}

fun IrType.isIntegerAssignableFrom(other: IrType): Boolean =
when (this.makeNullable()) {
context.irBuiltIns.byteType.makeNullable() ->
Expand Down
67 changes: 0 additions & 67 deletions mappie-api/src/commonMain/kotlin/tech/mappie/api/ObjectMappie.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@

package tech.mappie.api

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

/**
* Base class for object mappers. See the [documentation](https://mappie.tech/object-mapping/object-mapping-overview/)
* for a complete overview of how to generate object mappers.
Expand Down Expand Up @@ -79,66 +75,3 @@ public abstract class ObjectMappie<FROM, out TO> : Mappie<TO> {
protected fun mapping(builder: ObjectMappingConstructor<FROM, TO>.() -> Unit = { }): TO = generated()
}

public class ObjectMappingConstructor<FROM, out 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`.
*/
public val to: TO
get() = error("The to property should only be used in the context of `to::property fromX y`.")

/**
* Explicitly construct a mapping to [TO] from property source [source].
*
* For example
* ```kotlin
* Person::name fromProperty PersonDto::fullName
* ```
* will generate an explicit mapping, setting constructor parameter `Person.name` to `PersonDto.fullName`.
*/
public infix fun <TO_TYPE, FROM_TYPE> KProperty<TO_TYPE>.fromProperty(source: KProperty<FROM_TYPE>): TransformableValue<FROM_TYPE, TO_TYPE> =
generated()

/**
* Explicitly construct a mapping to [TO] from a value source [value].
*
* For example
* ```kotlin
* Person::name fromValue "John Doe"
* ```
* will generate an explicit mapping, setting constructor parameter `Person.name` to `"John Doe"`.
*/
public infix fun <TO_TYPE> KProperty<TO_TYPE>.fromValue(value: TO_TYPE): Unit =
generated()

/**
* Explicitly construct a mapping to [TO] from expression source [function].
*
* For example
* ```kotlin
* Person::name fromExpression { personDto -> personDto.fullName + " (full)" }
* ```
* will generate an explicit mapping, setting constructor parameter `Person.name` to `"John Doe (full)"`,
* assuming `personDto.fullName == "John Doe"`.
*/
public infix fun <FROM_TYPE, TO_TYPE> KProperty<TO_TYPE>.fromExpression(function: (FROM) -> FROM_TYPE): Unit =
generated()

/**
* Reference a constructor parameter in lieu of a property reference, if it not exists as a property.
*
* For example
* ```kotlin
* parameter("name") fromProperty PersonDto::fullName
* ```
* will generate an explicit mapping, setting constructor parameter `name` to `PersonDto.fullName`.
*/
public fun parameter(name: String): KProperty<*> =
generated()
}
103 changes: 14 additions & 89 deletions mappie-api/src/commonMain/kotlin/tech/mappie/api/ObjectMappie2.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,39 @@

package tech.mappie.api

import kotlin.reflect.KProperty

/**
* Base class for object mappers. See the [documentation](https://mappie.tech/object-mapping/object-mapping-overview/)
* for a complete overview of how to generate object mappers.
*
* For example
* ```kotlin
* class PersonMapper : ObjectMappie<PersonDto, Person>() {
* override fun map(from: PersonDto) = mapping()
* class PersonMapper : ObjectMappie2<PersonDto, AddressDto, Person>() {
* override fun map(first: PersonDto, second: AddressDto) = mapping()
* }
* ```
* will generate a mapper from `PersonDto` to `Person`, assuming both `PersonDto` and `Person` have a resolvable mapping.
* will generate a mapper from `PersonDto` and `AddressDto` to `Person`, assuming all dto's and `Person` have a resolvable mapping.
*
* @param FROM the source type to map from.
* @param FROM1 the first source type to map from.
* @param FROM2 the second source type to map from.
* @param TO the target type to map to.
*/
public abstract class ObjectMappie2<in FROM1, in FROM2, out TO> : Mappie<TO> {

/**
* A mapper for [List] to be used in [TransformableValue.via].
*/
public val forList: ListMappie<TO> get() =
error("The mapper forList should only be used in the context of 'via'. Use mapList instead.")

/**
* A mapper for [Set] to be used in [TransformableValue.via].
*/
public val forSet: SetMappie<TO> get() =
error("The mapper forSet should only be used in the context of 'via'. Use mapSet instead.")

/**
* Map [from] to an instance of [TO].
* Map [first] and [second] to an instance of [TO].
*
* @param from the source value.
* @return [from] mapped to an instance of [TO].
* @param first the first source value.
* @param second the second source value.
* @return [first] and [second] mapped to an instance of [TO].
*/
public open fun map(first: FROM1, second: FROM2): TO = generated()

/**
* Map nullable [from] to an instance of [TO].
* Map nullable [first] and [second] to an instance of [TO].
*
* @param from the source value.
* @return [from] mapped to an instance of [TO].
* @param first the first source value.
* @param second the second source value.
* @return [first] and [second] mapped an instance of [TO].
*/
public fun mapNullable(first: FROM1?, second: FROM2?): TO? =
if (first == null || second == null) null else map(first, second)
Expand All @@ -56,69 +45,5 @@ public abstract class ObjectMappie2<in FROM1, in FROM2, out TO> : Mappie<TO> {
* @param builder the configuration for the generation of this mapping.
* @return An instance of the mapped value at runtime.
*/
protected fun mapping(builder: ObjectMappingConstructor2<TO>.() -> Unit = { }): TO = generated()
protected fun mapping(builder: MultipleObjectMappingConstructor<TO>.() -> Unit = { }): TO = generated()
}

public class ObjectMappingConstructor2<out 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`.
*/
public val to: TO
get() = error("The to property should only be used in the context of `to::property fromX y`.")

/**
* Explicitly construct a mapping to [TO] from property source [source].
*
* For example
* ```kotlin
* Person::name fromProperty PersonDto::fullName
* ```
* will generate an explicit mapping, setting constructor parameter `Person.name` to `PersonDto.fullName`.
*/
public infix fun <TO_TYPE, FROM_TYPE> KProperty<TO_TYPE>.fromProperty(source: KProperty<FROM_TYPE>): TransformableValue<FROM_TYPE, TO_TYPE> =
generated()

/**
* Explicitly construct a mapping to [TO] from constant source [value].
*
* For example
* ```kotlin
* Person::name fromConstant "John Doe"
* ```
* will generate an explicit mapping, setting constructor parameter `Person.name` to `"John Doe"`.
*/
@Deprecated("This function is unnecessarily limiting.", replaceWith = ReplaceWith("this fromValue value"))
public infix fun <TO_TYPE> KProperty<TO_TYPE>.fromConstant(value: TO_TYPE): Unit =
generated()

/**
* Explicitly construct a mapping to [TO] from a value source [value].
*
* For example
* ```kotlin
* Person::name fromValue "John Doe"
* ```
* will generate an explicit mapping, setting constructor parameter `Person.name` to `"John Doe"`.
*/
public infix fun <TO_TYPE> KProperty<TO_TYPE>.fromValue(value: TO_TYPE): Unit =
generated()

/**
* Reference a constructor parameter in lieu of a property reference, if it not exists as a property.
*
* For example
* ```kotlin
* parameter("name") fromProperty PersonDto::fullName
* ```
* will generate an explicit mapping, setting constructor parameter `name` to `PersonDto.fullName`.
*/
public fun parameter(name: String): KProperty<*> =
generated()
}
Loading

0 comments on commit fe7c474

Please sign in to comment.