Skip to content

Commit 23c82d3

Browse files
authored
Merge pull request #53 from a8t3r/support-infix-notation
Support getters notation without parentheses
2 parents 4d75cee + ddfbfe5 commit 23c82d3

87 files changed

Lines changed: 10521 additions & 70 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

kobby-generator-kotlin/src/main/kotlin/io/github/ermadmi78/kobby/generator/kotlin/_poet.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ internal fun PropertySpecBuilder.buildGetter(
172172
getter(it)
173173
}
174174

175+
internal fun PropertySpecBuilder.buildSetter(
176+
block: FunSpecBuilder.() -> Unit
177+
): FunSpec = FunSpec.setterBuilder().apply(block).build().also {
178+
setter(it)
179+
}
180+
175181
internal fun PropertySpecBuilder.buildDelegate(
176182
block: CodeBlockBuilder.() -> Unit
177183
): CodeBlock = CodeBlock.builder().apply(block).build().also {

kobby-generator-kotlin/src/main/kotlin/io/github/ermadmi78/kobby/generator/kotlin/entity.kt

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.github.ermadmi78.kobby.generator.kotlin
22

3+
import com.squareup.kotlinpoet.ANY
34
import com.squareup.kotlinpoet.FileSpec
45
import com.squareup.kotlinpoet.KModifier.ABSTRACT
56
import com.squareup.kotlinpoet.KModifier.OVERRIDE
@@ -134,37 +135,49 @@ private fun FileSpecBuilder.buildProjection(node: KobbyNode, layout: KotlinLayou
134135
addKdoc("%L", it)
135136
}
136137
node.fields.values.asSequence().filter { !it.isRequired }.forEach { field ->
137-
buildFunction(field.projectionFieldName) {
138-
addModifiers(ABSTRACT)
139-
if (field.isOverride) {
140-
addModifiers(OVERRIDE)
141-
}
142-
field.comments {
143-
addKdoc("%L", it)
138+
if (field.isProjectionPropertyEnabled) {
139+
buildProperty(field.projectionFieldName, ANY.nullable()) {
140+
addModifiers(ABSTRACT)
141+
if (field.isOverride) {
142+
addModifiers(OVERRIDE)
143+
}
144+
field.comments {
145+
addKdoc("%L", it)
146+
}
144147
}
148+
} else {
149+
buildFunction(field.projectionFieldName) {
150+
addModifiers(ABSTRACT)
151+
if (field.isOverride) {
152+
addModifiers(OVERRIDE)
153+
}
154+
field.comments {
155+
addKdoc("%L", it)
156+
}
145157

146-
field.arguments.values.asSequence()
147-
.filter { !field.isSelection || !it.isInitialized }
148-
.forEach { arg ->
149-
buildParameter(arg.name, arg.entityType) {
150-
if (!field.isOverride && arg.isInitialized && !field.isMultiBase) {
151-
defaultValue("null")
152-
}
153-
arg.comments {
154-
addKdoc("%L", it)
155-
}
156-
arg.defaultValue?.also { literal ->
157-
if (arg.comments.isNotEmpty()) {
158-
addKdoc("%L", " ");
158+
field.arguments.values.asSequence()
159+
.filter { !field.isSelection || !it.isInitialized }
160+
.forEach { arg ->
161+
buildParameter(arg.name, arg.entityType) {
162+
if (!field.isOverride && arg.isInitialized && !field.isMultiBase) {
163+
defaultValue("null")
164+
}
165+
arg.comments {
166+
addKdoc("%L", it)
167+
}
168+
arg.defaultValue?.also { literal ->
169+
if (arg.comments.isNotEmpty()) {
170+
addKdoc("%L", " ");
171+
}
172+
addKdoc("%L", "Default: $literal")
159173
}
160-
addKdoc("%L", "Default: $literal")
161174
}
162175
}
163-
}
164-
field.lambda?.also {
165-
buildParameter(it) {
166-
if (!field.isOverride && field.type.node.hasDefaults) {
167-
defaultValue("{}")
176+
field.lambda?.also {
177+
buildParameter(it) {
178+
if (!field.isOverride && field.type.node.hasDefaults) {
179+
defaultValue("{}")
180+
}
168181
}
169182
}
170183
}

kobby-generator-kotlin/src/main/kotlin/io/github/ermadmi78/kobby/generator/kotlin/impl.kt

Lines changed: 86 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -414,17 +414,44 @@ private fun FileSpecBuilder.buildSelection(node: KobbyNode, layout: KotlinLayout
414414
if (isQuery) {
415415
superclass(field.type.node.implProjectionClass)
416416
}
417+
418+
val parentFields = if (!isQuery) emptyMap() else field.type.node.fields.values.filter { it.isProjectionPropertyEnabled }.associateBy { it.name }
419+
fun KobbyArgument.isProjectionPropertyEnabled() = layout.entity.projection.enableNotationWithoutParentheses && name in parentFields
420+
417421
addSuperinterface(if (isQuery) field.queryClass else field.selectionClass)
418422
field.arguments.values.asSequence().filter { it.isInitialized }.forEach { arg ->
419423
val argType = if (dto.serialization.enabled) {
420424
arg.entityTypeWithSerializer
421425
} else {
422426
arg.entityType
423427
}
428+
429+
if (arg.isProjectionPropertyEnabled()) {
430+
buildProperty(arg.innerName, argType) {
431+
if (impl.internal) {
432+
addModifiers(INTERNAL)
433+
}
434+
mutable()
435+
initializer(field.innerInitializer)
436+
}
437+
}
438+
424439
buildProperty(arg.name, argType) {
425440
mutable()
426441
addModifiers(OVERRIDE)
427-
initializer("null")
442+
if (arg.isProjectionPropertyEnabled()) {
443+
val parentField = parentFields.getValue(arg.name)
444+
buildGetter {
445+
addStatement("super.${parentField.innerName}·=·${!parentField.isDefault}")
446+
addStatement("return·${arg.innerName}")
447+
}
448+
buildSetter {
449+
addParameter("value", argType)
450+
addStatement("${arg.innerName}·=·value")
451+
}
452+
} else {
453+
initializer("null")
454+
}
428455
}
429456
}
430457

@@ -436,7 +463,8 @@ private fun FileSpecBuilder.buildSelection(node: KobbyNode, layout: KotlinLayout
436463
val repeat = entity.selection.selectionArgument
437464
buildParameter(repeat, field.selectionClass)
438465
field.arguments.values.asSequence().filter { it.isInitialized }.forEach { arg ->
439-
addStatement("$repeat.${arg.name.escape()}·=·${arg.name.escape()}")
466+
val argName = if (arg.isProjectionPropertyEnabled()) arg.innerName else arg.name
467+
addStatement("$repeat.${arg.name.escape()}·=·${argName.escape()}")
440468
}
441469
}
442470
}
@@ -493,48 +521,58 @@ private fun FileSpecBuilder.buildProjection(node: KobbyNode, layout: KotlinLayou
493521
}
494522
}
495523

496-
buildFunction(field.projectionFieldName) {
497-
addModifiers(OVERRIDE)
498-
field.arguments.values.asSequence()
499-
.filter { !field.isSelection || !it.isInitialized }
500-
.forEach { arg ->
501-
buildParameter(arg.name, arg.entityType)
524+
if (field.isProjectionPropertyEnabled) {
525+
buildProperty(field.projectionFieldName, ANY.nullable()) {
526+
addModifiers(OVERRIDE)
527+
buildGetter {
528+
addStatement("${field.innerName}·=·${!field.isDefault}")
529+
addStatement("return·null")
502530
}
531+
}
532+
} else {
533+
buildFunction(field.projectionFieldName) {
534+
addModifiers(OVERRIDE)
535+
field.arguments.values.asSequence()
536+
.filter { !field.isSelection || !it.isInitialized }
537+
.forEach { arg ->
538+
buildParameter(arg.name, arg.entityType)
539+
}
503540

504-
field.lambda?.also {
505-
buildParameter(it)
506-
addStatement(
507-
"${field.innerName}·=·%T().%M(${it.first})",
508-
field.innerClass,
509-
MemberName("kotlin", "apply")
510-
)
511-
} ?: addStatement("${field.innerName}·=·${!field.isDefault}")
541+
field.lambda?.also {
542+
buildParameter(it)
543+
addStatement(
544+
"${field.innerName}·=·%T().%M(${it.first})",
545+
field.innerClass,
546+
MemberName("kotlin", "apply")
547+
)
548+
} ?: addStatement("${field.innerName}·=·${!field.isDefault}")
512549

513-
field.arguments.values.asSequence()
514-
.filter { !field.isSelection || !it.isInitialized }
515-
.forEach { arg ->
516-
addStatement("${arg.innerName}·=·${arg.name.escape()}")
517-
}
550+
field.arguments.values.asSequence()
551+
.filter { !field.isSelection || !it.isInitialized }
552+
.forEach { arg ->
553+
addStatement("${arg.innerName}·=·${arg.name.escape()}")
554+
}
518555

519-
if (node.kind == INTERFACE || node.kind == UNION) {
520-
node.subObjects { subObject ->
521-
addStatement("")
556+
if (node.kind == INTERFACE || node.kind == UNION) {
557+
node.subObjects { subObject ->
558+
addStatement("")
522559

523-
val subField = subObject.fields[field.name] ?: invalidSchema(
524-
"The object type '${subObject.name}' does not have a field '${field.name}' " +
525-
"required via interface '${node.name}'"
526-
)
527-
addStatement("${subObject.innerProjectionOnName}.${subField.innerName}·=·${field.innerName}")
528-
subField.arguments.values.asSequence()
529-
.filter { !subField.isSelection || !it.isInitialized }
530-
.forEach { subArg ->
531-
addStatement("${subObject.innerProjectionOnName}.${subArg.innerName}·=·${subArg.name.escape()}")
532-
}
533-
}
560+
val subField = subObject.fields[field.name] ?: invalidSchema(
561+
"The object type '${subObject.name}' does not have a field '${field.name}' " +
562+
"required via interface '${node.name}'"
563+
)
564+
addStatement("${subObject.innerProjectionOnName}.${subField.innerName}·=·${field.innerName}")
565+
subField.arguments.values.asSequence()
566+
.filter { !subField.isSelection || !it.isInitialized }
567+
.forEach { subArg ->
568+
addStatement("${subObject.innerProjectionOnName}.${subArg.innerName}·=·${subArg.name.escape()}")
569+
}
570+
}
534571

535-
if (node.kind == INTERFACE && !field.isDefault) {
536-
addStatement("")
537-
addStatement("${impl.interfaceIgnore.first}·+=·%S", field.name)
572+
if (node.kind == INTERFACE && !field.isDefault) {
573+
addStatement("")
574+
addStatement("${impl.interfaceIgnore.first}·+=·%S", field.name)
575+
}
538576
}
539577
}
540578
}
@@ -608,10 +646,18 @@ private fun FileSpecBuilder.buildProjection(node: KobbyNode, layout: KotlinLayou
608646
}
609647
}
610648
} else {
611-
addStatement("$repeat.${field.projectionFieldName.escape()}($args)")
649+
addStatement(
650+
buildString {
651+
append(repeat)
652+
append('.')
653+
append(field.projectionFieldName.escape())
654+
if (!field.isProjectionPropertyEnabled) {
655+
append("($args)")
656+
}
657+
}
658+
)
612659
}
613660
}
614-
615661
}
616662

617663
node.subObjects { subObject ->

kobby-generator-kotlin/src/main/kotlin/io/github/ermadmi78/kobby/generator/kotlin/layout.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ data class KotlinLayout(
396396
get() = (field.name + field.number + name._capitalize())
397397
.decorate(impl.innerDecoration)
398398

399+
internal val KobbyField.isProjectionPropertyEnabled: Boolean
400+
get() = entity.projection.enableNotationWithoutParentheses && !isDefault && isProperty
401+
399402
// *****************************************************************************************************************
400403
// Context
401404
// *****************************************************************************************************************
@@ -606,7 +609,8 @@ class KotlinEntityProjectionLayout(
606609
val minimizeFun: String,
607610
val qualificationDecoration: Decoration,
608611
val qualifiedProjectionDecoration: Decoration,
609-
val onDecoration: Decoration
612+
val onDecoration: Decoration,
613+
val enableNotationWithoutParentheses: Boolean
610614
)
611615

612616
class KotlinEntitySelectionLayout(

kobby-generator-kotlin/src/test/kotlin/io/github/ermadmi78/kobby/generator/kotlin/SchemaValidationTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ class SchemaValidationTest {
8585
"__minimize",
8686
Decoration(null, "Qualification"),
8787
Decoration(null, "QualifiedProjection"),
88-
Decoration("__on", null)
88+
Decoration("__on", null),
89+
false
8990
),
9091
KotlinEntitySelectionLayout(
9192
Decoration(null, "Selection"),

kobby-gradle-plugin/src/main/kotlin/io/github/ermadmi78/kobby/KobbyKotlinExtention.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,13 @@ open class KobbyKotlinEntityProjectionExtension {
701701

702702
/** Postfix for qualification functions */
703703
var onPostfix: String? = null
704+
705+
/**
706+
* Enable notation without parentheses for projection field getters
707+
*
708+
* Default: false
709+
*/
710+
var enableNotationWithoutParentheses: Boolean? = false
704711
}
705712

706713
/**

kobby-gradle-plugin/src/main/kotlin/io/github/ermadmi78/kobby/KobbyPlugin.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,9 @@ class KobbyPlugin : Plugin<Project> {
364364
onPostfix?.also {
365365
kotlinTask.entityOnPostfix.convention(it)
366366
}
367+
enableNotationWithoutParentheses?.also {
368+
kotlinTask.enableNotationWithoutParentheses.convention(it)
369+
}
367370
}
368371
selectionExtension.valueOrNull?.apply {
369372
selectionPrefix?.also {

kobby-gradle-plugin/src/main/kotlin/io/github/ermadmi78/kobby/task/KobbyKotlin.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,14 @@ open class KobbyKotlin : DefaultTask() {
813813
)
814814
val adapterKtorReceiveTimeoutMillis: Property<Long> = project.objects.property(Long::class.java)
815815

816+
@Input
817+
@Optional
818+
@Option(
819+
option = "enableNotationWithoutParentheses",
820+
description = "Is notation without parentheses for projection field getters enabled (default false)"
821+
)
822+
val enableNotationWithoutParentheses: Property<Boolean> = project.objects.property(Boolean::class.java)
823+
816824
@OutputDirectory
817825
val outputDirectory: DirectoryProperty = project.objects.directoryProperty()
818826

@@ -920,6 +928,7 @@ open class KobbyKotlin : DefaultTask() {
920928
adapterKtorPackageName.convention("adapter.ktor")
921929
adapterKtorPostfix.convention("KtorAdapter")
922930
adapterKtorReceiveTimeoutMillis.convention(10_000L)
931+
enableNotationWithoutParentheses.convention(false)
923932

924933
outputDirectory.convention(project.layout.buildDirectory.dir("generated/sources/kobby/main/kotlin"))
925934
}
@@ -1061,7 +1070,8 @@ open class KobbyKotlin : DefaultTask() {
10611070
entityMinimizeFun.get(),
10621071
Decoration(entityQualificationPrefix.orNull, entityQualificationPostfix.orNull),
10631072
Decoration(entityQualifiedProjectionPrefix.orNull, entityQualifiedProjectionPostfix.orNull),
1064-
Decoration(entityOnPrefix.orNull, entityOnPostfix.orNull)
1073+
Decoration(entityOnPrefix.orNull, entityOnPostfix.orNull),
1074+
enableNotationWithoutParentheses.get()
10651075
),
10661076
KotlinEntitySelectionLayout(
10671077
Decoration(entitySelectionPrefix.orNull, entitySelectionPostfix.orNull),

0 commit comments

Comments
 (0)