// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
///|
/// Which version of JSON Schema a document is written against.
///
/// The five released versions that are still in use. `draft-03` is not here: it
/// predates the `$` keywords and nothing has been written against it since 2010.
/// The next release is not here yet either; it arrives as another value of this
/// enum and a keyword table, not as a second validator.
pub(all) enum Draft {
/// draft-04 (2013). What OpenAPI 3.0 profiles, and what a great deal of Java
/// and Python tooling still emits.
Draft4
/// draft-06 (2017). `$id` replaces `id`, `exclusiveMinimum` becomes a number.
Draft6
/// draft-07 (2018). The most widely deployed version.
Draft7
/// 2019-09. Vocabularies, `$defs`, `$recursiveRef`, and the annotation-aware
/// `unevaluatedProperties`.
Draft2019
/// 2020-12. `$dynamicRef`, `prefixItems`, and what OpenAPI 3.1 is.
Draft2020
} derive(Eq, Debug)
///|
pub extend Draft with Eq::{equal, not_equal}
///|
pub extend Draft with Debug::{to_repr}
///|
/// A version prints as the name the specification gives it, which is what a
/// message about a document should say.
pub impl Show for Draft with fn output(self, logger) {
logger.write_string(
match self {
Draft4 => "draft-04"
Draft6 => "draft-06"
Draft7 => "draft-07"
Draft2019 => "2019-09"
Draft2020 => "2020-12"
},
)
}
///|
pub extend Draft with Show::{output, to_string}
///|
/// The version assumed when a document does not name one.
///
/// 2020-12: the current release, and the one OpenAPI 3.1 is defined against. A
/// document that says `$schema` is read at the version it says, whatever this is
/// set to — the document is more specific than the setting.
pub let draft : Draft = Draft2020
///|
/// The `$schema` URI that names this version.
pub fn Draft::uri(self : Draft) -> String {
match self {
Draft4 => "http://json-schema.org/draft-04/schema#"
Draft6 => "http://json-schema.org/draft-06/schema#"
Draft7 => "http://json-schema.org/draft-07/schema#"
Draft2019 => "https://json-schema.org/draft/2019-09/schema"
Draft2020 => "https://json-schema.org/draft/2020-12/schema"
}
}
///|
/// The version a `$schema` URI names.
///
/// It answers `None` for a URI it does not know rather than falling back to a
/// version of its own choosing: a document written against something else is not
/// a document to guess at. Both the `http` and `https` spellings are accepted,
/// and a trailing `#` is optional, because both are found in the wild.
pub fn Draft::of(uri : StringView) -> Draft? {
let trimmed = if uri.has_suffix("#") {
uri[0:uri.length() - 1]
} else {
uri[:]
}
let bare = if trimmed.has_prefix("https://") {
trimmed[8:]
} else if trimmed.has_prefix("http://") {
trimmed[7:]
} else {
trimmed
}
match bare.to_owned() {
"json-schema.org/draft-04/schema" => Some(Draft4)
"json-schema.org/draft-06/schema" => Some(Draft6)
"json-schema.org/draft-07/schema" => Some(Draft7)
"json-schema.org/draft/2019-09/schema" => Some(Draft2019)
"json-schema.org/draft/2020-12/schema" => Some(Draft2020)
_ => None
}
}
///|
/// How much a validation says when it fails.
///
/// The four formats the specification itself defines (2020-12 §12.4), named as
/// it names them.
pub(all) enum Output {
/// Whether it validated, and nothing else. The cheapest, and what a guard on
/// a request wants.
Flag
/// A flat list of the errors, each with the location in the instance and in
/// the schema.
Basic
/// The errors as a tree, with the schema's own structure preserved.
Detailed
/// Every annotation as well as every error, which is what a tool that explains
/// a schema to a person needs.
Verbose
} derive(Eq, Debug)
///|
pub extend Output with Eq::{equal, not_equal}
///|
pub extend Output with Debug::{to_repr}
///|
/// An output format prints as the specification names it (2020-12 §12.4).
pub impl Show for Output with fn output(self, logger) {
logger.write_string(
match self {
Flag => "flag"
Basic => "basic"
Detailed => "detailed"
Verbose => "verbose"
},
)
}
///|
pub extend Output with Show::{output, to_string}
///|
/// The output format a validation produces unless asked for another.
///
/// `Flag`, because the common case is a guard that wants a yes or a no and
/// collecting errors nobody reads is work nobody asked for. A caller that wants
/// to tell someone what went wrong asks for `Basic`.
pub let output : Output = Flag