use std::collections::BTreeMap;
use xsd_parser_types::misc::Namespace;
use crate::models::{
schema::{NamespaceId, SchemaId},
Name, Naming, TypeIdent,
};
use crate::traits::{NameBuilder as NameBuilderTrait, Naming as NamingTrait};
use super::{MetaType, MetaTypeVariant};
#[derive(Debug)]
pub struct MetaTypes {
pub items: BTreeMap<TypeIdent, MetaType>,
pub modules: BTreeMap<NamespaceId, ModuleMeta>,
pub schemas: BTreeMap<SchemaId, SchemaMeta>,
pub naming: Box<dyn NamingTrait>,
}
#[derive(Debug)]
pub struct ModuleMeta {
pub name: Option<Name>,
pub prefix: Option<Name>,
pub namespace: Option<Namespace>,
pub namespace_id: NamespaceId,
pub schema_count: usize,
}
#[derive(Debug)]
pub struct SchemaMeta {
pub name: Option<Name>,
pub namespace: NamespaceId,
}
impl MetaTypes {
#[must_use]
pub fn name_builder(&self) -> Box<dyn NameBuilderTrait> {
self.naming.builder()
}
#[must_use]
pub fn get_resolved<'a>(
&'a self,
ident: &'a TypeIdent,
) -> Option<(&'a TypeIdent, &'a MetaType)> {
let mut visit = Vec::new();
get_resolved_impl(self, ident, &mut visit)
}
#[must_use]
pub fn get_resolved_type<'a>(&'a self, ident: &'a TypeIdent) -> Option<&'a MetaType> {
self.get_resolved(ident).map(|(_ident, ty)| ty)
}
#[must_use]
pub fn get_resolved_ident<'a>(&'a self, ident: &'a TypeIdent) -> Option<&'a TypeIdent> {
self.get_resolved(ident).map(|(ident, _ty)| ident)
}
#[inline]
#[must_use]
pub fn get_variant(&self, ident: &TypeIdent) -> Option<&MetaTypeVariant> {
self.items.get(ident).map(|ty| &ty.variant)
}
#[inline]
#[must_use]
pub fn get_variant_mut(&mut self, ident: &TypeIdent) -> Option<&mut MetaTypeVariant> {
self.items.get_mut(ident).map(|ty| &mut ty.variant)
}
}
impl Default for MetaTypes {
fn default() -> Self {
Self {
items: Default::default(),
modules: Default::default(),
schemas: Default::default(),
naming: Box::new(Naming::default()),
}
}
}
impl ModuleMeta {
#[must_use]
pub fn name(&self) -> Option<&Name> {
self.name.as_ref().or(self.prefix.as_ref())
}
#[must_use]
pub fn prefix(&self) -> Option<&Name> {
self.prefix.as_ref().or(self.name.as_ref())
}
}
fn get_resolved_impl<'a>(
types: &'a MetaTypes,
ident: &'a TypeIdent,
visited: &mut Vec<&'a TypeIdent>,
) -> Option<(&'a TypeIdent, &'a MetaType)> {
if visited.contains(&ident) {
let chain = visited
.iter()
.map(ToString::to_string)
.chain(Some(ident.to_string()))
.collect::<Vec<_>>()
.join(" >> ");
tracing::debug!("Detected type reference loop: {chain}");
return None;
}
let ty = types.items.get(ident)?;
match &ty.variant {
MetaTypeVariant::Reference(x) if x.is_simple() => {
visited.push(ident);
let ret = match get_resolved_impl(types, &x.type_, visited) {
None => Some((ident, ty)),
Some((ident, ty)) => Some((ident, ty)),
};
visited.pop();
ret
}
_ => Some((ident, ty)),
}
}