///|
test "load an ISL 1.0 schema" {
let schema = @schema.Schema::load_from_text(
(
#|$ion_schema_1_0
#|type::{
#| name: price,
#| content: closed,
#| fields: {
#| amount: { type: decimal, scale: 2, occurs: required },
#| note: nullable::string,
#| },
#|}
),
)
assert_true(
schema.is_valid("price", @text.read_ion("{ amount: 9.99, note: null }")),
)
// 9.9 has a scale of 1, `tax` is not a field of the type, and the default
// type `any` has no null.
assert_true(!schema.is_valid("price", @text.read_ion("{ amount: 9.9 }")))
assert_true(
!schema.is_valid("price", @text.read_ion("{ amount: 9.99, tax: 1.00 }")),
)
assert_true(!schema.is_valid("price", @text.read_ion("null.struct")))
}///|
test "validate records against a type" {
let definition =
#|$ion_schema_2_0
#|type::{
#| name: person,
#| type: struct,
#| fields: {
#| name: { type: string, occurs: required, codepoint_length: range::[1, 40] },
#| role: { valid_values: [admin, member, guest] },
#| tags: { type: list, element: symbol },
#| },
#|}
let schema = @schema.Schema::load_from_text(definition)
assert_true(
schema.is_valid("person", @text.read_ion("{ name: \"Ada\", role: admin }")),
)
let violations = schema.validate(
"person",
@text.read_ion("{ role: owner, tags: [a, \"b\"] }"),
)
let report = violations
.map(violation => violation.path() + ": " + violation.message())
.join("\n")
inspect(
report,
content=(
#|$.name: field occurs 0 time(s)
#|$.role: value is not one of the allowed values
#|$.tags[1]: expected one of [symbol] but found string
),
)
}///|
test "import a type from another schema" {
let schemas : Map[String, String] = {
"common.isl": "$ion_schema_2_0 type::{ name: positive_int, type: int, valid_values: range::[1, max] }",
}
let resolver = (id : String) => {
match schemas.get(id) {
Some(text) => @text.read_ion_datagram(text)
None => raise @ion.IonError::DataModel("no schema named " + id)
}
}
let schema = @schema.Schema::load_from_text(
(
#|$ion_schema_2_0
#|schema_header::{ imports: [{ id: "common.isl", type: positive_int, as: count }] }
#|type::{ name: tally, fields: { votes: count } }
),
resolver~,
)
assert_true(schema.is_valid("tally", @text.read_ion("{ votes: 3 }")))
assert_true(!schema.is_valid("tally", @text.read_ion("{ votes: 0 }")))
// Without a resolver, the import cannot load.
let message = try
@schema.Schema::load_from_text(
"$ion_schema_2_0 schema_header::{ imports: [{ id: \"common.isl\" }] }",
)
catch {
error => error.message()
} noraise {
_ => "loaded"
}
inspect(
message,
content="the schema imports 'common.isl', but no resolver was given to load it",
)
}pub(all) enum Constraint {
IsType(Array[IonType])
NullOr(Constraint)
TypeRef(String)
Element(Constraint)
Fields(Array[FieldConstraint])
ValidValues(Array[Ion])
InRange(ValueRange)
ContainerLength(Int, Int?)
CodepointLength(Int, Int?)
AllOf(Array[Constraint])
AnyOf(Array[Constraint])
OneOf(Array[Constraint])
Not(Constraint)
Nothing
IsDocument
Matches(Regex)
ByteLength(Int, Int?)
Utf8ByteLength(Int, Int?)
Precision(Int, Int?)
Exponent(Int, Int?)
TimestampPrecision(Int, Int?)
TimestampOffsets(Array[TimestampOffset])
Ieee754Float(FloatFormat)
FieldNames(Constraint)
DistinctFieldNames
DistinctElements
ClosedFields(Array[String])
Annotations(Constraint)
OrderedElements(Array[(Constraint, Occurs)])
Contains(Array[Ion])
} derive(Debug)pub struct Regex {
source : String
ignore_case : Bool
multiline : Bool
// private fields
} derive(Debug)Install
Download zipA MoonBit implementation of the Amazon Ion data format and Ion Schema.
Dependencies