Unverified Commit 42701d5b authored by Fahad Zubair's avatar Fahad Zubair Committed by GitHub
Browse files

Impl std::error::Error for a shape's ConstraintViolation struct (#3431)



## Motivation and Context
Each constrained shape has an associated `ConstraintViolation`
structure, which at the moment does not implement `std::error::Error`.

## Description

All of the `ConstraintViolation` structures now implements
`std::fmt::Display` and the `std::error::Error` marker trait. There is a
difference between the message used for `pub(crate) fn
as_validation_exception_field` and for `std::fmt::Display` as the latter
does not know of the particular field which is being set in the
structure. Hence, it uses the shape ID instead.

For example, for a constrained `int`, the following is the message used
in `Display`:

```
write!(f, "Value for `com.aws.example#MyNumberSensitive`failed to satisfy constraint: Member must be greater than or equal to 100")

```
and the following is used for `as_validation_exception_field`:

```
format!("Value at '{}' failed to satisfy constraint: Member must be greater than or equal to 100", &path),
```

## Testing

Tests have been added to ensure:

1. `std::fmt::Display` has all of the variants for the enum that
represents the associated `Error` type in the `ConstraintViolation`
struct.
2. `ConstraintViolation` implements `std::error::Error`.

---------

Co-authored-by: default avatarFahad Zubair <fahadzub@amazon.com>
parent 1f5cb697
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -41,3 +41,9 @@ message = "Fix an S3 crate's dependency on `ahash` so the crate can be compiled
references = ["smithy-rs#3590", "aws-sdk-rust#1131"]
meta = { "breaking" = false, "tada" = false, "bug" = true }
author = "ysaito1001"

[[smithy-rs]]
message = "Implement `std::error::Error` for `ConstraintViolation`"
references = ["smithy-rs#3430"]
meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "server" }
author = "drganjoo"
+17 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

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

import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.traits.EnumTrait

fun EnumTrait.validationErrorMessage() =
    "Value at '{}' failed to satisfy constraint: Member must satisfy enum value set: [${enumValueSet()}]"

fun EnumTrait.shapeConstraintViolationDisplayMessage(shape: Shape) =
    "Value provided for '${shape.id}' failed to satisfy constraint: Member must satisfy enum value set: [${enumValueSet()}]"

fun EnumTrait.enumValueSet() = this.enumDefinitionValues.joinToString(", ")
+16 −15
Original line number Diff line number Diff line
@@ -5,20 +5,21 @@

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

import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.traits.LengthTrait

fun LengthTrait.validationErrorMessage(): String {
    val beginning = "Value with length {} at '{}' failed to satisfy constraint: Member must have length "
    val ending =
fun LengthTrait.validationErrorMessage() =
    "Value with length {} at '{}' failed to satisfy constraint: Member must have length ${this.lengthDescription()}"

fun LengthTrait.shapeConstraintViolationDisplayMessage(shape: Shape) =
    "Value with length {} provided for '${shape.id}' failed to satisfy constraint: Member must have length ${this.lengthDescription()}"

fun LengthTrait.lengthDescription() =
    if (this.min.isPresent && this.max.isPresent) {
        "between ${this.min.get()} and ${this.max.get()}, inclusive"
    } else if (this.min.isPresent) {
            (
        "greater than or equal to ${this.min.get()}"
            )
    } else {
        check(this.max.isPresent)
        "less than or equal to ${this.max.get()}"
    }
    return "$beginning$ending"
}
+15 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

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

import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.traits.PatternTrait

fun PatternTrait.validationErrorMessage() =
    "Value at '{}' failed to satisfy constraint: Member must satisfy regular expression pattern: {}"

fun PatternTrait.shapeConstraintViolationDisplayMessage(shape: Shape) =
    "Value provided for `${shape.id}` failed to satisfy the constraint: Member must match the regular expression pattern: {}"
+16 −15
Original line number Diff line number Diff line
@@ -5,20 +5,21 @@

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

import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.traits.RangeTrait

fun RangeTrait.validationErrorMessage(): String {
    val beginning = "Value at '{}' failed to satisfy constraint: Member must be "
    val ending =
fun RangeTrait.validationErrorMessage() =
    "Value at '{}' failed to satisfy constraint: Member must be ${this.rangeDescription()}"

fun RangeTrait.shapeConstraintViolationDisplayMessage(shape: Shape) =
    "Value for `${shape.id}`failed to satisfy constraint: Member must be ${this.rangeDescription()}"

fun RangeTrait.rangeDescription() =
    if (this.min.isPresent && this.max.isPresent) {
        "between ${this.min.get()} and ${this.max.get()}, inclusive"
    } else if (this.min.isPresent) {
            (
        "greater than or equal to ${this.min.get()}"
            )
    } else {
        check(this.max.isPresent)
        "less than or equal to ${this.max.get()}"
    }
    return "$beginning$ending"
}
Loading