Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement @length on blobs #2131

Merged
merged 6 commits into from
Dec 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions codegen-core/common-test-models/constraints.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,11 @@ structure ConA {
maxLengthString: MaxLengthString,
fixedLengthString: FixedLengthString,

lengthBlob: LengthBlob,
minLengthBlob: MinLengthBlob,
maxLengthBlob: MaxLengthBlob,
fixedLengthBlob: FixedLengthBlob,

rangeInteger: RangeInteger,
minRangeInteger: MinRangeInteger,
maxRangeInteger: MaxRangeInteger,
Expand Down Expand Up @@ -486,6 +491,12 @@ structure ConA {
// setOfLengthString: SetOfLengthString,
mapOfLengthString: MapOfLengthString,

listOfLengthBlob: ListOfLengthBlob,
// TODO(https://github.com/awslabs/smithy-rs/issues/1401): a `set` shape is
// just a `list` shape with `uniqueItems`, which hasn't been implemented yet.
// setOfLengthBlob: SetOfLengthBlob,
mapOfLengthBlob: MapOfLengthBlob,

listOfRangeInteger: ListOfRangeInteger,
// TODO(https://github.com/awslabs/smithy-rs/issues/1401): a `set` shape is
// just a `list` shape with `uniqueItems`, which hasn't been implemented yet.
Expand Down Expand Up @@ -532,6 +543,11 @@ structure ConA {
// lengthSetOfPatternString: LengthSetOfPatternString,
}

map MapOfLengthBlob {
key: String,
value: LengthBlob,
}

map MapOfLengthString {
key: LengthString,
value: LengthString,
Expand Down Expand Up @@ -640,6 +656,23 @@ string MaxLengthString
@length(min: 69, max: 69)
string FixedLengthString

@length(min: 2, max: 8)
list LengthListOfLengthBlob {
member: LengthBlob
}

@length(min: 2, max: 70)
blob LengthBlob

@length(min: 2)
blob MinLengthBlob

@length(max: 70)
blob MaxLengthBlob

@length(min: 70, max: 70)
blob FixedLengthBlob

@pattern("[a-d]{5}")
string PatternString

Expand Down Expand Up @@ -732,6 +765,10 @@ set SetOfLengthString {
member: LengthString
}

set SetOfLengthBlob {
member: LengthBlob
}

set SetOfPatternString {
member: PatternString
}
Expand All @@ -749,6 +786,10 @@ list ListOfLengthString {
member: LengthString
}

list ListOfLengthBlob {
member: LengthBlob
}

// TODO(https://github.com/awslabs/smithy-rs/issues/1401): a `set` shape is
// just a `list` shape with `uniqueItems`, which hasn't been implemented yet.
// set SetOfRangeInteger {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.escape
import software.amazon.smithy.rust.codegen.core.rustlang.render
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.withBlock
import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
Expand All @@ -49,6 +51,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed
import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver
import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation
import software.amazon.smithy.rust.codegen.core.smithy.protocols.deserializeFunctionName
import software.amazon.smithy.rust.codegen.core.smithy.rustType
import software.amazon.smithy.rust.codegen.core.util.PANIC
import software.amazon.smithy.rust.codegen.core.util.dq
import software.amazon.smithy.rust.codegen.core.util.hasTrait
Expand Down Expand Up @@ -296,7 +299,7 @@ class JsonParserGenerator(
private fun RustWriter.deserializeBlob(target: BlobShape) {
rustTemplate(
"#{expect_blob_or_null}(tokens.next())?#{ConvertFrom:W}",
"ConvertFrom" to typeConversionGenerator.convertViaFrom(target),
"ConvertFrom" to writable { RuntimeType.blob(runtimeConfig).toSymbol().rustType().render() },
*codegenScope,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy
import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.knowledge.NullableIndex
import software.amazon.smithy.model.shapes.BlobShape
import software.amazon.smithy.model.shapes.ByteShape
import software.amazon.smithy.model.shapes.CollectionShape
import software.amazon.smithy.model.shapes.IntegerShape
Expand Down Expand Up @@ -99,7 +100,7 @@ class ConstrainedShapeSymbolProvider(
}
}

is StringShape, is IntegerShape, is ShortShape, is LongShape, is ByteShape -> {
is StringShape, is IntegerShape, is ShortShape, is LongShape, is ByteShape, is BlobShape -> {
if (shape.isDirectlyConstrained(base)) {
val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase())
symbolBuilder(shape, rustType).locatedIn(ModelsModule).build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.server.smithy

import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.BlobShape
import software.amazon.smithy.model.shapes.ByteShape
import software.amazon.smithy.model.shapes.CollectionShape
import software.amazon.smithy.model.shapes.IntegerShape
Expand Down Expand Up @@ -127,7 +128,7 @@ class ConstraintViolationSymbolProvider(
.build()
}

is StringShape, is IntegerShape, is ShortShape, is LongShape, is ByteShape -> {
is StringShape, is IntegerShape, is ShortShape, is LongShape, is ByteShape, is BlobShape -> {
val module = shape.shapeModule()
val rustType = RustType.Opaque(constraintViolationName, module.fullyQualifiedPath())
Symbol.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy
import software.amazon.smithy.codegen.core.SymbolProvider
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.neighbor.Walker
import software.amazon.smithy.model.shapes.BlobShape
import software.amazon.smithy.model.shapes.ByteShape
import software.amazon.smithy.model.shapes.CollectionShape
import software.amazon.smithy.model.shapes.IntegerShape
Expand Down Expand Up @@ -90,6 +91,7 @@ fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider): Boolean = when
is StringShape -> this.hasTrait<EnumTrait>() || supportedStringConstraintTraits.any { this.hasTrait(it) }
is CollectionShape -> supportedCollectionConstraintTraits.any { this.hasTrait(it) }
is IntegerShape, is ShortShape, is LongShape, is ByteShape -> this.hasTrait<RangeTrait>()
is BlobShape -> this.hasTrait<LengthTrait>()
else -> false
}

Expand Down Expand Up @@ -118,6 +120,7 @@ fun Shape.hasPublicConstrainedWrapperTupleType(model: Model, publicConstrainedTy
is StringShape -> !this.hasTrait<EnumTrait>() && (publicConstrainedTypes && supportedStringConstraintTraits.any(this::hasTrait))
is IntegerShape, is ShortShape, is LongShape, is ByteShape -> publicConstrainedTypes && this.hasTrait<RangeTrait>()
is MemberShape -> model.expectShape(this.target).hasPublicConstrainedWrapperTupleType(model, publicConstrainedTypes)
is BlobShape -> publicConstrainedTypes && this.hasTrait<LengthTrait>()
else -> false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import software.amazon.smithy.codegen.core.CodegenException
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.knowledge.NullableIndex
import software.amazon.smithy.model.neighbor.Walker
import software.amazon.smithy.model.shapes.BlobShape
import software.amazon.smithy.model.shapes.ByteShape
import software.amazon.smithy.model.shapes.CollectionShape
import software.amazon.smithy.model.shapes.IntegerShape
Expand Down Expand Up @@ -48,12 +49,14 @@ import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveSha
import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors
import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors
import software.amazon.smithy.rust.codegen.core.util.CommandFailed
import software.amazon.smithy.rust.codegen.core.util.hasEventStreamMember
import software.amazon.smithy.rust.codegen.core.util.hasTrait
import software.amazon.smithy.rust.codegen.core.util.isEventStream
import software.amazon.smithy.rust.codegen.core.util.runCommand
import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator
import software.amazon.smithy.rust.codegen.server.smithy.generators.CollectionConstraintViolationGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.CollectionTraitInfo
import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedBlobGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedCollectionGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedNumberGenerator
Expand Down Expand Up @@ -530,4 +533,17 @@ open class ServerCodegenVisitor(
).render(this)
}
}

override fun blobShape(shape: BlobShape) {
logger.info("[rust-server-codegen] Generating a service $shape")
if (shape.hasEventStreamMember(model)) {
return super.blobShape(shape)
}

if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) {
rustCrate.withModule(ModelsModule) {
ConstrainedBlobGenerator(codegenContext, this, shape).render()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,6 @@ private sealed class UnsupportedConstraintMessageKind {
),
)

is UnsupportedLengthTraitOnBlobShape -> LogMessage(
level,
buildMessageShapeHasUnsupportedConstraintTrait(shape, lengthTrait, constraintTraitsUberIssue),
)

is UnsupportedRangeTraitOnShape -> LogMessage(
level,
buildMessageShapeHasUnsupportedConstraintTrait(shape, rangeTrait, constraintTraitsUberIssue),
Expand All @@ -129,9 +124,6 @@ private data class UnsupportedLengthTraitOnStreamingBlobShape(
val streamingTrait: StreamingTrait,
) : UnsupportedConstraintMessageKind()

private data class UnsupportedLengthTraitOnBlobShape(val shape: Shape, val lengthTrait: LengthTrait) :
UnsupportedConstraintMessageKind()

private data class UnsupportedRangeTraitOnShape(val shape: Shape, val rangeTrait: RangeTrait) :
UnsupportedConstraintMessageKind()

Expand Down Expand Up @@ -240,18 +232,7 @@ fun validateUnsupportedConstraints(
val unsupportedConstraintShapeReachableViaAnEventStreamSet =
unsupportedConstraintOnNonErrorShapeReachableViaAnEventStreamSet + unsupportedConstraintErrorShapeReachableViaAnEventStreamSet

// 4. Length trait on blob shapes is used. It has not been implemented yet.
// TODO(https://github.com/awslabs/smithy-rs/issues/1401)
val unsupportedLengthTraitOnBlobShapeSet = walker
.walkShapes(service)
.asSequence()
.filterIsInstance<BlobShape>()
.mapNotNull {
it.getTrait<LengthTrait>()?.let { trait -> UnsupportedLengthTraitOnBlobShape(it, trait) }
}
.toSet()

// 5. Range trait used on unsupported shapes.
// 4. Range trait used on unsupported shapes.
// TODO(https://github.com/awslabs/smithy-rs/issues/1401)
val unsupportedRangeTraitOnShapeSet = walker
.walkShapes(service)
Expand All @@ -261,7 +242,7 @@ fun validateUnsupportedConstraints(
.map { (shape, rangeTrait) -> UnsupportedRangeTraitOnShape(shape, rangeTrait as RangeTrait) }
.toSet()

// 6. UniqueItems trait on any shape is used. It has not been implemented yet.
// 5. UniqueItems trait on any shape is used. It has not been implemented yet.
// TODO(https://github.com/awslabs/smithy-rs/issues/1401)
val unsupportedUniqueItemsTraitOnShapeSet = walker
.walkShapes(service)
Expand All @@ -279,7 +260,6 @@ fun validateUnsupportedConstraints(
unsupportedConstraintOnMemberShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedLengthTraitOnStreamingBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedConstraintShapeReachableViaAnEventStreamSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedLengthTraitOnBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedUniqueItemsTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package software.amazon.smithy.rust.codegen.server.smithy.customizations

import software.amazon.smithy.model.shapes.BlobShape
import software.amazon.smithy.model.shapes.ByteShape
import software.amazon.smithy.model.shapes.IntegerShape
import software.amazon.smithy.model.shapes.LongShape
Expand All @@ -31,7 +32,7 @@ class BeforeSerializingMemberJsonCustomization(private val codegenContext: Serve
codegenContext.settings.codegenConfig.publicConstrainedTypes,
)
) {
if (section.shape is IntegerShape || section.shape is ShortShape || section.shape is LongShape || section.shape is ByteShape) {
if (section.shape is IntegerShape || section.shape is ShortShape || section.shape is LongShape || section.shape is ByteShape || section.shape is BlobShape) {
section.context.valueExpression =
ValueExpression.Reference("&${section.context.valueExpression.name}.0")
}
Expand Down
Loading