mod crate_ident_cache;
mod crate_node_cache;
mod create_node_list;
mod fix_element_naming_conflicts;
mod generate_types;
mod prepare_modules;
mod prepare_schemas;
use std::collections::btree_map::{BTreeMap, Entry};
use std::str::from_utf8;
use indexmap::IndexMap;
use xsd_parser_types::{misc::RawByteStr, xml::QName};
use crate::models::meta::{ElementMetaVariant, MetaType, MetaTypeVariant, MetaTypes};
use crate::models::schema::xs::{
AttributeGroupType, AttributeType, ComplexBaseType, ElementType, GroupType, SimpleBaseType,
};
use crate::models::schema::{NamespaceId, Schemas};
use crate::models::{IdentCache, IdentType, Name, TypeIdent};
use crate::traits::{NameBuilder, Naming};
use super::Error;
#[derive(Debug)]
pub(super) struct State<'a> {
schemas: &'a Schemas,
types: MetaTypes,
node_cache: NodeCache<'a>,
ident_cache: IdentCache,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub(super) enum Node<'a> {
Group(&'a GroupType),
Element(&'a ElementType),
Attribute(&'a AttributeType),
SimpleType(&'a SimpleBaseType),
ComplexType(&'a ComplexBaseType),
AttributeGroup(&'a AttributeGroupType),
}
#[derive(Debug, Clone)]
struct NodeCacheEntry<'schema> {
node: Node<'schema>,
dependencies: BTreeMap<NodeDependencyKey, NodeDependency>,
redefine_base: Option<TypeIdent>,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
enum NodeDependencyKey {
Named(NamespaceId, IdentType, Name),
InlineElement(*const ElementType),
InlineSimpleType(*const SimpleBaseType),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum NodeDependency {
Strong(TypeIdent),
Lazy(TypeIdent),
Weak(TypeIdent),
}
type NodeCache<'s> = IndexMap<TypeIdent, NodeCacheEntry<'s>>;
impl<'a> State<'a> {
pub(super) fn new(schemas: &'a Schemas) -> Self {
let mut ret = Self {
schemas,
types: MetaTypes::default(),
node_cache: NodeCache::default(),
ident_cache: IdentCache::default(),
};
ret.create_ident_cache();
ret
}
pub(super) fn finish(mut self) -> Result<(MetaTypes, IdentCache), Error> {
self.create_node_cache()?;
self.prepare_modules();
self.prepare_schemas();
let nodes = self.create_node_list()?;
self.generate_types(nodes)?;
self.fix_element_naming_conflicts();
Ok((self.types, self.ident_cache))
}
pub(super) fn schemas(&self) -> &'a Schemas {
self.schemas
}
pub(super) fn set_naming(&mut self, naming: Box<dyn Naming>) {
self.types.naming = naming;
}
pub(super) fn name_builder(&self) -> Box<dyn NameBuilder> {
self.types.name_builder()
}
pub(super) fn add_type<I, T>(&mut self, ident: I, type_: T) -> Result<(), Error>
where
I: Into<TypeIdent>,
T: Into<MetaType>,
{
let mut type_ = type_.into();
let ident = ident.into();
let ident = self.resolve_type_ident_allow_unknown(ident)?;
self.resolve_type_idents(&mut type_)?;
let entry = self.types.items.entry(ident);
let ident = entry.key().clone();
self.ident_cache.insert(ident);
match entry {
Entry::Vacant(e) => {
e.insert(type_);
}
Entry::Occupied(mut e) => {
e.insert(type_);
}
}
Ok(())
}
pub(super) fn resolve_type_ident(&self, ident: TypeIdent) -> Result<TypeIdent, Error> {
self.ident_cache.resolve(ident)
}
pub(super) fn resolve_type_ident_allow_unknown(
&self,
ident: TypeIdent,
) -> Result<TypeIdent, Error> {
match self.resolve_type_ident(ident) {
Ok(ident) => Ok(ident),
Err(Error::UnknownType(ident)) => Ok(ident),
Err(error) => Err(error),
}
}
fn resolve_type_idents(&self, type_: &mut MetaType) -> Result<(), Error> {
match &mut type_.variant {
MetaTypeVariant::Union(x) => {
for ty in &mut *x.types {
ty.type_ = self.resolve_type_ident_allow_unknown(ty.type_.clone())?;
}
}
MetaTypeVariant::Enumeration(x) => {
for var in &mut *x.variants {
if let Some(ty) = &mut var.type_ {
*ty = self.resolve_type_ident_allow_unknown(ty.clone())?;
}
}
}
MetaTypeVariant::Dynamic(x) => {
if let Some(ty) = &mut x.type_ {
*ty = self.resolve_type_ident_allow_unknown(ty.clone())?;
}
for meta in &mut x.derived_types {
meta.type_ = self.resolve_type_ident_allow_unknown(meta.type_.clone())?;
}
}
MetaTypeVariant::Reference(x) => {
x.type_ = self.resolve_type_ident_allow_unknown(x.type_.clone())?;
}
MetaTypeVariant::All(x) | MetaTypeVariant::Choice(x) | MetaTypeVariant::Sequence(x) => {
for el in &mut *x.elements {
if let ElementMetaVariant::Type { type_, .. } = &mut el.variant {
*type_ = self.resolve_type_ident_allow_unknown(type_.clone())?;
}
}
}
MetaTypeVariant::ComplexType(x) => {
if let Some(ty) = &mut x.content {
*ty = self.resolve_type_ident_allow_unknown(ty.clone())?;
}
}
MetaTypeVariant::SimpleType(x) => {
x.base = self.resolve_type_ident_allow_unknown(x.base.clone())?;
}
_ => (),
}
Ok(())
}
}
fn parse_qname(
name: &QName,
ns: NamespaceId,
schemas: &Schemas,
) -> Result<(NamespaceId, Name), Error> {
let ns = name
.namespace()
.map(|ns| {
let ns = Some(ns.clone());
#[allow(clippy::unnecessary_literal_unwrap)]
schemas
.resolve_namespace(&ns)
.ok_or_else(|| Error::UnknownNamespace(ns.unwrap()))
})
.transpose()?
.unwrap_or(ns);
let name = name.local_name();
let name =
from_utf8(name).map_err(|_| Error::InvalidLocalName(RawByteStr::from_slice(name)))?;
let name = name.to_owned();
Ok((ns, Name::new_named(name)))
}