diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/AnnotationsProcessor.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/AnnotationsProcessor.kt index 8dee96c1d94..d2c6a827264 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/AnnotationsProcessor.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/AnnotationsProcessor.kt @@ -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 { if (invoked) { @@ -42,6 +41,9 @@ public class AnnotationsProcessor(private val environment: SymbolProcessorEnviro .filterIsInstance() .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 diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/HighLevelRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/HighLevelRenderer.kt index bc0e6ab4a05..51f3fd12253 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/HighLevelRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/HighLevelRenderer.kt @@ -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 @@ -25,24 +24,45 @@ 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 + } + } + } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaAttributes.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaAttributes.kt new file mode 100644 index 00000000000..0195309ed81 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaAttributes.kt @@ -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 = AttributeKey("ShouldRenderValueConverter") +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt index 7114762aa44..f0dee7310a8 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/annotations/rendering/SchemaRenderer.kt @@ -4,10 +4,7 @@ */ package aws.sdk.kotlin.hll.dynamodbmapper.codegen.annotations.rendering -import aws.sdk.kotlin.hll.codegen.model.Member -import aws.sdk.kotlin.hll.codegen.model.Type -import aws.sdk.kotlin.hll.codegen.model.TypeRef -import aws.sdk.kotlin.hll.codegen.model.Types +import aws.sdk.kotlin.hll.codegen.model.* import aws.sdk.kotlin.hll.codegen.rendering.* import aws.sdk.kotlin.hll.codegen.util.visibility import aws.sdk.kotlin.hll.dynamodbmapper.* @@ -19,10 +16,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. @@ -57,19 +51,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: @@ -94,6 +86,10 @@ internal class SchemaRenderer( renderItemConverter() } + if (ctx.attributes[SchemaAttributes.ShouldRenderValueConverterAttribute]) { + renderValueConverter() + } + renderSchema() if (ctx.attributes[AnnotationsProcessorOptions.GenerateGetTableMethodAttribute]) { @@ -117,7 +113,7 @@ internal class SchemaRenderer( } withBlock("descriptors = arrayOf(", "),") { - annotatedProperties.forEach { + properties.forEach { renderAttributeDescriptor(it) } } @@ -125,7 +121,25 @@ internal class SchemaRenderer( 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 @@ -137,18 +151,121 @@ internal class SchemaRenderer( write("#L,", "$className::${prop.name}::set") } - write("#T", prop.valueConverter) // converter + // converter + renderValueConverter(prop.type.resolve()) + write("") } } - 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()}") + /** + * 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 renderValueConverter(ksType: KSType) { + val type = Type.from(ksType) + + when { + ksType.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] + ksType.isUserClass -> writeInline("#T", TypeRef(ctx.pkg, "${ksType.declaration.simpleName.asString()}ValueConverter")) + + type.isGenericFor(Types.Kotlin.Collections.List) -> { + val listElementType = ksType.singleArgument() + writeInline("#T(", MapperTypes.Values.Collections.ListConverter) + renderValueConverter(listElementType) + writeInline(")") + } + + type.isGenericFor(Types.Kotlin.Collections.Map) -> { + check(ksType.arguments.size == 2) { "Expected map type ${ksType.declaration.qualifiedName?.asString()} to have 2 arguments, got ${ksType.arguments.size}" } + + val (keyType, valueType) = ksType.arguments.map { + checkNotNull(it.type?.resolve()) { "Failed to resolved argument type for $it" } + } + + writeInline("#T(#T, ", MapperTypes.Values.Collections.MapConverter, keyType.mapKeyConverter) + renderValueConverter(valueType) + writeInline(")") + } + + type.isGenericFor(Types.Kotlin.Collections.Set) -> writeInline("#T", ksType.singleArgument().setValueConverter) + + 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 + + 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 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() { @@ -168,20 +285,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() { @@ -204,22 +321,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 diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperTypes.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperTypes.kt index d2c1f006905..61ed0b6cb43 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperTypes.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/model/MapperTypes.kt @@ -24,6 +24,7 @@ internal object MapperTypes { fun keySpec(keyType: TypeRef) = TypeRef(Pkg.Hl.Items, "KeySpec", genericArgs = listOf(keyType)) val KeySpecNumber = TypeRef(Pkg.Hl.Items, "KeySpec.Number") val KeySpecString = TypeRef(Pkg.Hl.Items, "KeySpec.String") + val KeySpecByteArray = TypeRef(Pkg.Hl.Items, "KeySpec.ByteArray") val AttributeDescriptor = TypeRef(Pkg.Hl.Items, "AttributeDescriptor") fun itemConverter(objectType: TypeRef) = TypeRef(Pkg.Hl.Items, "ItemConverter", genericArgs = listOf(objectType)) val SimpleItemConverter = TypeRef(Pkg.Hl.Items, "SimpleItemConverter") @@ -44,14 +45,70 @@ internal object MapperTypes { } object Values { + fun valueConverter(value: Type) = TypeRef(Pkg.Hl.Values, "ValueConverter", genericArgs = listOf(value)) + val ItemToValueConverter = TypeRef(Pkg.Hl.Values, "ItemToValueConverter") + + object Collections { + val ListConverter = TypeRef(Pkg.Hl.CollectionValues, "ListConverter") + val MapConverter = TypeRef(Pkg.Hl.CollectionValues, "MapConverter") + + val StringSetConverter = TypeRef(Pkg.Hl.CollectionValues, "StringSetConverter") + val CharSetConverter = TypeRef(Pkg.Hl.CollectionValues, "CharSetConverter") + val CharArraySetConverter = TypeRef(Pkg.Hl.CollectionValues, "CharArraySetConverter") + + val ByteSetConverter = TypeRef(Pkg.Hl.CollectionValues, "ByteSetConverter") + val DoubleSetConverter = TypeRef(Pkg.Hl.CollectionValues, "DoubleSetConverter") + val FloatSetConverter = TypeRef(Pkg.Hl.CollectionValues, "FloatSetConverter") + val IntSetConverter = TypeRef(Pkg.Hl.CollectionValues, "IntSetConverter") + val LongSetConverter = TypeRef(Pkg.Hl.CollectionValues, "LongSetConverter") + val ShortSetConverter = TypeRef(Pkg.Hl.CollectionValues, "ShortSetConverter") + + val UByteSetConverter = TypeRef(Pkg.Hl.CollectionValues, "UByteSetConverter") + val UIntSetConverter = TypeRef(Pkg.Hl.CollectionValues, "UIntSetConverter") + val ULongSetConverter = TypeRef(Pkg.Hl.CollectionValues, "ULongSetConverter") + val UShortSetConverter = TypeRef(Pkg.Hl.CollectionValues, "UShortSetConverter") + } + object Scalars { + fun enumConverter(enumType: Type) = TypeRef(Pkg.Hl.ScalarValues, "EnumConverter", genericArgs = listOf(enumType)) + val BooleanConverter = TypeRef(Pkg.Hl.ScalarValues, "BooleanConverter") - val IntConverter = TypeRef(Pkg.Hl.ScalarValues, "IntConverter") val StringConverter = TypeRef(Pkg.Hl.ScalarValues, "StringConverter") + val CharConverter = TypeRef(Pkg.Hl.ScalarValues, "CharConverter") + val CharArrayConverter = TypeRef(Pkg.Hl.ScalarValues, "CharArrayConverter") + + val ByteConverter = TypeRef(Pkg.Hl.ScalarValues, "ByteConverter") + val ByteArrayConverter = TypeRef(Pkg.Hl.ScalarValues, "ByteArrayConverter") + val DoubleConverter = TypeRef(Pkg.Hl.ScalarValues, "DoubleConverter") + val FloatConverter = TypeRef(Pkg.Hl.ScalarValues, "FloatConverter") + val IntConverter = TypeRef(Pkg.Hl.ScalarValues, "IntConverter") + val LongConverter = TypeRef(Pkg.Hl.ScalarValues, "LongConverter") + val ShortConverter = TypeRef(Pkg.Hl.ScalarValues, "ShortConverter") + val UByteConverter = TypeRef(Pkg.Hl.ScalarValues, "UByteConverter") + val UIntConverter = TypeRef(Pkg.Hl.ScalarValues, "UIntConverter") + val ULongConverter = TypeRef(Pkg.Hl.ScalarValues, "ULongConverter") + val UShortConverter = TypeRef(Pkg.Hl.ScalarValues, "UShortConverter") + + val BooleanToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "BooleanToStringConverter") + val CharArrayToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "TextConverters.CharArrayToStringConverter") + val CharToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "TextConverters.CharToStringConverter") + val StringToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "TextConverters.StringToStringConverter") + val ByteToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.ByteToStringConverter") + val DoubleToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.DoubleToStringConverter") + val FloatToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.FloatToStringConverter") + val IntToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.IntToStringConverter") + val LongToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.LongToStringConverter") + val ShortToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.ShortToStringConverter") + val UByteToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.UByteToStringConverter") + val UIntToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.UIntToStringConverter") + val ULongToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.ULongToStringConverter") + val UShortToStringConverter = TypeRef(Pkg.Hl.ScalarValues, "NumberConverters.UShortToStringConverter") } object SmithyTypes { - val DefaultInstantConverter = TypeRef(Pkg.Hl.SmithyTypeValues, "InstantConverters.Default") + val DefaultInstantConverter = TypeRef(Pkg.Hl.SmithyTypeValues, "InstantConverter.Default") + val UrlConverter = TypeRef(Pkg.Hl.SmithyTypeValues, "UrlConverter") + val DefaultDocumentConverter = TypeRef(Pkg.Hl.SmithyTypeValues, "DocumentConverter.Default") } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/HighLevelOpsProcessor.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/HighLevelOpsProcessor.kt index f09038ce5f4..861334a5b00 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/HighLevelOpsProcessor.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/HighLevelOpsProcessor.kt @@ -35,8 +35,7 @@ internal class HighLevelOpsProcessor(environment: SymbolProcessorEnvironment) : logger.info("Scanning low-level DDB client for operations and types") val operations = getOperations(resolver) - - val codegenFactory = CodeGeneratorFactory(codeGenerator, logger) + val codegenFactory = CodeGeneratorFactory(codeGenerator, logger) // FIXME Pass dependencies val ctx = RenderContext(logger, codegenFactory, pkg, "dynamodb-mapper-ops-codegen") HighLevelRenderer(ctx, operations).render() diff --git a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/OperationRenderer.kt b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/OperationRenderer.kt index cc2f35d685b..2eb0050becf 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/OperationRenderer.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-codegen/src/main/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/codegen/operations/rendering/OperationRenderer.kt @@ -90,10 +90,10 @@ internal class OperationRenderer( closeAndOpenBlock(") = #L {", operation.request.lowLevelName) members(MemberCodegenBehavior.PassThrough) { write("#1L = this@convert.#1L", name) } members(MemberCodegenBehavior.MapKeys) { - write("this@convert.#1L?.let { #1L = schema.converter.toItem(it, schema.keyAttributeNames) }", name) + write("this@convert.#1L?.let { #1L = schema.converter.convertTo(it, schema.keyAttributeNames) }", name) } members(MemberCodegenBehavior.MapAll) { - write("this@convert.#1L?.let { #1L = schema.converter.toItem(it) }", name) + write("this@convert.#1L?.let { #1L = schema.converter.convertTo(it) }", name) } members(MemberCodegenBehavior.Hoist) { write("this.#1L = #1L", name) } closeBlock("}") @@ -120,7 +120,7 @@ internal class OperationRenderer( members(MemberCodegenBehavior.PassThrough) { write("#1L = this@convert.#1L,", name) } members(MemberCodegenBehavior.MapKeys, MemberCodegenBehavior.MapAll) { - write("#1L = this@convert.#1L?.#2T()?.let(schema.converter::fromItem),", name, MapperTypes.Model.toItem) + write("#1L = this@convert.#1L?.#2T()?.let(schema.converter::convertFrom),", name, MapperTypes.Model.toItem) } } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/build.gradle.kts b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/build.gradle.kts index bb9485cd0c6..f3ac82b7c97 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/build.gradle.kts +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/build.gradle.kts @@ -101,14 +101,32 @@ val generateKotlinVersionFile by tasks.registering { } } +/** + * Create a file containing the smithy-kotlin version to use as a resource + * This saves us from having to manually change version numbers in multiple places + */ +val generateSmithyKotlinVersionFile by tasks.registering { + val resourcesDir = layout.buildDirectory.dir("resources/main/aws/sdk/kotlin/hll/dynamodbmapper/plugins").get() + val versionFile = file("$resourcesDir/smithy-kotlin-version.txt") + val versionCatalogFile = rootProject.file("gradle/libs.versions.toml") + inputs.file(versionCatalogFile) + outputs.file(versionFile) + sourceSets.main.get().output.dir(resourcesDir) + doLast { + versionFile.writeText(libs.smithy.kotlin.runtime.core.get().version.toString()) + } +} + tasks.withType { dependsOn(generateSdkVersionFile) dependsOn(generateKotlinVersionFile) + dependsOn(generateSmithyKotlinVersionFile) } tasks.withType { dependsOn(generateSdkVersionFile) dependsOn(generateKotlinVersionFile) + dependsOn(generateSmithyKotlinVersionFile) } /** @@ -136,13 +154,16 @@ tasks.register("publishSmithyKotlinToMavenLocal") { dependsOn(smithyKotlin.task(":runtime:observability:telemetry-defaults:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:protocol:aws-json-protocols:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:protocol:aws-protocol-core:publishToMavenLocal")) + dependsOn(smithyKotlin.task(":runtime:protocol:aws-xml-protocols:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:protocol:http-client-engines:http-client-engine-default:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:protocol:http-client-engines:http-client-engine-okhttp:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:protocol:http-client:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:protocol:http:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:runtime-core:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:serde:publishToMavenLocal")) + dependsOn(smithyKotlin.task(":runtime:serde:serde-form-url:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:serde:serde-json:publishToMavenLocal")) + dependsOn(smithyKotlin.task(":runtime:serde:serde-xml:publishToMavenLocal")) dependsOn(smithyKotlin.task(":runtime:smithy-client:publishToMavenLocal")) } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt index b376421c1f0..d538ea69608 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/kotlin/aws/sdk/kotlin/hll/dynamodbmapper/plugins/SchemaGeneratorPluginTest.kt @@ -23,6 +23,7 @@ class SchemaGeneratorPluginTest { private fun getResource(resourceName: String): String = checkNotNull(this::class.java.getResource(resourceName)?.readText()) { "Could not read $resourceName" } private val kotlinVersion = getResource("kotlin-version.txt") private val sdkVersion = getResource("sdk-version.txt") + private val smithyKotlinVersion = getResource("smithy-kotlin-version.txt") @BeforeEach fun setup() { @@ -404,4 +405,109 @@ class SchemaGeneratorPluginTest { """.trimIndent(), ) } + + @Test + fun testPrimitives() { + buildFile.appendText( + """ + dependencies { + implementation("aws.smithy.kotlin:runtime-core:$smithyKotlinVersion") + testImplementation(kotlin("test")) + } + """.trimIndent(), + ) + + createClassFile("standard-item-converters/src/Primitives") + + val buildResult = runner.build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), buildResult.task(":build")?.outcome) + val schemaFile = File(testProjectDir, "build/generated/ksp/main/kotlin/org/example/dynamodbmapper/generatedschemas/PrimitivesSchema.kt") + assertTrue(schemaFile.exists()) + + val testFile = File(testProjectDir, "src/test/kotlin/org/example/standard-item-converters/test/PrimitivesTest.kt") + testFile.ensureParentDirsCreated() + testFile.createNewFile() + testFile.writeText(getResource("/standard-item-converters/test/PrimitivesTest.kt")) + + val testResult = runner.withArguments("test").build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), testResult.task(":test")?.outcome) + } + + @Test + fun testLists() { + buildFile.appendText( + """ + dependencies { + testImplementation(kotlin("test")) + } + """.trimIndent(), + ) + + createClassFile("standard-item-converters/src/Lists") + + val buildResult = runner.build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), buildResult.task(":build")?.outcome) + val schemaFile = File(testProjectDir, "build/generated/ksp/main/kotlin/org/example/dynamodbmapper/generatedschemas/ListsSchema.kt") + assertTrue(schemaFile.exists()) + + val testFile = File(testProjectDir, "src/test/kotlin/org/example/standard-item-converters/test/ListsTest.kt") + testFile.ensureParentDirsCreated() + testFile.createNewFile() + testFile.writeText(getResource("/standard-item-converters/test/ListsTest.kt")) + + val testResult = runner.withArguments("test").build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), testResult.task(":test")?.outcome) + } + + @Test + fun testSets() { + buildFile.appendText( + """ + dependencies { + testImplementation(kotlin("test")) + } + """.trimIndent(), + ) + + createClassFile("standard-item-converters/src/Sets") + + val buildResult = runner.build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), buildResult.task(":build")?.outcome) + val schemaFile = File(testProjectDir, "build/generated/ksp/main/kotlin/org/example/dynamodbmapper/generatedschemas/SetsSchema.kt") + assertTrue(schemaFile.exists()) + + val testFile = File(testProjectDir, "src/test/kotlin/org/example/standard-item-converters/test/SetsTest.kt") + testFile.ensureParentDirsCreated() + testFile.createNewFile() + testFile.writeText(getResource("/standard-item-converters/test/SetsTest.kt")) + + val testResult = runner.withArguments("test").build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), testResult.task(":test")?.outcome) + } + + @Test + fun testMaps() { + buildFile.appendText( + """ + dependencies { + testImplementation(kotlin("test")) + } + """.trimIndent(), + ) + + createClassFile("standard-item-converters/src/Maps") + + val buildResult = runner.build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), buildResult.task(":build")?.outcome) + val schemaFile = File(testProjectDir, "build/generated/ksp/main/kotlin/org/example/dynamodbmapper/generatedschemas/MapsSchema.kt") + assertTrue(schemaFile.exists()) + + val testFile = File(testProjectDir, "src/test/kotlin/org/example/standard-item-converters/test/MapsTest.kt") + testFile.ensureParentDirsCreated() + testFile.createNewFile() + testFile.writeText(getResource("/standard-item-converters/test/MapsTest.kt")) + + val testResult = runner.withArguments("test").build() + assertContains(setOf(TaskOutcome.SUCCESS, TaskOutcome.UP_TO_DATE), testResult.task(":test")?.outcome) + } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Lists.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Lists.kt new file mode 100644 index 00000000000..566cf45a57d --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Lists.kt @@ -0,0 +1,62 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package org.example + +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItem +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey + +enum class EnumAnimals { + CAT, + DOG, + SHEEP, +} + +@DynamoDbItem +public data class Lists( + @DynamoDbPartitionKey var id: Int, + var listBoolean: List, + var listString: List, + var listCharArray: List, + var listChar: List, + var listByte: List, + var listByteArray: List, + var listShort: List, + var listInt: List, + var listLong: List, + var listDouble: List, + var listFloat: List, + var listUByte: List, + var listUInt: List, + var listUShort: List, + var listULong: List, + var listEnum: List, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Lists) return false + + if (id != other.id) return false + if (listBoolean != other.listBoolean) return false + if (listString != other.listString) return false + if (listCharArray.size != other.listCharArray.size) return false + if (!listCharArray.zip(other.listCharArray).all { (a, b) -> a.contentEquals(b) }) return false + if (listChar != other.listChar) return false + if (listByte != other.listByte) return false + if (listByteArray.size != other.listByteArray.size) return false + if (!listByteArray.zip(other.listByteArray).all { (a, b) -> a.contentEquals(b) }) return false + if (listShort != other.listShort) return false + if (listInt != other.listInt) return false + if (listLong != other.listLong) return false + if (listDouble != other.listDouble) return false + if (listFloat != other.listFloat) return false + if (listUByte != other.listUByte) return false + if (listUInt != other.listUInt) return false + if (listUShort != other.listUShort) return false + if (listULong != other.listULong) return false + if (listEnum != other.listEnum) return false + + return true + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Maps.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Maps.kt new file mode 100644 index 00000000000..8b67f3d2c59 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Maps.kt @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package org.example + +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItem +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey + +enum class EnumAnimals { + CAT, + DOG, + SHEEP, +} + +@DynamoDbItem +public data class Maps( + @DynamoDbPartitionKey var id: Int, + var mapStringString: Map, + var mapStringInt: Map, + var mapIntString: Map, + var mapLongInt: Map, + var mapStringBoolean: Map, + var mapStringListString: Map>, + var mapStringListMapStringString: Map>>, + var mapEnum: Map, +) diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Primitives.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Primitives.kt new file mode 100644 index 00000000000..ff2915ac50b --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Primitives.kt @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package org.example + +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItem +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey +import aws.smithy.kotlin.runtime.content.Document +import aws.smithy.kotlin.runtime.net.url.Url +import aws.smithy.kotlin.runtime.time.Instant + +enum class EnumAnimals { + CAT, + DOG, + SHEEP, +} + +@DynamoDbItem +public data class Primitives( + @DynamoDbPartitionKey var id: Int, + + /** + * Enums + */ + var animal: EnumAnimals, + + /** + * Primitives + */ + var boolean: Boolean, + var string: String, + var charArray: CharArray, + var char: Char, + var byte: Byte, + var byteArray: ByteArray, + var short: Short, + var int: Int, + var long: Long, + var double: Double, + var float: Float, + var uByte: UByte, + var uInt: UInt, + var uShort: UShort, + var uLong: ULong, + + /** + * Smithy types + */ + var instant: Instant, + var url: Url, + var document: Document, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Primitives) return false + + if (id != other.id) return false + if (animal != other.animal) return false + if (boolean != other.boolean) return false + if (string != other.string) return false + if (!charArray.contentEquals(other.charArray)) return false + if (char != other.char) return false + if (byte != other.byte) return false + if (!byteArray.contentEquals(other.byteArray)) return false + if (short != other.short) return false + if (int != other.int) return false + if (long != other.long) return false + if (double != other.double) return false + if (float != other.float) return false + if (uByte != other.uByte) return false + if (uInt != other.uInt) return false + if (uShort != other.uShort) return false + if (uLong != other.uLong) return false + if (instant.epochSeconds != other.instant.epochSeconds) return false + if (url != other.url) return false + if (document != other.document) return false + + return true + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Sets.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Sets.kt new file mode 100644 index 00000000000..65b1dec69dd --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/src/Sets.kt @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package org.example + +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbItem +import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbPartitionKey + +@DynamoDbItem +public data class Sets( + @DynamoDbPartitionKey var id: Int, + + /** + * Sets + */ + var setString: Set, + var setCharArray: Set, + var setChar: Set, + var setByte: Set, + var setDouble: Set, + var setFloat: Set, + + var setInt: Set, + var setLong: Set, + var setShort: Set, + var setUByte: Set, + var setUInt: Set, + var setULong: Set, + var setUShort: Set, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Sets) return false + + if (id != other.id) return false + if (setString != other.setString) return false + if (setCharArray.size != other.setCharArray.size) return false + if (!setCharArray.all { thisArray -> other.setCharArray.any { otherArray -> thisArray.contentEquals(otherArray) } }) return false + if (setChar != other.setChar) return false + if (setByte != other.setByte) return false + if (setDouble != other.setDouble) return false + if (setFloat != other.setFloat) return false + if (setInt != other.setInt) return false + if (setLong != other.setLong) return false + if (setShort != other.setShort) return false + if (setUByte != other.setUByte) return false + if (setUInt != other.setUInt) return false + if (setULong != other.setULong) return false + if (setUShort != other.setUShort) return false + + return true + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/ListsTest.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/ListsTest.kt new file mode 100644 index 00000000000..646da3942d7 --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/ListsTest.kt @@ -0,0 +1,38 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package org.example + +import org.example.dynamodbmapper.generatedschemas.ListsConverter +import kotlin.test.Test +import kotlin.test.assertEquals + +public class ListsTest { + @Test + fun converterTest() { + val lists = Lists( + id = 1, + listBoolean = listOf(false, true, false, true), + listString = listOf("foo", "bar", "baz"), + listCharArray = listOf(charArrayOf('a', 'b'), charArrayOf('c', 'd'), charArrayOf('e', 'f')), + listChar = listOf('a', 'b', 'c'), + listByte = listOf(1, 2, 3), + listByteArray = listOf(byteArrayOf(1, 2, 3), byteArrayOf(4, 5, 6)), + listShort = listOf(Short.MIN_VALUE, 0, Short.MAX_VALUE), + listInt = listOf(Int.MIN_VALUE, 0, Int.MAX_VALUE), + listLong = listOf(Long.MIN_VALUE, 0L, Long.MAX_VALUE), + listDouble = listOf(Double.MIN_VALUE, 0.0, Double.MAX_VALUE), + listFloat = listOf(Float.MIN_VALUE, 0f, Float.MAX_VALUE), + listUByte = listOf(UByte.MIN_VALUE, UByte.MAX_VALUE), + listUInt = listOf(UInt.MIN_VALUE, UInt.MAX_VALUE), + listUShort = listOf(UShort.MIN_VALUE, UShort.MAX_VALUE), + listULong = listOf(ULong.MIN_VALUE, ULong.MAX_VALUE), + listEnum = listOf(EnumAnimals.CAT, EnumAnimals.DOG, EnumAnimals.SHEEP), + ) + + val item = ListsConverter.convertTo(lists) + val converted = ListsConverter.convertFrom(item) + assertEquals(lists, converted) + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/MapsTest.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/MapsTest.kt new file mode 100644 index 00000000000..68148262c0f --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/MapsTest.kt @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package org.example + +import org.example.dynamodbmapper.generatedschemas.MapsConverter +import kotlin.test.Test +import kotlin.test.assertEquals + +public class MapsTest { + @Test + fun converterTest() { + val maps = Maps( + id = 1, + mapStringString = mapOf("key1" to "value1", "key2" to "value2"), + mapStringInt = mapOf("one" to 1, "two" to 2, "three" to 3), + mapIntString = mapOf(1 to "one", 2 to "two", 3 to "three"), + mapLongInt = mapOf(1L to 10, 2L to 20, 3L to 30), + mapStringBoolean = mapOf("true" to true, "false" to false), + mapStringListString = mapOf( + "fruits" to listOf("apple", "banana", "cherry"), + "colors" to listOf("red", "green", "blue"), + ), + mapStringListMapStringString = mapOf( + "person1" to listOf( + mapOf("name" to "John", "age" to "30"), + mapOf("city" to "New York", "country" to "USA"), + ), + "person2" to listOf( + mapOf("name" to "Alice", "age" to "25"), + mapOf("city" to "London", "country" to "UK"), + ), + ), + mapEnum = mapOf("pet1" to EnumAnimals.CAT, "pet2" to EnumAnimals.DOG, "pet3" to EnumAnimals.SHEEP), + ) + + val item = MapsConverter.convertTo(maps) + val converted = MapsConverter.convertFrom(item) + assertEquals(maps, converted) + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/PrimitivesTest.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/PrimitivesTest.kt new file mode 100644 index 00000000000..6566b72677a --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/PrimitivesTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package org.example + +import aws.smithy.kotlin.runtime.content.Document +import aws.smithy.kotlin.runtime.net.url.Url +import aws.smithy.kotlin.runtime.time.Instant +import org.example.dynamodbmapper.generatedschemas.PrimitivesConverter +import kotlin.test.Test +import kotlin.test.assertEquals + +public class PrimitivesTest { + @Test + fun converterTest() { + val primitive = Primitives( + id = 1, + animal = EnumAnimals.CAT, + boolean = true, + string = "string", + charArray = charArrayOf('a', 'b', 'c'), + char = 'b', + byte = 42, + byteArray = byteArrayOf(5, 4, 3, 2, 1), + short = Short.MAX_VALUE, + int = Int.MAX_VALUE, + long = Long.MAX_VALUE, + double = Double.MAX_VALUE, + float = Float.MAX_VALUE, + uByte = UByte.MAX_VALUE, + uInt = UInt.MAX_VALUE, + uShort = UShort.MAX_VALUE, + uLong = ULong.MAX_VALUE, + instant = Instant.now(), + url = Url.parse("https://aws.amazon.com"), + document = Document.Number(5), + ) + + val item = PrimitivesConverter.convertTo(primitive) + val converted = PrimitivesConverter.convertFrom(item) + assertEquals(primitive, converted) + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/SetsTest.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/SetsTest.kt new file mode 100644 index 00000000000..6465f1cbe5c --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/standard-item-converters/test/SetsTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package org.example + +import org.example.dynamodbmapper.generatedschemas.SetsConverter +import kotlin.test.Test +import kotlin.test.assertEquals + +public class SetsTest { + @Test + fun converterTest() { + val sets = Sets( + id = 1, + setString = setOf("one", "two", "three"), + setCharArray = setOf(charArrayOf('a', 'b'), charArrayOf('c', 'd')), + setChar = setOf('x', 'y', 'z'), + setByte = setOf(10, 20, 30), + setDouble = setOf(1.1, 2.2, 3.3), + setFloat = setOf(1.0f, 2.0f, 3.0f), + setInt = setOf(100, 200, 300), + setLong = setOf(1000L, 2000L, 3000L), + setShort = setOf(1000.toShort(), 2000.toShort(), 3000.toShort()), + setUByte = setOf(10u, 20u, 30u), + setUInt = setOf(100u, 200u, 300u), + setULong = setOf(1000uL, 2000uL, 3000uL), + setUShort = setOf(1000u.toUShort(), 2000u.toUShort(), 3000u.toUShort()), + ) + + val item = SetsConverter.convertTo(sets) + val converted = SetsConverter.convertFrom(item) + assertEquals(sets, converted) + } +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/tests/UserTest.kt b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/tests/UserTest.kt index 169310c1e8c..7a11c1e660a 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/tests/UserTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper-schema-generator-plugin/src/test/resources/tests/UserTest.kt @@ -14,7 +14,7 @@ class UserTest { @Test fun testConversion() { val user = User(123, "Steve", "Rogers", 84) - val converted = UserConverter.toItem(user) + val converted = UserConverter.convertTo(user) assertEquals( itemOf( @@ -26,7 +26,7 @@ class UserTest { converted, ) - val unconverted = UserConverter.fromItem(converted) + val unconverted = UserConverter.convertFrom(converted) assertEquals(user, unconverted) } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api b/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api index 52dee80cc72..85f10d86873 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api +++ b/hll/dynamodb-mapper/dynamodb-mapper/api/dynamodb-mapper.api @@ -496,28 +496,35 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/items/AttributeDescriptorKt public final class aws/sdk/kotlin/hll/dynamodbmapper/items/DocumentConverter : aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter { public static final field INSTANCE Laws/sdk/kotlin/hll/dynamodbmapper/items/DocumentConverter; - public fun fromItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Item;)Laws/smithy/kotlin/runtime/content/Document; - public synthetic fun fromItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Item;)Ljava/lang/Object; - public fun toItem (Laws/smithy/kotlin/runtime/content/Document;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; - public synthetic fun toItem (Ljava/lang/Object;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public fun convertFrom (Laws/sdk/kotlin/hll/dynamodbmapper/model/Item;)Laws/smithy/kotlin/runtime/content/Document; + public synthetic fun convertFrom (Ljava/lang/Object;)Ljava/lang/Object; + public fun convertTo (Laws/smithy/kotlin/runtime/content/Document;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public fun convertTo (Laws/smithy/kotlin/runtime/content/Document;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public synthetic fun convertTo (Ljava/lang/Object;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public synthetic fun convertTo (Ljava/lang/Object;)Ljava/lang/Object; + public synthetic fun convertTo (Ljava/lang/Object;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; } public final class aws/sdk/kotlin/hll/dynamodbmapper/items/HeterogeneousItemConverter : aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter { public fun (Lkotlin/jvm/functions/Function1;Ljava/lang/String;Ljava/util/Map;)V - public fun fromItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Item;)Ljava/lang/Object; + public fun convertFrom (Laws/sdk/kotlin/hll/dynamodbmapper/model/Item;)Ljava/lang/Object; + public synthetic fun convertFrom (Ljava/lang/Object;)Ljava/lang/Object; + public fun convertTo (Ljava/lang/Object;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public synthetic fun convertTo (Ljava/lang/Object;)Ljava/lang/Object; + public fun convertTo (Ljava/lang/Object;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; public final fun getSubConverters ()Ljava/util/Map; public final fun getTypeAttribute ()Ljava/lang/String; public final fun getTypeMapper ()Lkotlin/jvm/functions/Function1; - public fun toItem (Ljava/lang/Object;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; } -public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter { - public abstract fun fromItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Item;)Ljava/lang/Object; - public abstract fun toItem (Ljava/lang/Object;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; +public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter : aws/sdk/kotlin/hll/mapping/core/converters/Converter { + public abstract fun convertTo (Ljava/lang/Object;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public abstract fun convertTo (Ljava/lang/Object;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; } public final class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter$DefaultImpls { - public static synthetic fun toItem$default (Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter;Ljava/lang/Object;Ljava/util/Set;ILjava/lang/Object;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public static fun convertTo (Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter;Ljava/lang/Object;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public static synthetic fun convertTo$default (Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter;Ljava/lang/Object;Ljava/util/Set;ILjava/lang/Object;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; } public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema { @@ -573,9 +580,12 @@ public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/items/KeySpec$ public final class aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverter : aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter { public fun (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;[Laws/sdk/kotlin/hll/dynamodbmapper/items/AttributeDescriptor;)V - public fun fromItem (Laws/sdk/kotlin/hll/dynamodbmapper/model/Item;)Ljava/lang/Object; + public fun convertFrom (Laws/sdk/kotlin/hll/dynamodbmapper/model/Item;)Ljava/lang/Object; + public synthetic fun convertFrom (Ljava/lang/Object;)Ljava/lang/Object; + public fun convertTo (Ljava/lang/Object;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public synthetic fun convertTo (Ljava/lang/Object;)Ljava/lang/Object; + public fun convertTo (Ljava/lang/Object;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; public final fun getDescriptors ()Ljava/util/Map; - public fun toItem (Ljava/lang/Object;Ljava/util/Set;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; } public abstract interface class aws/sdk/kotlin/hll/dynamodbmapper/model/Index : aws/sdk/kotlin/hll/dynamodbmapper/model/IndexSpec, aws/sdk/kotlin/hll/dynamodbmapper/model/ItemSource, aws/sdk/kotlin/hll/dynamodbmapper/operations/IndexOperations { @@ -919,6 +929,14 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/pipeline/SerializeInputKt { public static final fun SerializeInput (Ljava/lang/Object;Laws/sdk/kotlin/hll/dynamodbmapper/items/ItemSchema;)Laws/sdk/kotlin/hll/dynamodbmapper/pipeline/SerializeInput; } +public final class aws/sdk/kotlin/hll/dynamodbmapper/values/ItemToValueConverter : aws/sdk/kotlin/hll/mapping/core/converters/Converter { + public static final field INSTANCE Laws/sdk/kotlin/hll/dynamodbmapper/values/ItemToValueConverter; + public fun convertFrom (Laws/sdk/kotlin/services/dynamodb/model/AttributeValue;)Laws/sdk/kotlin/hll/dynamodbmapper/model/Item; + public synthetic fun convertFrom (Ljava/lang/Object;)Ljava/lang/Object; + public fun convertTo (Laws/sdk/kotlin/hll/dynamodbmapper/model/Item;)Laws/sdk/kotlin/services/dynamodb/model/AttributeValue; + public synthetic fun convertTo (Ljava/lang/Object;)Ljava/lang/Object; +} + public final class aws/sdk/kotlin/hll/dynamodbmapper/values/NullableConverter : aws/sdk/kotlin/hll/mapping/core/converters/SplittingConverter { public fun (Lkotlin/reflect/KClass;)V public fun convertFrom (Laws/sdk/kotlin/services/dynamodb/model/AttributeValue;)Laws/sdk/kotlin/hll/mapping/core/util/Either; @@ -968,6 +986,7 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/values/collections/Primitiv public final class aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/BooleanConverterKt { public static final fun getBooleanConverter ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; + public static final fun getBooleanToStringConverter ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; } public final class aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/ByteArrayConverterKt { @@ -1017,6 +1036,7 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/TextConverte public static final field INSTANCE Laws/sdk/kotlin/hll/dynamodbmapper/values/scalars/TextConverters; public final fun getCharArrayToStringConverter ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; public final fun getCharToStringConverter ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; + public final fun getStringToStringConverter ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; } public final class aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/TextConvertersKt { @@ -1025,8 +1045,8 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/TextConverte public static final fun getStringConverter ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; } -public final class aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverter : aws/sdk/kotlin/hll/mapping/core/converters/Converter { - public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverter$Companion; +public final class aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentConverter : aws/sdk/kotlin/hll/mapping/core/converters/Converter { + public static final field Companion Laws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentConverter$Companion; public fun ()V public fun (Laws/sdk/kotlin/hll/mapping/core/converters/Converter;Laws/sdk/kotlin/hll/mapping/core/converters/Converter;Laws/sdk/kotlin/hll/mapping/core/converters/Converter;Laws/sdk/kotlin/hll/dynamodbmapper/values/NullableConverter;Laws/sdk/kotlin/hll/mapping/core/converters/Converter;Laws/sdk/kotlin/hll/mapping/core/converters/Converter;)V public synthetic fun (Laws/sdk/kotlin/hll/mapping/core/converters/Converter;Laws/sdk/kotlin/hll/mapping/core/converters/Converter;Laws/sdk/kotlin/hll/mapping/core/converters/Converter;Laws/sdk/kotlin/hll/dynamodbmapper/values/NullableConverter;Laws/sdk/kotlin/hll/mapping/core/converters/Converter;Laws/sdk/kotlin/hll/mapping/core/converters/Converter;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -1036,12 +1056,12 @@ public final class aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/Document public synthetic fun convertTo (Ljava/lang/Object;)Ljava/lang/Object; } -public final class aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverter$Companion { - public final fun getDefault ()Laws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverter; +public final class aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentConverter$Companion { + public final fun getDefault ()Laws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentConverter; } -public final class aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverters { - public static final field INSTANCE Laws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverters; +public final class aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverter { + public static final field INSTANCE Laws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverter; public final fun getDefault ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; public final fun getEpochMs ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; public final fun getEpochS ()Laws/sdk/kotlin/hll/mapping/core/converters/Converter; diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/DocumentConverter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/DocumentConverter.kt index a68891f4083..141ce49105f 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/DocumentConverter.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/DocumentConverter.kt @@ -14,17 +14,17 @@ import aws.smithy.kotlin.runtime.util.toNumber // FIXME Combine with DocumentValueConverter or refactor to commonize as much code as possible public object DocumentConverter : ItemConverter { - override fun fromItem(item: Item): Document = item + override fun convertFrom(to: Item): Document = to .mapValues { (_, attr) -> fromAttributeValue(attr) } .let(Document::Map) - override fun toItem(obj: Document, onlyAttributes: Set?): Item { - require(obj is Document.Map) + override fun convertTo(from: Document, onlyAttributes: Set?): Item { + require(from is Document.Map) val map = if (onlyAttributes == null) { - obj + from } else { - obj.filterKeys { it in onlyAttributes } + from.filterKeys { it in onlyAttributes } } return map.mapValues { (_, value) -> toAttributeValue(value) }.toItem() diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/HeterogeneousItemConverter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/HeterogeneousItemConverter.kt index bdb882a3a21..2caf7f3f32d 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/HeterogeneousItemConverter.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/HeterogeneousItemConverter.kt @@ -13,15 +13,15 @@ public class HeterogeneousItemConverter( public val typeAttribute: String, public val subConverters: Map>, ) : ItemConverter { - override fun fromItem(item: Item): T { - val attr = item[typeAttribute] ?: error("Missing $typeAttribute") + override fun convertFrom(to: Item): T { + val attr = to[typeAttribute] ?: error("Missing $typeAttribute") val typeValue = attr.asSOrNull() ?: error("No string value for $attr") val converter = subConverters[typeValue] ?: error("No converter for $typeValue") - return converter.fromItem(item) + return converter.convertFrom(to) } - override fun toItem(obj: T, onlyAttributes: Set?): Item { - val typeValue = typeMapper(obj) + override fun convertTo(from: T, onlyAttributes: Set?): Item { + val typeValue = typeMapper(from) val converter = subConverters[typeValue] ?: error("No converter for $typeValue") return buildItem { @@ -29,7 +29,7 @@ public class HeterogeneousItemConverter( put(typeAttribute, AttributeValue.S(typeValue)) } - putAll(converter.toItem(obj, onlyAttributes)) + putAll(converter.convertTo(from, onlyAttributes)) } } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter.kt index b12e80658e6..0eb13f4794f 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/ItemConverter.kt @@ -5,25 +5,13 @@ package aws.sdk.kotlin.hll.dynamodbmapper.items import aws.sdk.kotlin.hll.dynamodbmapper.model.Item +import aws.sdk.kotlin.hll.mapping.core.converters.Converter /** * Defines the logic for converting between objects and DynamoDB items * @param T The type of objects which will be converted */ -public interface ItemConverter { - /** - * Convert the given [item] to an object of type [T] - * @param item The item to convert to an object - * @return The object converted from [item] - */ - public fun fromItem(item: Item): T - - /** - * Convert the given [obj] of type [T] to an [Item] - * @param obj The object to convert to an item - * @param onlyAttributes Limit the attributes which are set in the item to those named. If not set, converts all - * attributes. - * @return The item converted from [obj] - */ - public fun toItem(obj: T, onlyAttributes: Set? = null): Item +public interface ItemConverter : Converter { + public fun convertTo(from: T, onlyAttributes: Set? = null): Item + public override fun convertTo(from: T): Item = convertTo(from, null) } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverter.kt index 5e3dbdb8ae8..3b768e9fef8 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverter.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverter.kt @@ -34,7 +34,7 @@ public class SimpleItemConverter( } } - override fun fromItem(item: Item): T { + override fun convertFrom(to: Item): T { val builder = builderFactory() /** @@ -50,7 +50,7 @@ public class SimpleItemConverter( fun AttributeDescriptor.fromAttributeValue(attr: AttributeValue) = builder.setter(converter.convertFrom(attr)) - item.forEach { (name, attr) -> + to.forEach { (name, attr) -> // TODO make behavior for unknown attributes configurable (ignore, exception, other?) descriptors[name]?.fromAttributeValue(attr) } @@ -58,7 +58,7 @@ public class SimpleItemConverter( return builder.build() } - override fun toItem(obj: T, onlyAttributes: Set?): Item { + override fun convertTo(from: T, onlyAttributes: Set?): Item { /** * This is a convenience function to keep the compile-time safety for type param `A`. Without this, the compiler * can't track generic types across multiple statements: @@ -70,7 +70,7 @@ public class SimpleItemConverter( * ``` */ fun AttributeDescriptor.toAttributeValue() = - converter.convertTo(getter(obj)) + converter.convertTo(getter(from)) val descriptors = if (onlyAttributes == null) { this.descriptors.values diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/ItemToValueConverter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/ItemToValueConverter.kt new file mode 100644 index 00000000000..578269f064f --- /dev/null +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/ItemToValueConverter.kt @@ -0,0 +1,25 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.hll.dynamodbmapper.values + +import aws.sdk.kotlin.hll.dynamodbmapper.model.Item +import aws.sdk.kotlin.hll.dynamodbmapper.model.toItem +import aws.sdk.kotlin.services.dynamodb.model.AttributeValue + +/** + * Converts between [Item] and [AttributeValue]. + * This converter is typically chained following an [ItemConverter] using the [andThenTo] extension function. + */ +public object ItemToValueConverter : ValueConverter { + /** + * Convert from [AttributeValue] to [Item] + */ + override fun convertFrom(to: AttributeValue): Item = to.asM().toItem() + + /** + * Convert [from] [Item] to [AttributeValue] + */ + override fun convertTo(from: Item): AttributeValue = AttributeValue.M(from) +} diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/BooleanConverter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/BooleanConverter.kt index 3ad4ed92380..cf86a72407c 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/BooleanConverter.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/BooleanConverter.kt @@ -13,3 +13,8 @@ import aws.sdk.kotlin.services.dynamodb.model.AttributeValue * [DynamoDB `BOOL` values](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes.Boolean) */ public val BooleanConverter: ValueConverter = Converter(AttributeValue::Bool, AttributeValue::asBool) + +/** + * Converts between [Boolean] and [String] + */ +public val BooleanToStringConverter: Converter = Converter({ it.toString() }, { it.toBoolean() }) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/TextConverters.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/TextConverters.kt index 866672f7da5..06a24101ab0 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/TextConverters.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/scalars/TextConverters.kt @@ -22,6 +22,11 @@ public object TextConverters { * Converts between [Char] and [String] */ public val CharToStringConverter: Converter = Converter(Char::toString, String::single) + + /** + * Converts between [String] and [String] + */ + public val StringToStringConverter: Converter = Converter({ it }, { it }) } /** diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverter.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentConverter.kt similarity index 95% rename from hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverter.kt rename to hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentConverter.kt index 1b4311b7d2c..cf1c577e04e 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverter.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentConverter.kt @@ -26,7 +26,7 @@ import aws.smithy.kotlin.runtime.content.Document * * [Document.List] ↔ [DynamoDB `L` values](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes.Document.List) * * [Document.Map] ↔ [DynamoDB `M` values](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes.Document.Map) */ -public class DocumentValueConverter( +public class DocumentConverter( private val numberConverter: ValueConverter = AutoNumberConverter, private val stringConverter: ValueConverter = StringConverter, private val booleanConverter: ValueConverter = BooleanConverter, @@ -40,9 +40,9 @@ public class DocumentValueConverter( public companion object { /** - * The default instance of [DocumentValueConverter] + * The default instance of [DocumentConverter] */ - public val Default: DocumentValueConverter = DocumentValueConverter() + public val Default: DocumentConverter = DocumentConverter() } override fun convertFrom(to: AttributeValue): Document = when (to) { diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverters.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverter.kt similarity index 98% rename from hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverters.kt rename to hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverter.kt index 7a6705e0e7f..fbcac84d692 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverters.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/src/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConverter.kt @@ -17,7 +17,7 @@ import aws.smithy.kotlin.runtime.time.fromEpochMilliseconds /** * Provides access to [ValueConverter] types for various [Instant] representations */ -public object InstantConverters { +public object InstantConverter { /** * Converts between [Instant] and * [DynamoDB `N` values](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes.Number) diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverterTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverterTest.kt index 91b932c5831..dc9e33d22c4 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverterTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/items/SimpleItemConverterTest.kt @@ -23,14 +23,14 @@ class SimpleItemConverterTest { ) val foo = Product(42, "Foo 2.0", inStock = true) - val item = converter.toItem(foo) + val item = converter.convertTo(foo) assertEquals(3, item.size) assertEquals(42, item.getValue("id").asN().toInt()) assertEquals("Foo 2.0", item.getValue("name").asS()) assertTrue(item.getValue("in-stock").asBool()) - val unconverted = converter.fromItem(item) + val unconverted = converter.convertFrom(item) assertEquals(foo, unconverted) } } diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverterTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverterTest.kt index 7b94d57d1e1..e0daa9843d1 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverterTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/DocumentValueConverterTest.kt @@ -10,7 +10,7 @@ import kotlin.test.Test class DocumentValueConverterTest : ValueConvertersTest() { @Test - fun testKitchenSink() = given(DocumentValueConverter.Default) { + fun testKitchenSink() = given(DocumentConverter.Default) { val expectedDocument = buildDocument { "name" to "Ian" "pets" to buildList { diff --git a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConvertersTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConvertersTest.kt index 17f5b7926ef..cd49217b39d 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConvertersTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/common/test/aws/sdk/kotlin/hll/dynamodbmapper/values/smithytypes/InstantConvertersTest.kt @@ -15,20 +15,20 @@ private val NS_TIME = Instant.fromEpochSeconds(1234567890L, 123456789) // 2009-0 class InstantConvertersTest : ValueConvertersTest() { @Test - fun testEpochS() = given(InstantConverters.EpochS) { + fun testEpochS() = given(InstantConverter.EpochS) { WHOLE_TIME inDdbIs 1234567890L NS_TIME inDdbIs 1234567890L whenGoing Direction.TO_ATTRIBUTE_VALUE } @Test - fun testEpochMs() = given(InstantConverters.EpochMs) { + fun testEpochMs() = given(InstantConverter.EpochMs) { WHOLE_TIME inDdbIs 1234567890000L NS_TIME inDdbIs 1234567890123L whenGoing Direction.TO_ATTRIBUTE_VALUE MS_TIME inDdbIs 1234567890123L whenGoing Direction.FROM_ATTRIBUTE_VALUE } @Test - fun testIso8601() = given(InstantConverters.Iso8601) { + fun testIso8601() = given(InstantConverter.Iso8601) { WHOLE_TIME inDdbIs "2009-02-13T23:31:30Z" MS_TIME inDdbIs "2009-02-13T23:31:30.123Z" MICRO_TIME inDdbIs "2009-02-13T23:31:30.123456Z" diff --git a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/OperationTest.kt b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/OperationTest.kt index 27f6226e3e3..d758cb581f4 100644 --- a/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/OperationTest.kt +++ b/hll/dynamodb-mapper/dynamodb-mapper/jvm/test/aws/sdk/kotlin/hll/dynamodbmapper/pipeline/internal/OperationTest.kt @@ -167,8 +167,8 @@ class OperationTest { private data class Foo(val value: String) private val fooConverter = object : ItemConverter { - override fun fromItem(item: Item) = Foo(item["foo"]!!.asS()) - override fun toItem(obj: Foo, onlyAttributes: Set?) = itemOf("foo" to AttributeValue.S(obj.value)) + override fun convertFrom(to: Item): Foo = Foo(to["foo"]!!.asS()) + override fun convertTo(from: Foo, onlyAttributes: Set?): Item = itemOf("foo" to AttributeValue.S(from.value)) } private val fooSchema = fooConverter.withKeySpec(KeySpec.String("foo")) @@ -178,9 +178,9 @@ private data class LFooResponse(val foo: Item) private data class HFooResponse(val foo: Foo) private fun HFooRequest.convert(table: String, schema: ItemSchema) = - LFooRequest(table, schema.converter.toItem(foo)) + LFooRequest(table, schema.converter.convertTo(foo)) private fun LFooResponse.convert(schema: ItemSchema) = - HFooResponse(schema.converter.fromItem(foo)) + HFooResponse(schema.converter.convertFrom(foo)) private suspend fun dummyInvoke(req: LFooRequest) = LFooResponse(req.foo) diff --git a/hll/dynamodb-mapper/tests/dynamodb-mapper-annotation-processor-test/common/test/aws/sdk/kotlin/hll/dynamodbmapper/tests/processor/data/UserTest.kt b/hll/dynamodb-mapper/tests/dynamodb-mapper-annotation-processor-test/common/test/aws/sdk/kotlin/hll/dynamodbmapper/tests/processor/data/UserTest.kt index 1cdb2934665..e2082c04c42 100644 --- a/hll/dynamodb-mapper/tests/dynamodb-mapper-annotation-processor-test/common/test/aws/sdk/kotlin/hll/dynamodbmapper/tests/processor/data/UserTest.kt +++ b/hll/dynamodb-mapper/tests/dynamodb-mapper-annotation-processor-test/common/test/aws/sdk/kotlin/hll/dynamodbmapper/tests/processor/data/UserTest.kt @@ -14,7 +14,7 @@ class UserTest { @Test fun testConversion() { val user = User(123, "Steve", "Rogers", 84) - val converted = UserConverter.toItem(user) + val converted = UserConverter.convertTo(user) assertEquals( itemOf( @@ -26,7 +26,7 @@ class UserTest { converted, ) - val unconverted = UserConverter.fromItem(converted) + val unconverted = UserConverter.convertFrom(converted) assertEquals(user, unconverted) } diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/CodeGeneratorFactory.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/CodeGeneratorFactory.kt index 6835886c2d8..545f02ecce0 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/CodeGeneratorFactory.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/CodeGeneratorFactory.kt @@ -13,9 +13,11 @@ import com.google.devtools.ksp.processing.CodeGenerator as KSCodeGenerator * @param ksCodeGenerator The underlying KSP [KSCodeGenerator] to use for low-level file access and dependency tracking * @param logger A logger instance to use for message */ -class CodeGeneratorFactory(private val ksCodeGenerator: KSCodeGenerator, private val logger: KSPLogger) { - private val dependencies = Dependencies.ALL_FILES - +class CodeGeneratorFactory( + private val ksCodeGenerator: KSCodeGenerator, + private val logger: KSPLogger, + private val dependencies: Dependencies = Dependencies.ALL_FILES, +) { /** * Creates a new [CodeGenerator] backed by a [KSCodeGenerator]. The returned generator starts with no imports and * uses a configured [TemplateEngine] with the default set of processors. @@ -37,7 +39,7 @@ class CodeGeneratorFactory(private val ksCodeGenerator: KSCodeGenerator, private logger.info("Checking out code generator for class $pkg.$fileName") ksCodeGenerator - .createNewFile(dependencies, pkg, fileName) // FIXME don't depend on ALL_FILES + .createNewFile(dependencies, pkg, fileName) .use { outputStream -> outputStream.writer().use { writer -> writer.append(content) } } diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/TemplateProcessor.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/TemplateProcessor.kt index 0b5a4589e1d..c02c39ebdcb 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/TemplateProcessor.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/core/TemplateProcessor.kt @@ -61,6 +61,7 @@ data class TemplateProcessor(val key: Char, val handler: (Any) -> String) { private open class TypeProcessor { open fun format(type: Type): String = buildString { append(type.shortName) + if (type is TypeRef && type.genericArgs.isNotEmpty()) { type.genericArgs.joinToString(", ", "<", ">", transform = ::format).let(::append) } diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Type.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Type.kt index a4d28b5005d..3ae0086b3ef 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Type.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Type.kt @@ -93,3 +93,9 @@ fun Type.nullable() = when { this is TypeVar -> copy(nullable = true) else -> error("Unknown Type ${this::class}") // Should be unreachable, only here to make compiler happy } + +/** + * Returns whether this [TypeRef] is generic for an [other] + * For example, List.isGenericFor(List) returns true. + */ +fun TypeRef.isGenericFor(other: TypeRef): Boolean = fullName == other.fullName diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Types.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Types.kt index 8b823cf5872..918dbadc5cb 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Types.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/model/Types.kt @@ -6,16 +6,42 @@ import aws.sdk.kotlin.hll.codegen.util.Pkg * A container object for various [Type] instances */ object Types { + object Smithy { + val Instant = TypeRef("aws.smithy.kotlin.runtime.time", "Instant") + val Url = TypeRef("aws.smithy.kotlin.runtime.net.url", "Url") + val Document = TypeRef("aws.smithy.kotlin.runtime.content", "Document") + } + object Kotlin { - val String = kotlin("String") + val ByteArray = kotlin("ByteArray") + val Boolean = kotlin("Boolean") val Number = kotlin("Number") + val String = kotlin("String") val StringNullable = String.nullable() + val Char = kotlin("Char") + val CharArray = kotlin("CharArray") + val Byte = kotlin("Byte") + val Short = kotlin("Short") + val Int = kotlin("Int") + val Long = kotlin("Long") + val Float = kotlin("Float") + val Double = kotlin("Double") + val UByte = kotlin("UByte") + val UInt = kotlin("UInt") + val ULong = kotlin("ULong") + val UShort = kotlin("UShort") + + object Collections { + val Set = TypeRef(Pkg.Kotlin.Collections, "Set") + val List = TypeRef(Pkg.Kotlin.Collections, "List") + val Map = TypeRef(Pkg.Kotlin.Collections, "Map") + } /** * Creates a [TypeRef] for a generic [List] * @param element The type of elements in the list */ - fun list(element: Type) = TypeRef(Pkg.Kotlin.Collections, "List", listOf(element)) + fun list(element: TypeRef) = TypeRef(Pkg.Kotlin.Collections, "List", listOf(element)) /** * Creates a [TypeRef] for a named Kotlin type (e.g., `String`) @@ -27,12 +53,12 @@ object Types { * @param key The type of keys in the map * @param value The type of values in the map */ - fun map(key: Type, value: Type) = TypeRef(Pkg.Kotlin.Collections, "Map", listOf(key, value)) + fun map(key: TypeRef, value: TypeRef) = TypeRef(Pkg.Kotlin.Collections, "Map", listOf(key, value)) /** * Creates a [TypeRef] for a generic [Map] with [String] keys * @param value The type of values in the map */ - fun stringMap(value: Type) = map(String, value) + fun stringMap(value: TypeRef) = map(String, value) } } diff --git a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/util/Pkg.kt b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/util/Pkg.kt index 2a706af4eae..6d76bbc1885 100644 --- a/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/util/Pkg.kt +++ b/hll/hll-codegen/src/main/kotlin/aws/sdk/kotlin/hll/codegen/util/Pkg.kt @@ -16,6 +16,7 @@ object Pkg { val PipelineImpl = "$Base.pipeline.internal" val Values = "$Base.values" val ScalarValues = "$Values.scalars" + val CollectionValues = "$Values.collections" val SmithyTypeValues = "$Values.smithytypes" }