|
4 | 4 | */ |
5 | 5 | package aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model |
6 | 6 |
|
7 | | -import aws.sdk.kotlin.hll.codegen.model.Member |
8 | | -import aws.sdk.kotlin.hll.codegen.model.TypeRef |
9 | | -import aws.sdk.kotlin.hll.codegen.model.Types |
10 | | -import aws.sdk.kotlin.hll.codegen.model.nullable |
| 7 | +import aws.sdk.kotlin.hll.codegen.model.* |
11 | 8 | import aws.sdk.kotlin.hll.dynamodbmapper.codegen.model.MapperPkg |
12 | 9 | import aws.sdk.kotlin.hll.dynamodbmapper.codegen.model.MapperTypes |
13 | | - |
14 | | -private val attrMapTypes = setOf(MapperTypes.AttributeMap, MapperTypes.AttributeMap.nullable()) |
15 | | -private val attrMapListTypes = Types.Kotlin.list(MapperTypes.AttributeMap).let { setOf(it, it.nullable()) } |
| 10 | +import aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model.ExpressionArgumentsType.* |
| 11 | +import aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model.ExpressionLiteralType.* |
| 12 | +import aws.sdk.kotlin.hll.dynamodbmapper.codegen.operations.model.MemberCodegenBehavior.* |
16 | 13 |
|
17 | 14 | /** |
18 | 15 | * Describes a behavior to apply for a given [Member] in a low-level structure when generating code for an equivalent |
@@ -60,56 +57,105 @@ internal sealed interface MemberCodegenBehavior { |
60 | 57 | * structure). |
61 | 58 | */ |
62 | 59 | data object Hoist : MemberCodegenBehavior |
| 60 | + |
| 61 | + /** |
| 62 | + * Indicates that a member is a string expression parameter which should be replaced by an expression DSL |
| 63 | + * @param type The type of expression this member models |
| 64 | + */ |
| 65 | + data class ExpressionLiteral(val type: ExpressionLiteralType) : MemberCodegenBehavior |
| 66 | + |
| 67 | + /** |
| 68 | + * Indicates that a member is a map of expression arguments which should be automatically handled by an expression |
| 69 | + * DSL |
| 70 | + * @param type The type of expression arguments this member models |
| 71 | + */ |
| 72 | + data class ExpressionArguments(val type: ExpressionArgumentsType) : MemberCodegenBehavior |
| 73 | +} |
| 74 | + |
| 75 | +/** |
| 76 | + * Identifies a type of expression literal supported by DynamoDB APIs |
| 77 | + */ |
| 78 | +internal enum class ExpressionLiteralType { |
| 79 | + Condition, |
| 80 | + Filter, |
| 81 | + KeyCondition, |
| 82 | + Projection, |
| 83 | + Update, |
| 84 | +} |
| 85 | + |
| 86 | +/** |
| 87 | + * Identifies a type of expression arguments supported by DynamoDB APIs |
| 88 | + */ |
| 89 | +internal enum class ExpressionArgumentsType { |
| 90 | + AttributeNames, |
| 91 | + AttributeValues, |
63 | 92 | } |
64 | 93 |
|
65 | 94 | /** |
66 | 95 | * Identifies a [MemberCodegenBehavior] for this [Member] by way of various heuristics |
67 | 96 | */ |
68 | 97 | internal val Member.codegenBehavior: MemberCodegenBehavior |
69 | | - get() = when { |
70 | | - this in unsupportedMembers -> MemberCodegenBehavior.Drop |
71 | | - type in attrMapTypes -> if (name == "key") MemberCodegenBehavior.MapKeys else MemberCodegenBehavior.MapAll |
72 | | - type in attrMapListTypes -> MemberCodegenBehavior.ListMapAll |
73 | | - isTableName || isIndexName -> MemberCodegenBehavior.Hoist |
74 | | - else -> MemberCodegenBehavior.PassThrough |
75 | | - } |
| 98 | + get() = rules.firstNotNullOfOrNull { it.matchedBehaviorOrNull(this) } ?: PassThrough |
76 | 99 |
|
77 | | -private val Member.isTableName: Boolean |
78 | | - get() = name == "tableName" && type == Types.Kotlin.StringNullable |
| 100 | +private fun llType(name: String) = TypeRef(MapperPkg.Ll.Model, name) |
79 | 101 |
|
80 | | -private val Member.isIndexName: Boolean |
81 | | - get() = name == "indexName" && type == Types.Kotlin.StringNullable |
| 102 | +private data class Rule( |
| 103 | + val namePredicate: (String) -> Boolean, |
| 104 | + val typePredicate: (TypeRef) -> Boolean, |
| 105 | + val behavior: MemberCodegenBehavior, |
| 106 | +) { |
| 107 | + constructor(name: String, type: TypeRef, behavior: MemberCodegenBehavior) : |
| 108 | + this(name::equals, type::isEquivalentTo, behavior) |
82 | 109 |
|
83 | | -private fun llType(name: String) = TypeRef(MapperPkg.Ll.Model, name) |
| 110 | + constructor(name: Regex, type: TypeRef, behavior: MemberCodegenBehavior) : |
| 111 | + this(name::matches, type::isEquivalentTo, behavior) |
84 | 112 |
|
85 | | -private val unsupportedMembers = listOf( |
86 | | - // superseded by ConditionExpression |
87 | | - Member("conditionalOperator", llType("ConditionalOperator")), |
88 | | - Member("expected", Types.Kotlin.stringMap(llType("ExpectedAttributeValue"))), |
89 | | - |
90 | | - // superseded by FilterExpression |
91 | | - Member("queryFilter", Types.Kotlin.stringMap(llType("Condition"))), |
92 | | - Member("scanFilter", Types.Kotlin.stringMap(llType("Condition"))), |
93 | | - |
94 | | - // superseded by KeyConditionExpression |
95 | | - Member("keyConditions", Types.Kotlin.stringMap(llType("Condition"))), |
96 | | - |
97 | | - // superseded by ProjectionExpression |
98 | | - Member("attributesToGet", Types.Kotlin.list(Types.Kotlin.String)), |
99 | | - |
100 | | - // superseded by UpdateExpression |
101 | | - Member("attributeUpdates", Types.Kotlin.stringMap(llType("AttributeValueUpdate"))), |
102 | | - |
103 | | - // TODO add support for expressions |
104 | | - Member("expressionAttributeNames", Types.Kotlin.stringMap(Types.Kotlin.String)), |
105 | | - Member("expressionAttributeValues", MapperTypes.AttributeMap), |
106 | | - Member("conditionExpression", Types.Kotlin.String), |
107 | | - Member("projectionExpression", Types.Kotlin.String), |
108 | | - Member("updateExpression", Types.Kotlin.String), |
109 | | -).map { member -> |
110 | | - if (member.type is TypeRef) { |
111 | | - member.copy(type = member.type.nullable()) |
112 | | - } else { |
113 | | - member |
114 | | - } |
115 | | -}.toSet() |
| 113 | + fun matchedBehaviorOrNull(member: Member) = if (matches(member)) behavior else null |
| 114 | + fun matches(member: Member) = namePredicate(member.name) && typePredicate(member.type as TypeRef) |
| 115 | +} |
| 116 | + |
| 117 | +private fun Type.isEquivalentTo(other: Type): Boolean = when (this) { |
| 118 | + is TypeVar -> other is TypeVar && shortName == other.shortName |
| 119 | + is TypeRef -> |
| 120 | + other is TypeRef && |
| 121 | + fullName == other.fullName && |
| 122 | + genericArgs.size == other.genericArgs.size && |
| 123 | + genericArgs.zip(other.genericArgs).all { (thisArg, otherArg) -> thisArg.isEquivalentTo(otherArg) } |
| 124 | +} |
| 125 | + |
| 126 | +/** |
| 127 | + * Priority-ordered list of dispositions to apply to members found in structures. The first element from this list that |
| 128 | + * successfully matches with a member will be chosen. |
| 129 | + */ |
| 130 | +private val rules = listOf( |
| 131 | + // Deprecated expression members not to be carried forward into HLL |
| 132 | + Rule("conditionalOperator", llType("ConditionalOperator"), Drop), |
| 133 | + Rule("expected", Types.Kotlin.stringMap(llType("ExpectedAttributeValue")), Drop), |
| 134 | + Rule("queryFilter", Types.Kotlin.stringMap(llType("Condition")), Drop), |
| 135 | + Rule("scanFilter", Types.Kotlin.stringMap(llType("Condition")), Drop), |
| 136 | + Rule("keyConditions", Types.Kotlin.stringMap(llType("Condition")), Drop), |
| 137 | + Rule("attributesToGet", Types.Kotlin.list(Types.Kotlin.String), Drop), |
| 138 | + Rule("attributeUpdates", Types.Kotlin.stringMap(llType("AttributeValueUpdate")), Drop), |
| 139 | + |
| 140 | + // Hoisted members |
| 141 | + Rule("tableName", Types.Kotlin.String, Hoist), |
| 142 | + Rule("indexName", Types.Kotlin.String, Hoist), |
| 143 | + |
| 144 | + // Expression literals |
| 145 | + Rule("keyConditionExpression", Types.Kotlin.String, ExpressionLiteral(KeyCondition)), |
| 146 | + Rule("filterExpression", Types.Kotlin.String, ExpressionLiteral(Filter)), |
| 147 | + |
| 148 | + // TODO add support for remaining expression types |
| 149 | + Rule("conditionExpression", Types.Kotlin.String, Drop), |
| 150 | + Rule("projectionExpression", Types.Kotlin.String, Drop), |
| 151 | + Rule("updateExpression", Types.Kotlin.String, Drop), |
| 152 | + |
| 153 | + // Expression arguments |
| 154 | + Rule("expressionAttributeNames", Types.Kotlin.stringMap(Types.Kotlin.String), ExpressionArguments(AttributeNames)), |
| 155 | + Rule("expressionAttributeValues", MapperTypes.AttributeMap, ExpressionArguments(AttributeValues)), |
| 156 | + |
| 157 | + // Mappable members |
| 158 | + Rule(".*".toRegex(), Types.Kotlin.list(MapperTypes.AttributeMap), ListMapAll), |
| 159 | + Rule("key", MapperTypes.AttributeMap, MapKeys), |
| 160 | + Rule(".*".toRegex(), MapperTypes.AttributeMap, MapAll), |
| 161 | +) |
0 commit comments