xsd-parser 1.5.2

Rust code generator for XML schema files
Documentation
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> {
    /// The schemas that are processed by the interpreter.
    schemas: &'a Schemas,

    /// The types that are generated by the interpreter.
    types: MetaTypes,

    /// Cache for the nodes that are defined in the schemas. This is used to quickly access the nodes by their identifier.
    node_cache: NodeCache<'a>,

    /// Cache for the identifiers that are defined in the schemas. This is used to quickly resolve type identifiers.
    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 {
    /// A strong dependency means that the referenced type needs to
    /// be generated before the dependent type can be generated.
    Strong(TypeIdent),

    /// A lazy dependency means that the referenced type is always re-evaluated
    /// during generation. It may contain strong dependencies.
    Lazy(TypeIdent),

    /// A weak dependency is just a runtime reference. The generation
    /// of the types is not affected by weak dependencies.
    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),
        }
    }

    /// This method is used to patch types that were defined by the user
    /// using the config that type identifiers are maybe not fully qualified.
    /// The unqualified identifiers are resolved by this method. For type
    /// generated by the interpreter this is usually not necessary.
    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)))
}