Skip to content

Commit

Permalink
Convert from pub(crate) Constrained type to the publicly exposed type.
Browse files Browse the repository at this point in the history
  • Loading branch information
Fahad Zubair committed Oct 27, 2024
1 parent ef07c88 commit a7fb2e3
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 3 deletions.
30 changes: 30 additions & 0 deletions codegen-core/common-test-models/constraints.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ service ConstraintsService {
ConstrainedHttpBoundShapesOperation,
ConstrainedHttpPayloadBoundShapeOperation,
ConstrainedRecursiveShapesOperation,
ConstrainedListWithIndirectlyConstrainedAggregateOperation,

// `httpQueryParams` and `httpPrefixHeaders` are structurually
// exclusive, so we need one operation per target shape type
Expand Down Expand Up @@ -83,6 +84,13 @@ operation ConstrainedRecursiveShapesOperation {
errors: [ValidationException]
}

@http(uri: "/constrained-list-with-indirectly-constrained-aggregate", method: "POST")
operation ConstrainedListWithIndirectlyConstrainedAggregateOperation {
input: ConstrainedListWithIndirectlyConstrainedAggregateInputOutput,
output: ConstrainedListWithIndirectlyConstrainedAggregateInputOutput,
errors: [ValidationException]
}

@http(uri: "/query-params-targeting-length-map", method: "POST")
operation QueryParamsTargetingLengthMapOperation {
input: QueryParamsTargetingLengthMapOperationInputOutput,
Expand Down Expand Up @@ -335,6 +343,28 @@ structure ConstrainedHttpPayloadBoundShapeOperationInputOutput {
httpPayloadBoundConstrainedShape: ConA
}

structure ConstrainedListWithIndirectlyConstrainedAggregateInputOutput {
a: ListWithIndirectlyConstrainedList,
b: ListWithIndirectlyConstrainedMap
}

@length(min:1, max: 10)
list ListWithIndirectlyConstrainedList {
member: IndirectlyConstrainedList
}
list IndirectlyConstrainedList {
member: LengthString
}

@length(min:1, max: 10)
list ListWithIndirectlyConstrainedMap {
member: IndirectlyConstrainedMap
}
map IndirectlyConstrainedMap {
key: LengthString,
value: LengthString
}

structure QueryParamsTargetingMapOfPatternStringOperationInputOutput {
@httpQueryParams
mapOfPatternString: MapOfPatternString
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,74 @@ class UnconstrainedCollectionGenerator(
} else {
""
}

/**
* This example demonstrates a list shape that is directly constrained, while its member shape is
* indirectly constrained:
* ```smithy
* @length(min: 1, max: 100)
* list ItemList {
* member: Item
* }
* list Item {
* member: ItemName
* }
* @length(min: 1, max: 100)
* string ItemName
* ```
*
* For this model, two `pub(crate)` types are generated for the `Item` shape:
* - `ItemUnconstrained`: represents the non-validated version
* - `ItemConstrained`: represents the validated version
*
* Similarly, for `ItemList`:
* - `ItemListUnconstrained`: a `pub(crate)` type representing the non-validated version
* - `ItemList`: the publicly exposed validated version
*
* A `TryFrom` implementation must be generated to convert from `ItemListUnconstrained` to `ItemList`.
* Since the final type exposed to the user is `struct ItemList(Vec<Vec<ItemName>>)`, the conversion
* process involves two steps:
* 1. Converting each element of the vector from `ItemUnconstrained` to `ItemConstrained` to validate
* constraints
* 2. Converting the resulting `Vec<ItemConstrained>` to `Vec<Vec<ItemName>>`
*/
val constrainedValueTypeIsNotFinalType =
resolvesToNonPublicConstrainedValueType && shape.isDirectlyConstrained(symbolProvider)

val finalType =
if (constrainedValueTypeIsNotFinalType) {
constrainedShapeSymbolProvider.toSymbol(shape.member)
} else {
constrainedMemberSymbol
}

val constrainValueWritable =
writable {
conditionalBlock("inner.map(|inner| ", ").transpose()", constrainedMemberSymbol.isOptional()) {
rust("inner.try_into().map_err(|inner_violation| (idx, inner_violation))")
rustTemplate(
"""
inner.try_into()
#{FinalMapping}
.map_err(|inner_violation| (idx, inner_violation))
""",
"FinalMapping" to
writable {
if (constrainedValueTypeIsNotFinalType) {
rustTemplate(
".map(|c : #{ConstrainedMemberSymbol}| c.into())",
"ConstrainedMemberSymbol" to constrainedMemberSymbol,
)
} else {
rust("")
}
},
)
}
}

rustTemplate(
"""
let res: Result<#{Vec}<#{ConstrainedMemberSymbol}>, (usize, #{InnerConstraintViolationSymbol}) > = value
let res: Result<#{Vec}<#{FinalType}>, (usize, #{InnerConstraintViolationSymbol}) > = value
.0
.into_iter()
.enumerate()
Expand All @@ -139,7 +197,7 @@ class UnconstrainedCollectionGenerator(
.map_err(|(idx, inner_violation)| Self::Error::Member(idx, inner_violation))?;
""",
"Vec" to RuntimeType.Vec,
"ConstrainedMemberSymbol" to constrainedMemberSymbol,
"FinalType" to finalType,
"InnerConstraintViolationSymbol" to innerConstraintViolationSymbol,
"ConstrainValueWritable" to constrainValueWritable,
)
Expand Down

0 comments on commit a7fb2e3

Please sign in to comment.