use crate::{error::Error, storage::SchemaStorage};
use serde_json::Value;
use url::Url;
use crate::{schema::Schema, scope::SchemaScope};
pub struct SchemaResolver<'a> {
url: Url,
storage: Option<&'a SchemaStorage>,
}
impl<'a> SchemaResolver<'a> {
pub fn new(schema: &Schema, storage: &'a SchemaStorage) -> Self {
Self {
url: schema.get_url().clone(),
storage: Some(storage),
}
}
pub fn empty() -> Self {
Self {
url: Url::parse("inline://none").unwrap(),
storage: None,
}
}
pub fn resolve<F, T>(&self, node: &Value, scope: &mut SchemaScope, mut f: F) -> Result<T, Error>
where
F: FnMut(&Value, &mut SchemaScope) -> Result<T, Error>,
{
if !node.is_object()
|| node.as_object().unwrap().get("$ref").is_none()
|| self.storage.is_none()
{
return f(node, scope);
}
match self.storage {
Some(storage) => match node.as_object().unwrap().get("$ref").unwrap() {
Value::String(reference) => {
let mut url = super::storage::ref_to_url(&self.url, reference).unwrap();
let copy = url.clone();
let pointer = copy.fragment();
url.set_fragment(None);
let referenced_schema = storage.schemas.get(&url);
match referenced_schema {
Some(schema) => match pointer {
Some(p) => {
if let Some(s) = schema.get_body().pointer(p) {
scope.reference(p);
let result = self.resolve(s, scope, f);
scope.pop();
result
} else {
log::error!("Cannot resolve: {}", p);
f(node, scope)
}
}
None => f(schema.get_body(), scope),
},
None => {
log::error!("Cannot find schema: {}", url);
f(node, scope)
}
}
}
_ => {
log::error!("Invalid reference");
f(node, scope)
}
},
None => f(node, scope),
}
}
pub fn resolve_once<F, T>(
&self,
node: &Value,
scope: &mut SchemaScope,
mut f: F,
) -> Result<T, Error>
where
F: FnMut(&Value, &mut SchemaScope) -> Result<T, Error>,
{
if !node.is_object()
|| node.as_object().unwrap().get("$ref").is_none()
|| self.storage.is_none()
{
return f(node, scope);
}
match self.storage {
Some(storage) => match node.as_object().unwrap().get("$ref").unwrap() {
Value::String(reference) => {
let mut url = super::storage::ref_to_url(&self.url, reference).unwrap();
let copy = url.clone();
let pointer = copy.fragment();
url.set_fragment(None);
let referenced_schema = storage.schemas.get(&url);
match referenced_schema {
Some(schema) => match pointer {
Some(p) => {
if let Some(s) = schema.get_body().pointer(p) {
scope.reference(p);
let result = f(s, scope);
scope.pop();
result
} else {
log::error!("Cannot resolve: {}", p);
f(node, scope)
}
}
None => f(schema.get_body(), scope),
},
None => {
log::error!("Cannot find schema: {}", url);
f(node, scope)
}
}
}
_ => {
log::error!("Invalid reference");
f(node, scope)
}
},
None => f(node, scope),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_when_file_and_spec_are_valid() {
let url = Url::parse(&format!(
"file://{}/{}",
env!("CARGO_MANIFEST_DIR"),
"resources/test/json-schemas/17-ref-multiple.json"
))
.unwrap();
println!("url: {:?}", url.path());
let url2 = Url::parse(&format!(
"file://{}/{}",
env!("CARGO_MANIFEST_DIR"),
"resources/test/json-schemas/18-shared-ref-17.json"
))
.unwrap();
let spec = Schema::load_url(url);
assert!(spec.is_ok());
let spec2 = Schema::load_url(url2);
assert!(spec2.is_ok());
}
}