Skip to content

Commit 9f51b41

Browse files
author
Fahad Zubair
committed
Constraint member types are refactored as standalone shapes.
1 parent d7f8130 commit 9f51b41

File tree

45 files changed

+1743
-228
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1743
-228
lines changed

codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider
2323
import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter
2424
import software.amazon.smithy.rust.codegen.core.rustlang.implBlock
2525
import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget
26+
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
2627
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
2728
import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator
2829
import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels
@@ -51,6 +52,7 @@ abstract class ClientEventStreamBaseRequirements : EventStreamTestRequirements<C
5152
)
5253

5354
override fun renderBuilderForShape(
55+
rustCrate: RustCrate,
5456
writer: RustWriter,
5557
codegenContext: ClientCodegenContext,
5658
shape: StructureShape,

codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
1818
import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget
1919
import software.amazon.smithy.rust.codegen.core.smithy.DirectedWalker
2020
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
21+
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
2122
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
2223
import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator
2324
import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant
@@ -62,6 +63,7 @@ interface EventStreamTestRequirements<C : CodegenContext> {
6263

6364
/** Render a builder for the given shape */
6465
fun renderBuilderForShape(
66+
rustCrate: RustCrate,
6567
writer: RustWriter,
6668
codegenContext: C,
6769
shape: StructureShape,

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ import software.amazon.smithy.model.shapes.ServiceShape
1919
import software.amazon.smithy.model.shapes.Shape
2020
import software.amazon.smithy.model.shapes.ShortShape
2121
import software.amazon.smithy.model.shapes.StringShape
22+
import software.amazon.smithy.model.shapes.StructureShape
2223
import software.amazon.smithy.model.traits.LengthTrait
24+
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
25+
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords
2326
import software.amazon.smithy.rust.codegen.core.rustlang.RustType
2427
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
2528
import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider
@@ -32,6 +35,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.symbolBuilder
3235
import software.amazon.smithy.rust.codegen.core.util.hasTrait
3336
import software.amazon.smithy.rust.codegen.core.util.orNull
3437
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
38+
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
39+
import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderModule
3540

3641
/**
3742
* The [ConstrainedShapeSymbolProvider] returns, for a given _directly_
@@ -56,14 +61,19 @@ class ConstrainedShapeSymbolProvider(
5661
private val base: RustSymbolProvider,
5762
private val model: Model,
5863
private val serviceShape: ServiceShape,
64+
private val publicConstrainedTypes : Boolean = true
5965
) : WrappingSymbolProvider(base) {
6066
private val nullableIndex = NullableIndex.of(model)
6167

6268
private fun publicConstrainedSymbolForMapOrCollectionShape(shape: Shape): Symbol {
6369
check(shape is MapShape || shape is CollectionShape)
70+
// FZ rebase
71+
// val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase())
72+
// return symbolBuilder(shape, rustType).locatedIn(ServerRustModule.Model).build()
6473

65-
val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase())
66-
return symbolBuilder(shape, rustType).locatedIn(ServerRustModule.Model).build()
74+
val (name, module) = getMemberNameAndModule(shape, serviceShape, ServerRustModule.Model, !publicConstrainedTypes)
75+
val rustType = RustType.Opaque(name)
76+
return symbolBuilder(shape, rustType).locatedIn(module).build()
6777
}
6878

6979
override fun toSymbol(shape: Shape): Symbol {
@@ -74,8 +84,14 @@ class ConstrainedShapeSymbolProvider(
7484
val target = model.expectShape(shape.target)
7585
val targetSymbol = this.toSymbol(target)
7686
// Handle boxing first, so we end up with `Option<Box<_>>`, not `Box<Option<_>>`.
77-
handleOptionality(handleRustBoxing(targetSymbol, shape), shape, nullableIndex, base.config().nullabilityCheckMode)
87+
handleOptionality(
88+
handleRustBoxing(targetSymbol, shape),
89+
shape,
90+
nullableIndex,
91+
base.config().nullabilityCheckMode,
92+
)
7893
}
94+
7995
is MapShape -> {
8096
if (shape.isDirectlyConstrained(base)) {
8197
check(shape.hasTrait<LengthTrait>()) {
@@ -91,6 +107,7 @@ class ConstrainedShapeSymbolProvider(
91107
.build()
92108
}
93109
}
110+
94111
is CollectionShape -> {
95112
if (shape.isDirectlyConstrained(base)) {
96113
check(constrainedCollectionCheck(shape)) {
@@ -105,8 +122,15 @@ class ConstrainedShapeSymbolProvider(
105122

106123
is StringShape, is IntegerShape, is ShortShape, is LongShape, is ByteShape, is BlobShape -> {
107124
if (shape.isDirectlyConstrained(base)) {
108-
val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase())
109-
symbolBuilder(shape, rustType).locatedIn(ServerRustModule.Model).build()
125+
// FZ rebase
126+
//val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase())
127+
//symbolBuilder(shape, rustType).locatedIn(ServerRustModule.Model).build()
128+
129+
// A standalone constrained shape goes into `ModelsModule`, but one
130+
// arising from a constrained member shape goes into a module for the container.
131+
val (name, module) = getMemberNameAndModule(shape, serviceShape, ServerRustModule.Model, !publicConstrainedTypes)
132+
val rustType = RustType.Opaque(name)
133+
symbolBuilder(shape, rustType).locatedIn(module).build()
110134
} else {
111135
base.toSymbol(shape)
112136
}
@@ -122,9 +146,51 @@ class ConstrainedShapeSymbolProvider(
122146
* - That it has no unsupported constraints applied.
123147
*/
124148
private fun constrainedCollectionCheck(shape: CollectionShape): Boolean {
125-
val supportedConstraintTraits = supportedCollectionConstraintTraits.mapNotNull { shape.getTrait(it).orNull() }.toSet()
149+
val supportedConstraintTraits =
150+
supportedCollectionConstraintTraits.mapNotNull { shape.getTrait(it).orNull() }.toSet()
126151
val allConstraintTraits = allConstraintTraits.mapNotNull { shape.getTrait(it).orNull() }.toSet()
127152

128-
return supportedConstraintTraits.isNotEmpty() && allConstraintTraits.subtract(supportedConstraintTraits).isEmpty()
153+
return supportedConstraintTraits.isNotEmpty() && allConstraintTraits.subtract(supportedConstraintTraits)
154+
.isEmpty()
155+
}
156+
157+
158+
/**
159+
* Returns the pair (Rust Symbol Name, Inline Module) for the shape. At the time of model transformation all
160+
* constrained member shapes are extracted and are given a model-wide unique name. However, the generated code
161+
* for the new shapes is in a module that is named after the containing shape (structure, list, map or union).
162+
* The new shape's Rust Symbol is renamed from `{structureName}{memberName}` to `{structure_name}::{member_name}`
163+
*/
164+
private fun getMemberNameAndModule(
165+
shape: Shape,
166+
serviceShape: ServiceShape,
167+
defaultModule: RustModule.LeafModule,
168+
pubCrateServerBuilder: Boolean,
169+
): Pair<String, RustModule.LeafModule> {
170+
val (container, member) =
171+
shape.overriddenConstrainedMemberInfo() ?: return Pair(shape.contextName(serviceShape), defaultModule)
172+
173+
return if (container is StructureShape) {
174+
val builderModule = container.serverBuilderModule(base, pubCrateServerBuilder)
175+
val renameTo = member.memberName ?: member.id.name
176+
Pair(renameTo.toPascalCase(), builderModule)
177+
} else {
178+
// For List, Union and Map, the new shape defined for a constrained member shape
179+
// need to be placed into an inline module named `pub {container_name_in_snake_case}`
180+
val innerModuleName = RustReservedWords.escapeIfNeeded(container.id.name.toSnakeCase()) + if (pubCrateServerBuilder) {
181+
"_internal"
182+
} else {
183+
""
184+
}
185+
186+
val innerModule = RustModule.new(
187+
innerModuleName,
188+
visibility = Visibility.publicIf(!pubCrateServerBuilder, Visibility.PUBCRATE),
189+
parent = defaultModule,
190+
inline = true,
191+
)
192+
val renameTo = member.memberName ?: member.id.name
193+
Pair(renameTo.toPascalCase(), innerModule)
194+
}
129195
}
130196
}

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.contextName
2929
import software.amazon.smithy.rust.codegen.core.smithy.locatedIn
3030
import software.amazon.smithy.rust.codegen.core.smithy.module
3131
import software.amazon.smithy.rust.codegen.core.smithy.rustType
32+
import software.amazon.smithy.rust.codegen.core.util.getTrait
3233
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
3334
import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol
35+
import software.amazon.smithy.rust.codegen.server.smithy.traits.SyntheticStructureFromConstrainedMemberTrait
3436

3537
/**
3638
* The [ConstraintViolationSymbolProvider] returns, for a given constrained
@@ -79,14 +81,34 @@ class ConstraintViolationSymbolProvider(
7981

8082
private fun Shape.shapeModule(): RustModule.LeafModule {
8183
val documentation = if (publicConstrainedTypes && this.isDirectlyConstrained(base)) {
82-
"See [`${this.contextName(serviceShape)}`]."
84+
if (this.hasTrait(SyntheticStructureFromConstrainedMemberTrait.ID)) {
85+
val symbol = base.toSymbol(this)
86+
"See [`${this.contextName(serviceShape)}`]($symbol)."
87+
} else {
88+
"See [`${this.contextName(serviceShape)}`]."
89+
}
8390
} else {
8491
null
8592
}
86-
return RustModule.new(
93+
94+
val syntheticTrait = getTrait<SyntheticStructureFromConstrainedMemberTrait>()
95+
96+
val (module, name) = if (syntheticTrait != null) {
97+
// For constrained member shapes, the ConstraintViolation code needs to go in an inline rust module
98+
// that is a descendant of the module that contains the extracted shape itself.
99+
val overriddenMemberModule = this.getParentAndInlineModuleForConstrainedMember(base, !publicConstrainedTypes)!!
100+
val name = syntheticTrait.member.memberName
101+
Pair(overriddenMemberModule.second, RustReservedWords.escapeIfNeeded(name).toSnakeCase())
102+
} else {
87103
// Need to use the context name so we get the correct name for maps.
88-
name = RustReservedWords.escapeIfNeeded(this.contextName(serviceShape)).toSnakeCase(),
104+
Pair(ModelsModule, RustReservedWords.escapeIfNeeded(this.contextName(serviceShape)).toSnakeCase())
105+
}
106+
107+
return RustModule.new(
108+
name = name,
89109
visibility = visibility,
110+
// FZ rebase
111+
//parent = ServerRustModule.Model,
90112
parent = ServerRustModule.Model,
91113
inline = true,
92114
documentation = documentation,

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.IntegerShape
1414
import software.amazon.smithy.model.shapes.LongShape
1515
import software.amazon.smithy.model.shapes.MapShape
1616
import software.amazon.smithy.model.shapes.MemberShape
17+
import software.amazon.smithy.model.shapes.ServiceShape
1718
import software.amazon.smithy.model.shapes.Shape
1819
import software.amazon.smithy.model.shapes.ShortShape
1920
import software.amazon.smithy.model.shapes.SimpleShape
@@ -26,11 +27,21 @@ import software.amazon.smithy.model.traits.PatternTrait
2627
import software.amazon.smithy.model.traits.RangeTrait
2728
import software.amazon.smithy.model.traits.RequiredTrait
2829
import software.amazon.smithy.model.traits.UniqueItemsTrait
30+
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
31+
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords
32+
import software.amazon.smithy.rust.codegen.core.rustlang.Visibility
2933
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
3034
import software.amazon.smithy.rust.codegen.core.smithy.DirectedWalker
35+
import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule
36+
import software.amazon.smithy.rust.codegen.core.smithy.contextName
3137
import software.amazon.smithy.rust.codegen.core.smithy.isOptional
38+
import software.amazon.smithy.rust.codegen.core.smithy.module
3239
import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE
40+
import software.amazon.smithy.rust.codegen.core.util.getTrait
3341
import software.amazon.smithy.rust.codegen.core.util.hasTrait
42+
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
43+
import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderModule
44+
import software.amazon.smithy.rust.codegen.server.smithy.traits.SyntheticStructureFromConstrainedMemberTrait
3445

3546
/**
3647
* This file contains utilities to work with constrained shapes.
@@ -160,3 +171,52 @@ fun Shape.typeNameContainsNonPublicType(
160171
is StructureShape, is UnionShape -> false
161172
else -> UNREACHABLE("the above arms should be exhaustive, but we received shape: $this")
162173
}
174+
175+
176+
/**
177+
* For synthetic shapes that are added to the model because of member constrained shapes, it returns
178+
* the "container" and "the member shape" that originally had the constraint trait. For all other
179+
* shapes, it returns null.
180+
*/
181+
fun Shape.overriddenConstrainedMemberInfo(): Pair<Shape, MemberShape>? {
182+
val trait = getTrait<SyntheticStructureFromConstrainedMemberTrait>() ?: return null
183+
return Pair(trait.container, trait.member)
184+
}
185+
186+
187+
/**
188+
* Returns the parent and the inline module that this particular shape should go in.
189+
*/
190+
fun Shape.getParentAndInlineModuleForConstrainedMember(symbolProvider: SymbolProvider, pubCrateServerBuilder: Boolean): Pair<RustModule.LeafModule, RustModule.LeafModule>? {
191+
//val container = overriddenConstrainedMemberInfo()?.first ?: return null
192+
val overriddenTrait = getTrait<SyntheticStructureFromConstrainedMemberTrait>() ?: return null
193+
return if (overriddenTrait.container is StructureShape) {
194+
val structureModule = symbolProvider.toSymbol(overriddenTrait.container).module()
195+
val builderModule = overriddenTrait.container.serverBuilderModule(symbolProvider, pubCrateServerBuilder)
196+
Pair(structureModule, builderModule)
197+
}
198+
else {
199+
// For constrained member shapes, the ConstraintViolation code needs to go in an inline rust module
200+
// that is a descendant of the module that contains the extracted shape itself.
201+
return if (!pubCrateServerBuilder) {
202+
// List, union and map types need to go into their own module
203+
val shapeSymbol = symbolProvider.toSymbol(this)
204+
val shapeModule = shapeSymbol.module()
205+
check(!shapeModule.parent.isInline()) {
206+
"parent module of $id should not be an inline module"
207+
}
208+
Pair(shapeModule.parent as RustModule.LeafModule, shapeModule)
209+
}
210+
else {
211+
val name = RustReservedWords.escapeIfNeeded(overriddenTrait.container.id.name).toSnakeCase() + "_internal"
212+
val innerModule = RustModule.new(
213+
name = name,
214+
visibility = Visibility.PUBCRATE,
215+
parent = ModelsModule,
216+
inline = true,
217+
)
218+
219+
Pair(ModelsModule, innerModule)
220+
}
221+
}
222+
}

0 commit comments

Comments
 (0)