// The format checks of `jsonschema._format` in upstream's `format-nongpl`
// configuration: all of its optional format dependencies (fqdn, idna,
// rfc3986-validator, rfc3987-syntax, rfc3339-validator, webcolors,
// jsonpointer, uri-template, isoduration) are ported in internal/formats.
///|
/// The format checker for draft 3.
pub let draft3_format_checker : FormatChecker = { checkers: Map([]), }
///|
/// The format checker for draft 4.
pub let draft4_format_checker : FormatChecker = { checkers: Map([]), }
///|
/// The format checker for draft 6.
pub let draft6_format_checker : FormatChecker = { checkers: Map([]), }
///|
/// The format checker for draft 7.
pub let draft7_format_checker : FormatChecker = { checkers: Map([]), }
///|
/// The format checker for draft 2019-09.
pub let draft201909_format_checker : FormatChecker = { checkers: Map([]), }
///|
/// The format checker for draft 2020-12.
pub let draft202012_format_checker : FormatChecker = { checkers: Map([]), }
///|
/// Upstream's `_checks_drafts`: register `func` under per-draft names in the
/// draft-specific checkers and (by its latest name) in the class registry.
fn checks_drafts(
func : FormatCheckFn,
name? : String,
draft3? : String,
draft4? : String,
draft6? : String,
draft7? : String,
draft201909? : String,
draft202012? : String,
raises? : (Error) -> Bool = raises_nothing,
) -> Unit {
let func : FormatCheckFn = instance => {
func(instance) catch {
e => raise lift_cause(e)
}
}
let or_ = (a : String?, b : String?) => if a is Some(_) { a } else { b }
let draft3 = or_(draft3, name)
let draft4 = or_(draft4, name)
let draft6 = or_(draft6, name)
let draft7 = or_(draft7, name)
let draft201909 = or_(draft201909, name)
let draft202012 = or_(draft202012, name)
let pairs = [
(draft3, draft3_format_checker),
(draft4, draft4_format_checker),
(draft6, draft6_format_checker),
(draft7, draft7_format_checker),
(draft201909, draft201909_format_checker),
(draft202012, draft202012_format_checker),
]
for pair in pairs {
if pair.0 is Some(format) {
pair.1.checks(format, func, raises~)
}
}
let latest = or_(
draft202012,
or_(draft201909, or_(draft7, or_(draft6, or_(draft4, draft3)))),
)
if latest is Some(format) {
FormatChecker::cls_checks(format, func, raises~)
}
}
///|
fn is_email(instance : Json) -> Bool {
guard instance is String(s) else { return true }
s.contains("@")
}
///|
fn is_ipv4(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.ipv4_address(s)
true
}
///|
fn is_ipv6(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
let scope_id = @formats.ipv6_address(s)
// `not getattr(address, "scope_id", "")`
match scope_id {
None => true
Some(id) => id == ""
}
}
///|
fn is_host_name(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.fqdn_is_valid(s, min_labels=1)
}
///|
fn is_idn_host_name(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.idna_encode(s)
true
}
///|
fn is_iri(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.rfc3987_syntax_is_valid("iri", s)
}
///|
fn is_iri_reference(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.rfc3987_syntax_is_valid("iri_reference", s)
}
///|
fn is_uri(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.validate_rfc3986(s, rule="URI")
}
///|
fn is_uri_reference(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.validate_rfc3986(s, rule="URI_reference")
}
///|
fn is_datetime(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.validate_rfc3339(@formats.ascii_upper(s))
}
///|
fn is_time(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
is_datetime(Json::string("1970-01-01T" + s))
}
///|
fn is_regex(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
ignore(@regex.compile(s))
true
}
///|
fn is_date(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.is_iso_date(s)
}
///|
fn is_draft3_time(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.strptime_hms(s)
true
}
///|
fn is_css21_color(instance : Json) -> Bool raise {
if instance is String(s) {
@formats.css_color(s)
}
true
}
///|
fn is_json_pointer(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.json_pointer(s)
true
}
///|
fn is_relative_json_pointer(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.relative_json_pointer(s)
}
///|
fn is_uri_template(instance : Json) -> Bool {
guard instance is String(s) else { return true }
@formats.uri_template_validate(s)
}
///|
fn is_duration(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.parse_duration(s)
// FIXME upstream: See bolsote/isoduration#25 and bolsote/isoduration#21
match s.get_char(s.length() - 1) {
Some('D' | 'M' | 'Y' | 'W' | 'H' | 'S') => true
_ => false
}
}
///|
fn is_uuid(instance : Json) -> Bool raise {
guard instance is String(s) else { return true }
@formats.uuid_round_trips(s)
}
///|
fn init {
// Registration order mirrors `jsonschema/_format.py` (it matters for the
// class-level registry, where draft 3's `time` replaces the RFC 3339 one).
checks_drafts(i => is_email(i), name="email")
checks_drafts(i => is_email(i), name="idn-email")
checks_drafts(
is_ipv4,
draft3="ip-address",
draft4="ipv4",
draft6="ipv4",
draft7="ipv4",
draft201909="ipv4",
draft202012="ipv4",
raises=is_address_value_error_cause,
)
checks_drafts(is_ipv6, name="ipv6", raises=is_address_value_error_cause)
checks_drafts(
is_host_name,
draft3="host-name",
draft4="hostname",
draft6="hostname",
draft7="hostname",
draft201909="hostname",
draft202012="hostname",
raises=is_value_error_cause,
)
checks_drafts(
is_idn_host_name,
draft7="idn-hostname",
draft201909="idn-hostname",
draft202012="idn-hostname",
raises=is_idna_error_cause,
)
checks_drafts(is_uri, name="uri")
checks_drafts(
is_uri_reference,
draft6="uri-reference",
draft7="uri-reference",
draft201909="uri-reference",
draft202012="uri-reference",
raises=is_value_error_cause,
)
checks_drafts(
is_iri,
draft7="iri",
draft201909="iri",
draft202012="iri",
raises=is_value_error_cause,
)
checks_drafts(
is_iri_reference,
draft7="iri-reference",
draft201909="iri-reference",
draft202012="iri-reference",
raises=is_value_error_cause,
)
checks_drafts(is_datetime, name="date-time")
checks_drafts(is_time, draft7="time", draft201909="time", draft202012="time")
checks_drafts(is_regex, name="regex", raises=is_pattern_error_cause)
checks_drafts(
is_date,
draft3="date",
draft7="date",
draft201909="date",
draft202012="date",
raises=is_value_error_cause,
)
checks_drafts(is_draft3_time, draft3="time", raises=is_value_error_cause)
checks_drafts(is_css21_color, draft3="color", raises=is_value_error_cause)
checks_drafts(
is_json_pointer,
draft6="json-pointer",
draft7="json-pointer",
draft201909="json-pointer",
draft202012="json-pointer",
raises=is_json_pointer_cause,
)
checks_drafts(
is_relative_json_pointer,
draft7="relative-json-pointer",
draft201909="relative-json-pointer",
draft202012="relative-json-pointer",
raises=is_json_pointer_cause,
)
checks_drafts(
i => is_uri_template(i),
draft6="uri-template",
draft7="uri-template",
draft201909="uri-template",
draft202012="uri-template",
)
checks_drafts(
is_duration,
draft201909="duration",
draft202012="duration",
raises=is_duration_cause,
)
checks_drafts(
is_uuid,
draft201909="uuid",
draft202012="uuid",
raises=is_value_error_cause,
)
}