Direct application value validation against Smithy shapes
///|
test {
let loaded = @smithy.Loader::new()
.add_idl(
"model.smithy", "$version: \"2\"\nnamespace example\n@range(min: 1, max: 100)\ninteger Count\nstructure Input { @required count: Count }",
)
.assemble()
let prepared = @validation.PreparedValidator::prepare(
loaded.model.unwrap(),
@smithy.ShapeId::parse("example#Input").unwrap(),
)
assert_true(
prepared
.validate_json(@validation.parse_json_value("{\"count\":2}"))
.is_valid(),
)
let result = prepared.validate_json(
@validation.parse_json_value("{\"count\":0}"),
)
assert_false(result.is_valid())
assert_eq(result.findings[0].code, @validation.Range)
assert_eq(result.findings[0].path.pointer(), "/count")
assert_eq(result.findings[0].shape.to_string(), "example#Input$count")
}///|
test "validate an order" {
let model = @smithy.Loader::new()
.add_idl(
"order.smithy",
(
#|$version: "2"
#|
#|namespace example.orders
#|
#|/// An order in a shop.
#|structure Order {
#| @required
#| id: String
#|
#| @range(min: 1, max: 10)
#| quantity: Integer
#|
#| @length(max: 20)
#| note: String
#|}
),
)
.assemble().model.unwrap()
let prepared = @validation.PreparedValidator::prepare(
model,
@smithy.ShapeId::parse("example.orders#Order").unwrap(),
)
let order =
#|{
#| "id": "A-1001",
#| "quantity": 2,
#| "note": "Leave at the door"
#|}
assert_true(
prepared.validate_json(@validation.parse_json_value(order)).is_valid(),
)
let too_many =
#|{
#| "id": "A-1001",
#| "quantity": 11,
#| "note": "Leave at the door"
#|}
let result = prepared.validate_json(@validation.parse_json_value(too_many))
assert_false(result.is_valid())
assert_eq(result.findings.length(), 1)
assert_eq(result.findings[0].code, @validation.Range)
assert_eq(result.findings[0].path.pointer(), "/quantity")
assert_eq(
result.findings[0].shape.to_string(),
"example.orders#Order$quantity",
)
}smithy validate value --shape example#Input --value request.json model.smithy
smithy validate value --shape example#Input --value - --diagnostics json model.smithy| Concern | Default and explicit choices |
|---|---|
| Unknown structure fields | Reject by default. @smithy.rpc#unknownMembers("ignore") on a structure overrides the config or CLI policy. Unknown union alternatives always fail. |
| Member names | Smithy names; member_names=JsonName or CLI --member-names json uses jsonName. Preparation rejects collisions. |
| Null | Distinct from absence. Optional members may be absent but must match their target when present. Document and sparse list/map values permit null. |
| Defaults/coercion | No mutation, default insertion or string-to-number coercion. Defaults do not excuse an absent required member. |
| Constraints | Exact range, Unicode scalar string length, decoded blob byte length, collection length, pattern, uniqueness and enum checks. Each explicit member trait replaces that same target trait as a whole. |
| Numbers | Exact mathematical integers and byte/short/integer/long bounds. BigInteger/BigDecimal remain exact. Float/double magnitude is checked without rounding input. |
| Nonnumeric floats | Opt-in nonnumeric_floats=true permits exactly NaN, Infinity, -Infinity strings, subject to range rules. |
| Blob | Canonical padded RFC 4648 base64, including zero padding bits. |
| Timestamp | Exact epoch-seconds number; timestamp_format=DateTime or effective timestampFormat("date-time") selects a calendar-checked string with an offset, including recorded positive leap seconds. HTTP-date is unsupported. |
| Runtime contracts | Reachable smithy.contracts#conditions raises UnsupportedConstraint. Assembly still validates the expression definition. |
pub(open) trait ValueView {
fn inspect(Self) -> ValueNode[Self]
fn expansion_size(Self) -> Int = _
fn source(Self) -> InstancePath? = _
}pub(all) suberror ValueInputError {
InvalidJson(Int, String)
DuplicateJsonKey(Int)
JsonInputLimit(Int, String)
} derive(Debug)pub struct Config {
profile : ValueProfile
limits : Limits
unknown_members : UnknownMembers
member_names : MemberNames
timestamp_format : TimestampFormat
nonnumeric_floats : Bool
} derive(Eq, Debug)fn Config::new(profile? : ValueProfile, limits? : Limits, unknown_members? : UnknownMembers, member_names? : MemberNames, timestamp_format? : TimestampFormat, nonnumeric_floats? : Bool) -> Configpub(all) struct Finding {
code : FindingCode
severity : FindingSeverity
path : InstancePath
shape : ShapeId
message : String
} derive(Eq, Debug)impl ToJson for InstancePathfn InstancePath::field(self : InstancePath, name : String, occurrence? : Int) -> InstancePath raise ConfigurationErrorfn InstancePath::field_name_position(self : InstancePath, index : Int) -> InstancePath raise ConfigurationErrorfn InstancePath::field_position(self : InstancePath, index : Int) -> InstancePath raise ConfigurationErrorfn InstancePath::map_key(self : InstancePath, name : String, occurrence? : Int) -> InstancePath raise ConfigurationErrorpub(all) enum NativeScalar {
TypedNull(NativeKind)
Float(Double)
Decimal(NodeNumber)
Timestamp(NativeTimestamp)
InvalidTimestamp
Symbol(String?)
Blob(Bytes)
Clob(Bytes)
} derive(Debug)pub struct NativeTimestamp {
year : Int
month : Int?
day : Int?
hour : Int?
minute : Int?
second : Int?
fraction : NodeNumber?
offset : NativeOffset
} derive(Debug)impl Eq for NativeTimestampfn NativeTimestamp::new(year : Int, month? : Int?, day? : Int?, hour? : Int?, minute? : Int?, second? : Int?, fraction? : NodeNumber?, offset? : NativeOffset) -> NativeTimestamp raise ConfigurationErrorfn PreparedValidator::prepare(model : Model, target : ShapeId, config? : Config) -> PreparedValidator raise PreparationErrorfn PreparedValidator::unknown_members(self : PreparedValidator, target : ShapeId) -> UnknownMembers?fn[V : ValueView] PreparedValidator::validate(self : PreparedValidator, value : V, base_path? : InstancePath) -> ValidationResultfn PreparedValidator::validate_json(self : PreparedValidator, value : Json, base_path? : InstancePath) -> ValidationResultfn[V : ValueView] PreparedValidator::validate_metered(self : PreparedValidator, value : V, meter~ : WorkMeter, base_path? : InstancePath) -> ValidationResultpub struct StructureContract {
shape : ShapeId
unknown_members : UnknownMembers
members : Array[ValueMemberContract]
} derive(Eq, Debug)impl ToJson for ValidationResultpub(all) enum ValueNode[V] {
Null
Boolean(Bool)
Number(NodeNumber)
InvalidNumber
String(String)
Array(Array[V])
Object(Array[(String, V)])
NativeObject(Array[(String?, V)])
LocatedObject(Array[LocatedField[V]])
Native(NativeScalar)
Sexp(Array[V])
Annotated(Array[String?], V)
}pub struct WorkMeter {
// private fields
}#internal(internal, "a helper of the smithy-validation packages")
fn utf8_length(text : StringView) -> Int