///|
pub struct PreparedValidator {
priv core : @validation.PreparedValidator
}
///|
pub fn PreparedValidator::prepare(
model : @smithy.Model,
shape : @smithy.ShapeId,
config~ : @validation.Config = @validation.Config::new(),
) -> PreparedValidator raise @validation.PreparationError {
{
core: @validation.PreparedValidator::prepare(
model,
shape,
config=config.with_profile(@validation.NativeProfile),
),
}
}
///|
pub fn PreparedValidator::validate(
self : PreparedValidator,
value : @ion.Ion,
) -> ValidationResult {
let kinds : Map[@validation.InstancePath, @ion.IonType] = Map([])
let view = IonView::new(value, @validation.InstancePath::root(), kinds)
let result = self.core.validate(view)
{
findings: result.findings.map(f => {
let native_kind = if f.code == @validation.RequiredMember {
None
} else if f.path.steps().last()
is Some(@validation.MapKey(_, _) | @validation.FieldNamePosition(_)) {
Some(@ion.IonType::Symbol)
} else {
kinds.get(f.path)
}
{
code: f.code,
severity: f.severity,
path: f.path,
shape: f.shape,
message: f.message,
native_kind,
}
}),
complete: result.complete,
}
}
///|
priv struct IonView {
value : @ion.Ion
path : @validation.InstancePath
kinds : Map[@validation.InstancePath, @ion.IonType]
bare : Bool
}
///|
fn IonView::new(
value : @ion.Ion,
path : @validation.InstancePath,
kinds : Map[@validation.InstancePath, @ion.IonType],
) -> IonView {
kinds.set(path, value.ion_type())
{ value, path, kinds, bare: false, }
}
///|
fn native_kind(kind : @ion.IonType) -> @validation.NativeKind {
match kind {
Null => @validation.NullKind
Bool => @validation.BoolKind
Int => @validation.IntKind
Float => @validation.FloatKind
Decimal => @validation.DecimalKind
Timestamp => @validation.TimestampKind
Symbol => @validation.SymbolKind
String => @validation.StringKind
Blob => @validation.BlobKind
Clob => @validation.ClobKind
Struct => @validation.StructKind
List => @validation.ListKind
Sexp => @validation.SexpKind
}
}
///|
fn decimal_number(value : @ion.Decimal) -> @smithy.NodeNumber? {
let coefficient = if value.is_negative_zero() {
"-0"
} else {
value.coefficient().to_string()
}
@smithy.NodeNumber::parse(coefficient + "e" + value.exponent().to_string())
}
///|
fn timestamp_scalar(value : @ion.Timestamp) -> @validation.NativeScalar {
let fraction = match value.fraction() {
Some(decimal) =>
match decimal_number(decimal) {
Some(number) => Some(number)
None => return @validation.InvalidTimestamp
}
None => None
}
let offset = match value.offset() {
Some(@ion.Known(minutes)) => @validation.KnownOffsetMinutes(minutes)
Some(@ion.Unknown) | None => @validation.UnknownOffset
}
let timestamp = @validation.NativeTimestamp::new(
value.year(),
month=value.month(),
day=value.day(),
hour=value.hour(),
minute=value.minute(),
second=value.second(),
fraction~,
offset~,
) catch {
_ => return @validation.InvalidTimestamp
}
@validation.Timestamp(timestamp)
}
///|
impl @validation.ValueView for IonView with fn expansion_size(self) {
let children = match self.value.kind() {
List(values) | Sexp(values) => values.length()
Struct(fields) => fields.length()
_ => 0
}
let annotations = if self.bare {
0
} else {
self.value.annotations().length()
}
if children > annotations {
children
} else {
annotations
}
}
///|
impl @validation.ValueView for IonView with fn inspect(self) {
if !self.bare && self.value.has_annotations() {
return @validation.Annotated(
self.value.annotations().map(token => token.text()),
{ ..self, bare: true, },
)
}
match self.value.kind() {
Null(kind) => @validation.Native(@validation.TypedNull(native_kind(kind)))
Bool(value) => @validation.Boolean(value)
Int(value) =>
match @smithy.NodeNumber::parse(value.to_string()) {
Some(number) => @validation.Number(number)
None => @validation.InvalidNumber
}
Float(value) => @validation.Native(@validation.Float(value))
Decimal(value) =>
match decimal_number(value) {
Some(number) => @validation.Native(@validation.Decimal(number))
None => @validation.InvalidNumber
}
Timestamp(value) => @validation.Native(timestamp_scalar(value))
Symbol(token) => @validation.Native(@validation.Symbol(token.text()))
String(value) => @validation.String(value)
Blob(value) => @validation.Native(@validation.Blob(value))
Clob(value) => @validation.Native(@validation.Clob(value))
List(values) =>
@validation.Array(
values.mapi((index, value) => {
IonView::new(
value,
self.path.index(index) catch {
_ => self.path
},
self.kinds,
)
}),
)
Sexp(values) =>
@validation.Sexp(
values.mapi((index, value) => {
IonView::new(
value,
self.path.index(index) catch {
_ => self.path
},
self.kinds,
)
}),
)
Struct(fields) => {
let occurrences : Map[String, Int32] = Map([])
@validation.NativeObject(
fields.mapi((position, field) => {
let name = field.name().text()
let path = match name {
Some(name) => {
let occurrence = occurrences.get(name).unwrap_or(0)
occurrences.set(name, occurrence + 1)
self.path.field(name, occurrence~) catch {
_ => self.path
}
}
None => self.path.field_position(position) catch { _ => self.path }
}
(name, IonView::new(field.value(), path, self.kinds))
}),
)
}
}
}