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")
}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; unknown_members=Ignore or CLI --unknown-members ignore permits them. 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. HTTP-date and leap-second notation are unsupported. |
| Runtime contracts | Reachable smithy.contracts#conditions raises UnsupportedConstraint. Assembly still validates the expression definition. |
pub(all) suberror ValueInputError {
InvalidJson(Int, String)
DuplicateJsonKey(Int)
JsonInputLimit(Int, String)
} derive(Debug)pub struct Config {
limits : Limits
unknown_members : UnknownMembers
member_names : MemberNames
timestamp_format : TimestampFormat
nonnumeric_floats : Bool
} derive(Eq, Debug)fn Config::new(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::map_key(self : InstancePath, name : String, occurrence? : Int) -> InstancePath raise ConfigurationErrorpub(all) enum NativeScalar {
TypedNull(NativeKind)
Float(Double)
Decimal(NodeNumber)
Timestamp(NativeTimestamp)
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)fn 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[V : ValueView] PreparedValidator::validate(self : PreparedValidator, value : V, base_path? : InstancePath) -> ValidationResultfn PreparedValidator::validate_json(self : PreparedValidator, value : Json, base_path? : InstancePath) -> ValidationResultimpl ToJson for ValidationResultpub(all) enum ValueNode[V] {
Null
Boolean(Bool)
Number(NodeNumber)
InvalidNumber
String(String)
Array(Array[V])
Object(Array[(String, V)])
LocatedObject(Array[LocatedField[V]])
Native(NativeScalar)
Sexp(Array[V])
Annotated(Array[String?], V)
}