Skip to content
Merged
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
d107a6c
Working
lauzadis Aug 27, 2024
2c3b746
Add a comment
lauzadis Aug 27, 2024
84cb888
ktlint
lauzadis Aug 27, 2024
b7b6c5b
cleaning up
lauzadis Aug 27, 2024
c4a13ed
Add a comment
lauzadis Aug 27, 2024
fb145a8
Undo changes to BuilderRenderer
lauzadis Aug 27, 2024
1d0a599
Disable plugin test package (for now)
lauzadis Aug 27, 2024
bf459be
Bump network timeout for downloading Gradle
lauzadis Aug 27, 2024
c8830bc
Update comment
lauzadis Aug 27, 2024
3fff3df
ktlint
lauzadis Aug 28, 2024
1a2966b
revert
lauzadis Aug 28, 2024
524858c
clean up
lauzadis Aug 28, 2024
22a6ffe
fill out test
lauzadis Aug 28, 2024
ed889da
Fix rendering schema
lauzadis Aug 28, 2024
e94033e
Merge branch 'feat-ddb-mapper' of github.com:awslabs/aws-sdk-kotlin i…
lauzadis Aug 28, 2024
d627e30
Merge branch 'feat-ddb-mapper' of github.com:awslabs/aws-sdk-kotlin i…
lauzadis Aug 28, 2024
9707eb8
PR feedback
lauzadis Aug 28, 2024
4edb573
ktlint
lauzadis Aug 28, 2024
d7547d3
rename plugin extension
lauzadis Aug 28, 2024
b876ada
use default `false` member for `mutable` `Member`
lauzadis Aug 28, 2024
00e93a5
Fix new attributes
lauzadis Aug 28, 2024
6bc65fa
Commonize and remove unnecessary test
lauzadis Aug 28, 2024
0cbd2a8
commonize GradleRunner
lauzadis Aug 28, 2024
4e4e473
revert UserTest
lauzadis Aug 28, 2024
d232651
Add more codegen options
lauzadis Aug 28, 2024
a4fe6bc
All codegen options are working
lauzadis Aug 29, 2024
e5cd349
ktlintFormat
lauzadis Aug 29, 2024
9b2d135
Add TestKit tests for all codegen options
lauzadis Aug 29, 2024
d6fb875
ktlint
lauzadis Aug 29, 2024
3cc951a
clean up
lauzadis Aug 29, 2024
a2bcaec
ktlint
lauzadis Aug 29, 2024
9b9de55
Update KDocs
lauzadis Aug 29, 2024
8abda74
Add more KDocs
lauzadis Aug 29, 2024
6c49b8f
Running unit tests
lauzadis Aug 29, 2024
8ebc112
ktlint
lauzadis Aug 29, 2024
b503c15
Add KDocs
lauzadis Aug 29, 2024
dc30dfb
Implicit -> empty string
lauzadis Aug 29, 2024
9a58f5c
ktlint
lauzadis Aug 29, 2024
29d77c9
Commit latest
lauzadis Sep 6, 2024
6ce008d
Merge branch 'feat-ddb-mapper' of github.com:awslabs/aws-sdk-kotlin i…
lauzadis Sep 6, 2024
ae26243
Fix merge
lauzadis Sep 6, 2024
633268a
Commit latest
lauzadis Sep 6, 2024
5cad5bb
Get tests passing again
lauzadis Sep 6, 2024
74a6157
ListConverter
lauzadis Sep 6, 2024
731a679
ktlintformat
lauzadis Sep 6, 2024
80b8107
revert old changes
lauzadis Sep 6, 2024
43dc70c
Remove Dependencies.ALL_FILES in annotation processor
lauzadis Sep 9, 2024
f7bdf42
start work on configurable unknown attributes behavior
lauzadis Sep 9, 2024
bf216d6
Delete AnnotatedClassProperty
lauzadis Sep 9, 2024
537463d
Latest
lauzadis Sep 9, 2024
b49ff7e
ktlintFormat
lauzadis Sep 9, 2024
9f55f5a
Remove logs
lauzadis Sep 9, 2024
329ac7b
Update comment
lauzadis Sep 9, 2024
8cc75cc
Simplification
lauzadis Sep 10, 2024
8b19546
Save latest changes
lauzadis Sep 10, 2024
57c5f13
temp. commit
lauzadis Sep 12, 2024
a43e506
Merge branch 'feat-ddb-mapper' of github.com:awslabs/aws-sdk-kotlin i…
lauzadis Sep 12, 2024
8b989b9
Fix merge
lauzadis Sep 12, 2024
7481db6
ktlint
lauzadis Sep 13, 2024
f287fd5
Latest
lauzadis Sep 16, 2024
ee115d5
ktlintFormat
lauzadis Sep 16, 2024
b0e6b2b
1st pass
lauzadis Sep 16, 2024
976e9eb
Commonize `SchemaAttributes` and compare using `TypeRef` instead of p…
lauzadis Sep 18, 2024
57b7fea
Extract `shouldRenderValueConverter` to extension function
lauzadis Sep 18, 2024
39651f5
Fix parenthesis and args hacks
lauzadis Sep 18, 2024
c9cdf85
ktlint
lauzadis Sep 18, 2024
fc763db
api changes
lauzadis Sep 18, 2024
1974963
ItemConverter implements Converter<T, Item> and generate usage of Ite…
lauzadis Sep 18, 2024
b075c92
ktlintFormat
lauzadis Sep 18, 2024
4afaf33
Fix compile
lauzadis Sep 18, 2024
fe84aa6
Clean up rendering value converter
lauzadis Sep 18, 2024
7f667b8
Zip
lauzadis Sep 18, 2024
232b858
Remove removeSuffix
lauzadis Sep 18, 2024
7144d0c
Remove comment
lauzadis Sep 18, 2024
295aa45
Merge branch 'feat-ddb-mapper' of github.com:awslabs/aws-sdk-kotlin i…
lauzadis Sep 18, 2024
f45548d
revert back to zip
lauzadis Sep 19, 2024
eaab8cd
remove unnecessary parens
lauzadis Sep 19, 2024
10044f6
Add KDocs
lauzadis Sep 19, 2024
dff75e4
isGenericFor
lauzadis Sep 19, 2024
87f8d03
refactor renderValueConverter as a non-extension function
lauzadis Sep 19, 2024
d1ff053
ktlintFormat
lauzadis Sep 19, 2024
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
Expand Up @@ -23,7 +23,6 @@ public class AnnotationsProcessor(private val environment: SymbolProcessorEnviro
private var invoked = false
private val logger = environment.logger
private val codeGenerator = environment.codeGenerator
private val codeGeneratorFactory = CodeGeneratorFactory(codeGenerator, logger)

override fun process(resolver: Resolver): List<KSAnnotated> {
if (invoked) {
Expand All @@ -42,6 +41,9 @@ public class AnnotationsProcessor(private val environment: SymbolProcessorEnviro
.filterIsInstance<KSClassDeclaration>()
.filter { it.validate() }

val dependencies = Dependencies(aggregating = true, *(annotatedClasses.mapNotNull { it.containingFile }.toTypedArray()))
val codeGeneratorFactory = CodeGeneratorFactory(environment.codeGenerator, logger, dependencies)

HighLevelRenderer(annotatedClasses, logger, codeGeneratorFactory, getCodegenAttributes()).render()

return invalid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ package aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.rendering

import aws.sdk.kotlin.hll.codegen.core.CodeGeneratorFactory
import aws.sdk.kotlin.hll.codegen.rendering.RenderContext
import aws.sdk.kotlin.hll.codegen.util.plus
import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.AnnotationsProcessorOptions
import aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.DestinationPackage
import aws.smithy.kotlin.runtime.collections.Attributes
import aws.smithy.kotlin.runtime.collections.emptyAttributes
import aws.smithy.kotlin.runtime.collections.get
import aws.smithy.kotlin.runtime.collections.*
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.symbol.KSClassDeclaration

Expand All @@ -25,24 +24,47 @@ internal class HighLevelRenderer(
private val codegenAttributes: Attributes = emptyAttributes(),
) {
internal fun render() {
annotatedClasses.forEach {
logger.info("Processing annotation on ${it.simpleName}")
annotatedClasses.forEach { annotated ->
logger.info("Processing annotation on ${annotated.simpleName}")

val codegenPkg = when (val dstPkg = codegenAttributes[AnnotationsProcessorOptions.DestinationPackageAttribute]) {
is DestinationPackage.Relative -> "${it.packageName.asString()}.${dstPkg.pkg}"
is DestinationPackage.Relative -> "${annotated.packageName.asString()}.${dstPkg.pkg}"
is DestinationPackage.Absolute -> dstPkg.pkg
}

val attributes = codegenAttributes + (SchemaAttributes.ShouldRenderValueConverterAttribute to annotated.shouldRenderValueConverter)

val renderCtx = RenderContext(
logger,
codegenFactory,
codegenPkg,
"dynamodb-mapper-annotation-processor",
codegenAttributes,
attributes,
)

val annotation = SchemaRenderer(it, renderCtx)
val annotation = SchemaRenderer(annotated, renderCtx)
annotation.render()
}
}

// Value converters must be generated for any DynamoDbItem which is referenced by another DynamoDbItem
private val KSClassDeclaration.shouldRenderValueConverter: Boolean
get() = annotatedClasses.any { otherClass ->
val name = requireNotNull(qualifiedName).asString()

otherClass.getAllProperties().any { prop ->
val propType = prop.type.resolve()
val propName = requireNotNull(propType.declaration.qualifiedName).asString()

// If the property OR any of its arguments reference the annotated type
(propName == name) ||
(
propType.arguments.any { arg ->
val argType = arg.type?.resolve()
val argName = requireNotNull(argType?.declaration?.qualifiedName).asString()
argName == name
}
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Parentheses around propType.arguments.any call are unnecessary

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.rendering

import aws.smithy.kotlin.runtime.collections.AttributeKey

/**
* Internal schema code generation attributes
*/
internal object SchemaAttributes {
/**
* Whether a value converter should be generated for the class being processed
*/
internal val ShouldRenderValueConverterAttribute: AttributeKey<Boolean> = AttributeKey("ShouldRenderValueConverter")
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.getConstructors
import com.google.devtools.ksp.isAnnotationPresent
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSName
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.symbol.*

/**
* Renders the classes and objects required to make a class usable with the DynamoDbMapper such as schemas, builders, and converters.
Expand Down Expand Up @@ -57,19 +54,17 @@ internal class SchemaRenderer(
.getAllProperties()
.filterNot { it.modifiers.contains(Modifier.PRIVATE) || it.isAnnotationPresent(DynamoDbIgnore::class) }

private val annotatedProperties = properties.mapNotNull(AnnotatedClassProperty.Companion::from)

init {
check(annotatedProperties.count { it.isPk } == 1) {
check(properties.count { it.isPk } == 1) {
"Expected exactly one @DynamoDbPartitionKey annotation on a property"
}
check(annotatedProperties.count { it.isSk } <= 1) {
check(properties.count { it.isSk } <= 1) {
"Expected at most one @DynamoDbSortKey annotation on a property"
}
}

private val partitionKeyProp = annotatedProperties.single { it.isPk }
private val sortKeyProp = annotatedProperties.singleOrNull { it.isSk }
private val partitionKeyProp = properties.single { it.isPk }
private val sortKeyProp = properties.singleOrNull { it.isSk }

/**
* Skip rendering a class builder if:
Expand All @@ -94,6 +89,10 @@ internal class SchemaRenderer(
renderItemConverter()
}

if (ctx.attributes[SchemaAttributes.ShouldRenderValueConverterAttribute]) {
renderValueConverter()
}

renderSchema()

if (ctx.attributes[AnnotationsProcessorOptions.GenerateGetTableMethodAttribute]) {
Expand All @@ -117,15 +116,33 @@ internal class SchemaRenderer(
}

withBlock("descriptors = arrayOf(", "),") {
annotatedProperties.forEach {
properties.forEach {
renderAttributeDescriptor(it)
}
}
}
blankLine()
}

private fun renderAttributeDescriptor(prop: AnnotatedClassProperty) {
/**
* Render a [ValueConverter] for the current class by wrapping the generated/user-provided [ItemConverter]
* with our [ItemToValueConverter]
*/
private fun renderValueConverter() {
// TODO Offer alternate serialization options besides AttributeValue.M?
write(
"#Lval #L : #T = #T.#T(#T)",
ctx.attributes.visibility,
"${className}ValueConverter",
MapperTypes.Values.valueConverter(classType),
itemConverter,
TypeRef("aws.sdk.kotlin.hll.mapping.core.converters", "andThenTo"),
MapperTypes.Values.ItemToValueConverter,
)
blankLine()
}

private fun renderAttributeDescriptor(prop: KSPropertyDeclaration) {
withBlock("#T(", "),", MapperTypes.Items.AttributeDescriptor) {
write("#S,", prop.ddbName) // key
write("#L,", "$className::${prop.name}") // getter
Expand All @@ -137,18 +154,123 @@ internal class SchemaRenderer(
write("#L,", "$className::${prop.name}::set")
}

write("#T", prop.valueConverter) // converter
// converter
prop.type.resolve().renderValueConverter(this)
write("")
}
}

/**
* Renders a ValueConverter for the [KSType].
*
* Note: The ValueConverter(s) will be rendered without a newline in order to support deep recursion.
* Callers are responsible for adding a newline after the top-level invocation of this function.
*/
private fun KSType.renderValueConverter(writer: SchemaRenderer) {
writer.apply {
val ksType = this@renderValueConverter
val type = Type.from(ksType).copy(genericArgs = listOf())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: I see you're removing the generics from ksType in order to use an equality check with non-parameterized map/list types from Types.Kotlin.Collections. That works but it's not obvious at first read why you're stripping the generics. It might be cleaner to define an extension method Type.isGenericFor(other: Type) which performs an equality check on name, package, and nullability.


when {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: I can see how this works recursively and is better than trying to bundle multiple actual types into a single #T param but an in-class extension method that takes its own class instance as an argument feels awkward. Could this just be a regular instance method that takes the type as an argument:

private fun renderValueConverter(type: KSType)

// calling code
renderValueConverter(prop.type.resolve())

isEnum -> writeInline("#T()", MapperTypes.Values.Scalars.enumConverter(type))

// FIXME Handle multi-module codegen rather than assuming nested classes will be in the same [ctx.pkg]
isUserClass -> writeInline("#T", TypeRef(ctx.pkg, "${declaration.simpleName.asString()}ValueConverter"))

type == Types.Kotlin.Collections.List -> {
val listElementType = singleArgument()
writeInline("#T(", MapperTypes.Values.Collections.ListConverter)
listElementType.renderValueConverter(this)
writeInline(")")
}

type == Types.Kotlin.Collections.Map -> {
check(arguments.size == 2) { "Expected map type ${declaration.qualifiedName?.asString()} to have 2 arguments, got ${arguments.size}" }

val (keyType, valueType) = arguments.map {
checkNotNull(it.type?.resolve()) { "Failed to resolved argument type for $it" }
}

writeInline("#T(#T, ", MapperTypes.Values.Collections.MapConverter, keyType.mapKeyConverter)
valueType.renderValueConverter(this)
writeInline(")")
}

else -> writeInline(
"#T",
when (type) {
Types.Smithy.Instant -> MapperTypes.Values.SmithyTypes.DefaultInstantConverter
Types.Smithy.Url -> MapperTypes.Values.SmithyTypes.UrlConverter
Types.Smithy.Document -> MapperTypes.Values.SmithyTypes.DefaultDocumentConverter

Types.Kotlin.Boolean -> MapperTypes.Values.Scalars.BooleanConverter
Types.Kotlin.String -> MapperTypes.Values.Scalars.StringConverter
Types.Kotlin.CharArray -> MapperTypes.Values.Scalars.CharArrayConverter
Types.Kotlin.Char -> MapperTypes.Values.Scalars.CharConverter
Types.Kotlin.Byte -> MapperTypes.Values.Scalars.ByteConverter
Types.Kotlin.ByteArray -> MapperTypes.Values.Scalars.ByteArrayConverter
Types.Kotlin.Short -> MapperTypes.Values.Scalars.ShortConverter
Types.Kotlin.Int -> MapperTypes.Values.Scalars.IntConverter
Types.Kotlin.Long -> MapperTypes.Values.Scalars.LongConverter
Types.Kotlin.Double -> MapperTypes.Values.Scalars.DoubleConverter
Types.Kotlin.Float -> MapperTypes.Values.Scalars.FloatConverter
Types.Kotlin.UByte -> MapperTypes.Values.Scalars.UByteConverter
Types.Kotlin.UInt -> MapperTypes.Values.Scalars.UIntConverter
Types.Kotlin.UShort -> MapperTypes.Values.Scalars.UShortConverter
Types.Kotlin.ULong -> MapperTypes.Values.Scalars.ULongConverter

Types.Kotlin.Collections.Set -> singleArgument().setValueConverter
else -> error("Unsupported attribute type $this")
},
)
}
}
}

private val KSType.mapKeyConverter: Type
get() = when (val type = Type.from(this)) {
// String
Types.Kotlin.ByteArray -> MapperTypes.Values.Scalars.CharArrayToStringConverter
Types.Kotlin.Char -> MapperTypes.Values.Scalars.CharToStringConverter
Types.Kotlin.String -> MapperTypes.Values.Scalars.StringToStringConverter

// Number
Types.Kotlin.Byte -> MapperTypes.Values.Scalars.ByteToStringConverter
Types.Kotlin.Double -> MapperTypes.Values.Scalars.DoubleToStringConverter
Types.Kotlin.Float -> MapperTypes.Values.Scalars.FloatToStringConverter
Types.Kotlin.Int -> MapperTypes.Values.Scalars.IntToStringConverter
Types.Kotlin.Long -> MapperTypes.Values.Scalars.LongToStringConverter
Types.Kotlin.Short -> MapperTypes.Values.Scalars.ShortToStringConverter
Types.Kotlin.UByte -> MapperTypes.Values.Scalars.UByteToStringConverter
Types.Kotlin.UInt -> MapperTypes.Values.Scalars.UIntToStringConverter
Types.Kotlin.ULong -> MapperTypes.Values.Scalars.ULongToStringConverter
Types.Kotlin.UShort -> MapperTypes.Values.Scalars.UShortToStringConverter

// Boolean
Types.Kotlin.Boolean -> MapperTypes.Values.Scalars.BooleanToStringConverter
else -> error("Unsupported key type: $type")
}

private fun KSType.singleArgument(): KSType = checkNotNull(arguments.single().type?.resolve()) {
"Failed to resolve single argument type for ${this.declaration.qualifiedName?.asString()}"
}

private val AnnotatedClassProperty.valueConverter: Type
get() = when (typeName.asString()) {
"aws.smithy.kotlin.runtime.time.Instant" -> MapperTypes.Values.SmithyTypes.DefaultInstantConverter
"kotlin.Boolean" -> MapperTypes.Values.Scalars.BooleanConverter
"kotlin.Int" -> MapperTypes.Values.Scalars.IntConverter
"kotlin.String" -> MapperTypes.Values.Scalars.StringConverter
// TODO Add additional "standard" item converters
else -> error("Unsupported attribute type ${typeName.asString()}")
private val KSType.setValueConverter: Type
get() = when (Type.from(this)) {
Types.Kotlin.String -> MapperTypes.Values.Collections.StringSetConverter
Types.Kotlin.Char -> MapperTypes.Values.Collections.CharSetConverter
Types.Kotlin.CharArray -> MapperTypes.Values.Collections.CharArraySetConverter
Types.Kotlin.Byte -> MapperTypes.Values.Collections.ByteSetConverter
Types.Kotlin.Double -> MapperTypes.Values.Collections.DoubleSetConverter
Types.Kotlin.Float -> MapperTypes.Values.Collections.FloatSetConverter
Types.Kotlin.Int -> MapperTypes.Values.Collections.IntSetConverter
Types.Kotlin.Long -> MapperTypes.Values.Collections.LongSetConverter
Types.Kotlin.Short -> MapperTypes.Values.Collections.ShortSetConverter
Types.Kotlin.UByte -> MapperTypes.Values.Collections.UByteSetConverter
Types.Kotlin.UInt -> MapperTypes.Values.Collections.UIntSetConverter
Types.Kotlin.ULong -> MapperTypes.Values.Collections.ULongSetConverter
Types.Kotlin.UShort -> MapperTypes.Values.Collections.UShortSetConverter
else -> error("Unsupported set element $this")
}

private fun renderSchema() {
Expand All @@ -168,20 +290,20 @@ internal class SchemaRenderer(
blankLine()
}

private val AnnotatedClassProperty.keySpec: TypeRef
get() = when (typeName.asString()) {
private val KSPropertyDeclaration.keySpec: TypeRef
get() = when (typeName) {
"kotlin.ByteArray" -> Types.Kotlin.ByteArray
"kotlin.Int" -> Types.Kotlin.Number
"kotlin.String" -> Types.Kotlin.String
// TODO Handle ByteArray
else -> error("Unsupported key type ${typeName.asString()}, expected Int or String")
else -> error("Unsupported key type $typeName, expected ByteArray, Int, or String")
}

private val AnnotatedClassProperty.keySpecType: TypeRef
get() = when (typeName.asString()) {
private val KSPropertyDeclaration.keySpecType: TypeRef
get() = when (typeName) {
"kotlin.ByteArray" -> MapperTypes.Items.KeySpecByteArray
"kotlin.Int" -> MapperTypes.Items.KeySpecNumber
"kotlin.String" -> MapperTypes.Items.KeySpecString
// TODO Handle ByteArray
else -> error("Unsupported key type ${typeName.asString()}, expected Int or String")
else -> error("Unsupported key type $typeName, expected ByteArray, Int, or String")
}

private fun renderGetTable() {
Expand All @@ -204,22 +326,30 @@ internal class SchemaRenderer(
}
}

private data class AnnotatedClassProperty(val name: String, val typeRef: TypeRef, val ddbName: String, val typeName: KSName, val isPk: Boolean, val isSk: Boolean) {
companion object {
@OptIn(KspExperimental::class)
fun from(ksProperty: KSPropertyDeclaration) = ksProperty
.getter
?.returnType
?.resolve()
?.declaration
?.qualifiedName
?.let { typeName ->
val isPk = ksProperty.isAnnotationPresent(DynamoDbPartitionKey::class)
val isSk = ksProperty.isAnnotationPresent(DynamoDbSortKey::class)
val name = ksProperty.simpleName.getShortName()
val typeRef = Type.from(checkNotNull(ksProperty.type) { "Failed to determine class type for $name" })
val ddbName = ksProperty.getAnnotationsByType(DynamoDbAttribute::class).singleOrNull()?.name ?: name
AnnotatedClassProperty(name, typeRef, ddbName, typeName, isPk, isSk)
}
}
}
@OptIn(KspExperimental::class)
private val KSType.isUserClass: Boolean
get() = declaration.isAnnotationPresent(DynamoDbItem::class)

private val KSPropertyDeclaration.typeName: String
get() = checkNotNull(getter?.returnType?.resolve()?.declaration?.qualifiedName?.asString()) { "Failed to determine type name for $this" }

@OptIn(KspExperimental::class)
private val KSPropertyDeclaration.isPk: Boolean
get() = isAnnotationPresent(DynamoDbPartitionKey::class)

@OptIn(KspExperimental::class)
private val KSPropertyDeclaration.isSk: Boolean
get() = isAnnotationPresent(DynamoDbSortKey::class)

private val KSPropertyDeclaration.name: String
get() = simpleName.getShortName()

private val KSPropertyDeclaration.typeRef: TypeRef
get() = Type.from(type)

@OptIn(KspExperimental::class)
private val KSPropertyDeclaration.ddbName: String
get() = getAnnotationsByType(DynamoDbAttribute::class).singleOrNull()?.name ?: name

private val KSType.isEnum: Boolean
get() = (declaration as? KSClassDeclaration)?.classKind == ClassKind.ENUM_CLASS
Loading