use std::sync::Arc;
use referencing::{Draft, Registry};
use serde_json::Value;
use crate::{
canonical::{
ir::{RawJson, Schema, SchemaKind},
parse,
schema::CanonicalSchema,
CanonicalizationError, DefinitionMap,
},
compiler::{formats_are_assertions_by_default, validate_schema},
options::PatternEngineOptions,
};
#[must_use]
pub fn options() -> CanonicalizeOptions<'static> {
CanonicalizeOptions::default()
}
#[derive(Default)]
pub struct CanonicalizeOptions<'r> {
registry: Option<&'r Registry<'r>>,
pattern_options: PatternEngineOptions,
draft: Option<Draft>,
validate_formats: Option<bool>,
}
impl<'r> CanonicalizeOptions<'r> {
#[must_use]
pub fn with_registry(mut self, registry: &'r Registry<'r>) -> Self {
self.registry = Some(registry);
self
}
#[must_use]
pub fn with_draft(mut self, draft: Draft) -> Self {
self.draft = Some(draft);
self
}
#[must_use]
pub fn should_validate_formats(mut self, enabled: bool) -> Self {
self.validate_formats = Some(enabled);
self
}
pub fn canonicalize(self, value: &Value) -> Result<CanonicalSchema, CanonicalizationError> {
build(
value,
self.draft,
self.registry,
self.validate_formats,
self.pattern_options,
)
}
}
fn build(
value: &Value,
draft: Option<Draft>,
registry: Option<&Registry<'_>>,
validate_formats: Option<bool>,
pattern_options: PatternEngineOptions,
) -> Result<CanonicalSchema, CanonicalizationError> {
match value {
Value::Bool(_) | Value::Object(_) => {}
other => return Err(CanonicalizationError::InvalidSchemaType(other.to_string())),
}
let draft = detect_draft(value, draft, registry)?;
let validate_formats =
validate_formats.unwrap_or_else(|| formats_are_assertions_by_default(draft));
validate_schema(draft, value)?;
let inner = parse::parse(value, draft)
.unwrap_or_else(|| Schema::new(SchemaKind::Raw(RawJson::new(value.clone()))));
Ok(CanonicalSchema::new(
inner,
draft,
pattern_options,
validate_formats,
Arc::new(DefinitionMap::new()),
))
}
fn detect_draft<'r>(
value: &Value,
draft: Option<Draft>,
registry: Option<&'r Registry<'r>>,
) -> Result<Draft, CanonicalizationError> {
let mut options = crate::options();
if let Some(draft) = draft {
options = options.with_draft(draft);
}
if let Some(registry) = registry {
options = options.with_registry(registry);
}
options
.draft_for(value)
.map_err(|error| CanonicalizationError::InvalidSchemaType(error.to_string()))
}