Skip to content

Commit

Permalink
Use .into similar to how it is being generated for `UnconstrainedMa…
Browse files Browse the repository at this point in the history
…pGenerator`
  • Loading branch information
Fahad Zubair committed Nov 11, 2024
1 parent 6362a0d commit 5402b45
Showing 1 changed file with 40 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,73 +117,15 @@ 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) {
symbolProvider.toSymbol(shape.member)
} else {
constrainedMemberSymbol
}

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

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

val constrainedValueTypeIsNotFinalType =
resolvesToNonPublicConstrainedValueType && shape.isDirectlyConstrained(symbolProvider)
if (constrainedValueTypeIsNotFinalType) {
// Refer to the comments under `UnconstrainedMapGenerator` for a more in-depth explanation
// of this process. Consider the following Smithy model where a constrained list contains
// an indirectly constrained shape as a member:
//
// ```smithy
// @length(min: 1, max: 100)
// list ItemList {
// member: Item
// }
//
// list Item {
// member: ItemName
// }
//
// @length(min: 1, max: 100)
// string ItemName
// ```
//
// The final type exposed to the user is `ItemList<Vec<Vec<ItemName>>>`. However, the
// intermediate representation generated is `Vec<ItemConstrained>`. This needs to be
// transformed into `Vec<Vec<ItemName>>` to satisfy the `TryFrom` implementation and
// successfully construct an `ItemList` instance.
//
// This transformation is necessary due to the nested nature of the constraints and
// the difference between the internal constrained representation and the external
// user-facing type.
rustTemplate(
"""
let inner: Vec<#{FinalType}> = inner.into_iter().map(|value| value.into()).collect();
""",
"FinalType" to symbolProvider.toSymbol(shape.member),
)
}
} else {
rust("let inner = value.0;")
}
Expand Down

0 comments on commit 5402b45

Please sign in to comment.