use std::collections::{HashMap, HashSet, VecDeque};
use crate::error::Result;
use crate::schema::fetcher::SchemaFetcher;
use super::super::parser::parse_xsd_ast;
use super::super::types::XsdSchema;
use super::common::resolve_uri;
pub struct SchemaResolver<'a, F: SchemaFetcher> {
fetcher: &'a F,
schemas: HashMap<String, XsdSchema>,
resolving: HashSet<String>,
}
impl<'a, F: SchemaFetcher> SchemaResolver<'a, F> {
pub fn new(fetcher: &'a F) -> Self {
Self {
fetcher,
schemas: HashMap::new(),
resolving: HashSet::new(),
}
}
pub fn resolve_all(&mut self, entry_content: &[u8], entry_uri: &str) -> Result<Vec<XsdSchema>> {
let entry_schema = parse_xsd_ast(entry_content)?;
self.schemas.insert(entry_uri.to_string(), entry_schema);
let mut queue: VecDeque<String> = VecDeque::new();
queue.push_back(entry_uri.to_string());
while let Some(current_uri) = queue.pop_front() {
if self.resolving.contains(¤t_uri) {
return Err(crate::schema::error::SchemaError::CircularDependency {
uri: current_uri,
}
.into());
}
self.resolving.insert(current_uri.clone());
let (imports, includes) = {
let schema = self.schemas.get(¤t_uri).ok_or_else(|| {
crate::schema::error::SchemaError::SchemaNotFound {
uri: current_uri.clone(),
}
})?;
(schema.imports.clone(), schema.includes.clone())
};
for import in imports {
if let Some(location) = &import.schema_location {
let resolved_uri = resolve_uri(¤t_uri, location)?;
if !self.schemas.contains_key(&resolved_uri) {
let content = self.fetch_schema(&resolved_uri)?;
let schema = parse_xsd_ast(&content)?;
self.schemas.insert(resolved_uri.clone(), schema);
queue.push_back(resolved_uri);
}
}
}
for include in includes {
let resolved_uri = resolve_uri(¤t_uri, &include.schema_location)?;
if !self.schemas.contains_key(&resolved_uri) {
let content = self.fetch_schema(&resolved_uri)?;
let schema = parse_xsd_ast(&content)?;
self.schemas.insert(resolved_uri.clone(), schema);
queue.push_back(resolved_uri);
}
}
self.resolving.remove(¤t_uri);
}
let mut result: Vec<XsdSchema> = Vec::new();
for (uri, schema) in &self.schemas {
if uri != entry_uri {
result.push(schema.clone());
}
}
if let Some(entry) = self.schemas.remove(entry_uri) {
result.push(entry);
}
Ok(result)
}
fn fetch_schema(&self, uri: &str) -> Result<Vec<u8>> {
let result = self.fetcher.fetch(uri)?;
Ok(result.content)
}
pub fn resolve_entry(&mut self, entry_content: &[u8], entry_uri: &str) -> Result<()> {
if self.schemas.contains_key(entry_uri) {
return Ok(());
}
let entry_schema = parse_xsd_ast(entry_content)?;
self.schemas.insert(entry_uri.to_string(), entry_schema);
let mut queue: VecDeque<String> = VecDeque::new();
queue.push_back(entry_uri.to_string());
while let Some(current_uri) = queue.pop_front() {
if self.resolving.contains(¤t_uri) {
return Err(crate::schema::error::SchemaError::CircularDependency {
uri: current_uri,
}
.into());
}
self.resolving.insert(current_uri.clone());
let (imports, includes) = {
let schema = self.schemas.get(¤t_uri).ok_or_else(|| {
crate::schema::error::SchemaError::SchemaNotFound {
uri: current_uri.clone(),
}
})?;
(schema.imports.clone(), schema.includes.clone())
};
for import in imports {
if let Some(location) = &import.schema_location {
let resolved_uri = resolve_uri(¤t_uri, location)?;
if !self.schemas.contains_key(&resolved_uri) {
let content = self.fetch_schema(&resolved_uri)?;
let schema = parse_xsd_ast(&content)?;
self.schemas.insert(resolved_uri.clone(), schema);
queue.push_back(resolved_uri);
}
}
}
for include in includes {
let resolved_uri = resolve_uri(¤t_uri, &include.schema_location)?;
if !self.schemas.contains_key(&resolved_uri) {
let content = self.fetch_schema(&resolved_uri)?;
let schema = parse_xsd_ast(&content)?;
self.schemas.insert(resolved_uri.clone(), schema);
queue.push_back(resolved_uri);
}
}
self.resolving.remove(¤t_uri);
}
Ok(())
}
pub fn take_all_schemas(self) -> Vec<XsdSchema> {
self.schemas.into_values().collect()
}
pub fn into_schemas(self) -> HashMap<String, XsdSchema> {
self.schemas
}
}