use anyhow::Error;
use serde_json::Value;
#[non_exhaustive]
#[derive(Debug, Default, PartialEq, Copy, Clone, Hash, Eq, PartialOrd, Ord)]
pub enum Draft {
Draft4,
Draft6,
Draft7,
Draft201909,
#[default]
Draft202012,
}
impl Draft {
#[must_use]
pub fn create_resource_ref(self, contents: &Value) -> ResourceRef<'_> {
ResourceRef::new(contents, self)
}
pub fn detect(self, contents: &Value) -> Result<Draft, Error> {
if let Some(schema) = contents
.as_object()
.and_then(|contents| contents.get("$schema"))
.and_then(|schema| schema.as_str())
{
Ok(match schema.trim_end_matches('#') {
"https://json-schema.org/draft/2020-12/schema" => Draft::Draft202012,
"https://json-schema.org/draft/2019-09/schema" => Draft::Draft201909,
"http://json-schema.org/draft-07/schema" => Draft::Draft7,
"http://json-schema.org/draft-06/schema" => Draft::Draft6,
"http://json-schema.org/draft-04/schema" => Draft::Draft4,
value => return Err(anyhow::anyhow!("Unknown specification: {}", value)),
})
} else {
Ok(self)
}
}
#[must_use]
pub fn is_known_keyword(&self, keyword: &str) -> bool {
match keyword {
"$ref"
| "$schema"
| "additionalItems"
| "additionalProperties"
| "allOf"
| "anyOf"
| "dependencies"
| "enum"
| "exclusiveMaximum"
| "exclusiveMinimum"
| "format"
| "items"
| "maxItems"
| "maxLength"
| "maxProperties"
| "maximum"
| "minItems"
| "minLength"
| "minProperties"
| "minimum"
| "multipleOf"
| "not"
| "oneOf"
| "pattern"
| "patternProperties"
| "properties"
| "required"
| "type"
| "uniqueItems" => true,
"id" if *self == Draft::Draft4 => true,
"$id" | "const" | "contains" | "propertyNames" if *self >= Draft::Draft6 => true,
"contentEncoding" | "contentMediaType"
if matches!(self, Draft::Draft6 | Draft::Draft7) =>
{
true
}
"else" | "if" | "then" if *self >= Draft::Draft7 => true,
"$anchor"
| "$defs"
| "$recursiveAnchor"
| "$recursiveRef"
| "dependentRequired"
| "dependentSchemas"
| "maxContains"
| "minContains"
| "prefixItems"
| "unevaluatedItems"
| "unevaluatedProperties"
if *self >= Draft::Draft201909 =>
{
true
}
"$dynamicAnchor" | "$dynamicRef" if *self == Draft::Draft202012 => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ResourceRef<'a> {
contents: &'a Value,
draft: Draft,
}
impl<'a> ResourceRef<'a> {
#[must_use]
pub fn new(contents: &'a Value, draft: Draft) -> Self {
Self { contents, draft }
}
#[must_use]
pub fn contents(&self) -> &'a Value {
self.contents
}
#[must_use]
#[allow(dead_code)]
pub fn draft(&self) -> Draft {
self.draft
}
}