Skip to content

Commit 44e6650

Browse files
stabbylambdasugarmanzphubbard
authored
Switch to KotlinPoet Code Generation (#28)
* conditionally sign * wip * wip on version catalog * wip ksp * finish compiler plugin re-org * fix all tests for compiler plugin and enhance validations * re-enable docs + example-application * gradle plugin * maven plugin * fix some links * fix gradle and maven readmes and lint * try to fix signing * fix dependency * hacky way to fix test dependency * api dump * remove unnecessary option * add missing deps to version catalog * Get SyncHook working with Kotlin Poet * Implement AsyncSeriesHook * SyncBailHook and AsyncSeriesBailHook * SyncWaterfallHook * SyncLoopHook * AsyncParallelBailHook * Make all the tests pass! 🎉 * Cleanup * compiler-plugin -> processor * lint,dump,knit * make knit happy (and me sad :() * upgrade knit * Fix the generics test * upgrade arrow meta and handle no requested version a bit more gracefully * remove example-library api folder * Remove all the string-based codegen * Fix compiler issues * Make the apiCheck happy Co-authored-by: Paul Hubbard <[email protected]> * Make the linter happy Co-authored-by: Paul Hubbard <[email protected]> * Remove all the excess Poet descriptors Co-authored-by: Paul Hubbard <[email protected]> * Pass TypeResolvers through to the signature * Move all KSP -> Poet stuff into the validators * Split all the ksp/poet stuff into different parts * Clean up duplicate cases * Fix external api * More cleanup * Switch from withEither to andThen Co-authored-by: Jeremiah Zucker <[email protected]> Co-authored-by: Paul Hubbard <[email protected]>
1 parent 0b9f36b commit 44e6650

File tree

11 files changed

+316
-244
lines changed

11 files changed

+316
-244
lines changed

hooks/src/test/kotlin/com/intuit/hooks/SyncWaterfallHookTests.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ class SyncWaterfallHookTests {
3333
@Test
3434
fun `waterfall taps work with arity 2`() {
3535
val h = Hook2<String, Int>()
36-
h.tap("continue") { _, x, y -> "$x David" }
37-
h.tap("continue again") { _, x, y -> "$x Jeremiah" }
36+
h.tap("continue") { _, x, _ -> "$x David" }
37+
h.tap("continue again") { _, x, _ -> "$x Jeremiah" }
3838

3939
val result = h.call("Kian", 3)
4040
Assertions.assertEquals("Kian David Jeremiah", result)

processor/api/processor.api

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
public final class com/intuit/hooks/plugin/ksp/HooksProcessor : com/google/devtools/ksp/processing/SymbolProcessor {
22
public fun <init> (Lcom/google/devtools/ksp/processing/CodeGenerator;Lcom/google/devtools/ksp/processing/KSPLogger;)V
3-
public fun finish ()V
4-
public fun onError ()V
53
public fun process (Lcom/google/devtools/ksp/processing/Resolver;)Ljava/util/List;
64
}
75

processor/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
dependencies {
22
implementation(libs.kotlin.stdlib)
33
implementation(libs.ksp.spa)
4-
// implementation(libs.ksp.poet)
4+
implementation(libs.ksp.poet)
55
implementation(libs.arrow.core)
66

77
testImplementation(project(":hooks"))
Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,60 @@
11
package com.intuit.hooks.plugin.codegen
22

3-
internal data class HookMember(
3+
import com.squareup.kotlinpoet.*
4+
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
5+
6+
internal data class HooksContainer(
47
val name: String,
5-
val visibility: String,
6-
)
8+
val originalClassName: ClassName,
9+
val typeSpecKind: TypeSpec.Kind,
10+
val resolvedPackageName: String?,
11+
val visibilityModifier: KModifier,
12+
val typeArguments: List<TypeVariableName>,
13+
val hooks: List<HookInfo>
14+
) {
15+
val superclass get() = originalClassName.let {
16+
if (typeArguments.isNotEmpty()) {
17+
it.parameterizedBy(typeArguments)
18+
} else
19+
it
20+
}
21+
}
722

823
internal data class HookSignature(
9-
val text: String,
24+
val hookFunctionSignatureTypeText: String,
1025
val isSuspend: Boolean,
11-
val returnType: String,
12-
/** For hooks that return a wrapped result, like [BailResult], this is the inner type */
13-
val returnTypeType: String?,
26+
val returnType: TypeName,
27+
val returnTypeType: TypeName?,
28+
val hookFunctionSignatureType: TypeName,
1429
) {
15-
val nullableReturnTypeType = "${returnTypeType}${if (returnTypeType?.last() == '?') "" else "?"}"
16-
17-
override fun toString() = text
30+
val nullableReturnTypeType: TypeName get() {
31+
requireNotNull(returnTypeType)
32+
return returnTypeType.copy(nullable = true)
33+
}
34+
override fun toString(): String = hookFunctionSignatureTypeText
1835
}
1936

2037
internal class HookParameter(
2138
val name: String?,
22-
val type: String,
39+
val type: TypeName,
2340
val position: Int,
24-
)
25-
26-
internal val HookParameter.withType get() = "$withoutType: $type"
27-
internal val HookParameter.withoutType get() = name ?: "p$position"
41+
) {
42+
val withType get() = "$withoutType: $type"
43+
val withoutType get() = name ?: "p$position"
44+
}
2845

2946
internal data class HookInfo(
30-
val property: HookMember,
47+
val property: String,
3148
val hookType: HookType,
3249
val hookSignature: HookSignature,
3350
val params: List<HookParameter>,
51+
val propertyVisibility: KModifier
3452
) {
3553
val zeroArity = params.isEmpty()
3654
val isAsync = hookType.properties.contains(HookProperty.Async)
3755
}
3856

39-
internal val HookInfo.tapMethod get() = if (!zeroArity) """
40-
public fun tap(name: String, f: $hookSignature): String? = tap(name, generateRandomId(), f)
41-
public fun tap(name: String, id: String, f: $hookSignature): String? = super.tap(name, id) { _: HookContext, $paramsWithTypes -> f($paramsWithoutTypes) }
42-
""".trimIndent() else ""
4357
internal val HookInfo.paramsWithTypes get() = params.joinToString(transform = HookParameter::withType)
4458
internal val HookInfo.paramsWithoutTypes get() = params.joinToString(transform = HookParameter::withoutType)
45-
internal fun HookInfo.generateClass() = this.hookType.generateClass(this)
46-
internal fun HookInfo.generateProperty() = (if (hookType == HookType.AsyncParallelBailHook) "@kotlinx.coroutines.ExperimentalCoroutinesApi\n" else "") +
47-
"override val ${property.name}: $className = $className()"
48-
internal fun HookInfo.generateImports(): List<String> = emptyList()
49-
5059
internal val HookInfo.superType get() = this.hookType.toString()
51-
52-
internal val HookInfo.className get() = "${property.name.replaceFirstChar(Char::titlecase)}$superType"
53-
internal val HookInfo.typeParameter get() = "(${if (isAsync) "suspend " else ""}(HookContext, $paramsWithTypes) -> ${hookSignature.returnType})"
54-
internal val HookInfo.interceptParameter get() = "${if (isAsync) "suspend " else ""}(HookContext, $paramsWithTypes) -> Unit"
60+
internal val HookInfo.className get() = "${property.replaceFirstChar(Char::titlecase)}$superType"

processor/src/main/kotlin/com/intuit/hooks/plugin/codegen/HookType.kt

Lines changed: 10 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -8,112 +8,16 @@ internal sealed class HookProperty {
88
}
99

1010
internal enum class HookType(vararg val properties: HookProperty) {
11-
SyncHook {
12-
override fun generateClass(info: HookInfo): String {
13-
// todo: potentially protected
14-
return """|${info.property.visibility} inner class ${info.className} : ${info.superType}<${info.typeParameter}>() {
15-
| public fun call(${info.paramsWithTypes}): Unit = super.call { f, context -> f(context, ${info.paramsWithoutTypes}) }
16-
| ${info.tapMethod}
17-
|}"""
18-
}
19-
},
20-
SyncBailHook(HookProperty.Bail) {
21-
override fun generateClass(info: HookInfo): String {
22-
// todo: Potentially protected
23-
return """|${info.property.visibility} inner class ${info.className} : ${info.superType}<${info.typeParameter}, ${info.hookSignature.returnTypeType}>() {
24-
| public fun call(${info.paramsWithTypes}): ${info.hookSignature.nullableReturnTypeType} = super.call { f, context -> f(context, ${info.paramsWithoutTypes}) }
25-
| ${info.tapMethod}
26-
|}"""
27-
}
28-
},
29-
SyncWaterfallHook(HookProperty.Waterfall) {
30-
override fun generateClass(info: HookInfo): String {
31-
val accumulatorName = info.params.first().withoutType
32-
return """|${info.property.visibility} inner class ${info.className} : ${info.superType}<${info.typeParameter}, ${info.params.first().type}>() {
33-
| public fun call(${info.paramsWithTypes}): ${info.hookSignature.returnType} = super.call($accumulatorName,
34-
| invokeTap = { f, $accumulatorName, context -> f(context, ${info.paramsWithoutTypes}) },
35-
| invokeInterceptor = { f, context -> f(context, ${info.paramsWithoutTypes})}
36-
| )
37-
| ${info.tapMethod}
38-
|}"""
39-
}
40-
},
41-
42-
SyncLoopHook(HookProperty.Loop) {
43-
override fun generateClass(info: HookInfo): String {
44-
return """|${info.property.visibility} inner class ${info.className}: ${info.superType}<${info.typeParameter}, ${info.interceptParameter}>() {
45-
| public fun call(${info.paramsWithTypes}): Unit = super.call(
46-
| invokeTap = { f, context -> f(context, ${info.paramsWithoutTypes}) },
47-
| invokeInterceptor = { f, context -> f(context, ${info.paramsWithoutTypes}) }
48-
| )
49-
| ${info.tapMethod}
50-
|}"""
51-
}
52-
},
53-
54-
AsyncParallelHook(HookProperty.Async) {
55-
override fun generateClass(info: HookInfo): String {
56-
return """|${info.property.visibility} inner class ${info.className}: ${info.superType}<${info.typeParameter}>() {
57-
| public suspend fun call(${info.paramsWithTypes}): Unit = super.call { f, context -> f(context, ${info.paramsWithoutTypes}) }
58-
| ${info.tapMethod}
59-
|}"""
60-
}
61-
},
62-
63-
AsyncParallelBailHook(HookProperty.Async, HookProperty.Bail) {
64-
override fun generateClass(info: HookInfo): String {
65-
return """|@kotlinx.coroutines.ExperimentalCoroutinesApi
66-
|${info.property.visibility} inner class ${info.className}: ${info.superType}<${info.typeParameter}, ${info.hookSignature.returnTypeType}>() {
67-
| public suspend fun call(concurrency: Int, ${info.paramsWithTypes}): ${info.hookSignature.nullableReturnTypeType} = super.call(concurrency) { f, context -> f(context, ${info.paramsWithoutTypes}) }
68-
| ${info.tapMethod}
69-
|}"""
70-
}
71-
},
72-
73-
AsyncSeriesHook(HookProperty.Async) {
74-
override fun generateClass(info: HookInfo): String {
75-
return """|${info.property.visibility} inner class ${info.className}: ${info.superType}<${info.typeParameter}>() {
76-
| public suspend fun call(${info.paramsWithTypes}): Unit = super.call { f, context -> f(context, ${info.paramsWithoutTypes}) }
77-
| ${info.tapMethod}
78-
|}"""
79-
}
80-
},
81-
82-
AsyncSeriesBailHook(HookProperty.Async, HookProperty.Bail) {
83-
override fun generateClass(info: HookInfo): String {
84-
return """|${info.property.visibility} inner class ${info.className}: ${info.superType}<${info.typeParameter}, ${info.hookSignature.returnTypeType}>() {
85-
| public suspend fun call(${info.paramsWithTypes}): ${info.hookSignature.nullableReturnTypeType} = super.call { f, context -> f(context, ${info.paramsWithoutTypes}) }
86-
| ${info.tapMethod}
87-
|}"""
88-
}
89-
},
90-
91-
AsyncSeriesWaterfallHook(HookProperty.Async, HookProperty.Waterfall) {
92-
override fun generateClass(info: HookInfo): String {
93-
val accumulatorName = info.params.first().withoutType
94-
return """|${info.property.visibility} inner class ${info.className} : ${info.superType}<${info.typeParameter}, ${info.params.first().type}>() {
95-
| public suspend fun call(${info.paramsWithTypes}): ${info.hookSignature.returnType} = super.call($accumulatorName,
96-
| invokeTap = { f, $accumulatorName, context -> f(context, ${info.paramsWithoutTypes}) },
97-
| invokeInterceptor = { f, context -> f(context, ${info.paramsWithoutTypes})}
98-
| )
99-
| ${info.tapMethod}
100-
|}"""
101-
}
102-
},
103-
104-
AsyncSeriesLoopHook(HookProperty.Async, HookProperty.Loop) {
105-
override fun generateClass(info: HookInfo): String {
106-
return """|${info.property.visibility} inner class ${info.className}: ${info.superType}<${info.typeParameter}, ${info.interceptParameter}>() {
107-
| public suspend fun call(${info.paramsWithTypes}): Unit = super.call(
108-
| invokeTap = { f, context -> f(context, ${info.paramsWithoutTypes}) },
109-
| invokeInterceptor = { f, context -> f(context, ${info.paramsWithoutTypes}) }
110-
| )
111-
| ${info.tapMethod}
112-
|}"""
113-
}
114-
};
115-
116-
abstract fun generateClass(info: HookInfo): String
11+
SyncHook,
12+
SyncBailHook(HookProperty.Bail),
13+
SyncWaterfallHook(HookProperty.Waterfall),
14+
SyncLoopHook(HookProperty.Loop),
15+
AsyncParallelHook(HookProperty.Async),
16+
AsyncParallelBailHook(HookProperty.Async, HookProperty.Bail),
17+
AsyncSeriesHook(HookProperty.Async),
18+
AsyncSeriesBailHook(HookProperty.Async, HookProperty.Bail),
19+
AsyncSeriesWaterfallHook(HookProperty.Async, HookProperty.Waterfall),
20+
AsyncSeriesLoopHook(HookProperty.Async, HookProperty.Loop);
11721

11822
companion object {
11923
val annotationDslMarkers = values().map {

0 commit comments

Comments
 (0)