xsd-parser 1.1.0

Rust code generator for XML schema files
Documentation
use std::collections::{btree_map::Entry, BTreeMap};

use crate::schema::xs::{
    AttributeGroupType, ComplexBaseType, ElementType, GroupType, Schema, SchemaContent,
    SimpleBaseType,
};
use crate::schema::{NamespaceId, Schemas};
use crate::types::{Ident, IdentType, Name, NameBuilder, Type, Types};

use super::Error;

#[derive(Default, Debug)]
pub(super) struct State<'a> {
    pub types: Types,
    pub node_cache: BTreeMap<Ident, Node<'a>>,
    pub type_stack: Vec<Option<Ident>>,
}

#[derive(Debug, Clone, Copy)]
pub(super) enum Node<'a> {
    Group(&'a GroupType),
    Element(&'a ElementType),
    SimpleType(&'a SimpleBaseType),
    ComplexType(&'a ComplexBaseType),
    AttributeGroup(&'a AttributeGroupType),
}

impl<'a> State<'a> {
    pub(super) fn current_ident(&self) -> Option<&Ident> {
        self.type_stack.iter().rev().flatten().next()
    }

    pub(super) fn current_ns(&self) -> Option<NamespaceId> {
        self.current_ident().and_then(|x| x.ns)
    }

    pub(super) fn last_named_type(&self, stop_at_group: bool) -> Option<&str> {
        for x in self.type_stack.iter().rev() {
            match x {
                Some(x) if x.name.is_named() => return Some(x.name.as_str()),
                None if stop_at_group => return None,
                _ => (),
            }
        }

        None
    }

    pub(super) fn name_builder(&mut self) -> NameBuilder {
        self.types.name_builder()
    }

    pub(super) fn make_content_name(&mut self) -> Name {
        self.name_builder()
            .auto_extend(false, true, self)
            .remove_suffix("Type")
            .remove_suffix("Content")
            .shared_name("Content")
            .finish()
    }

    pub(super) fn add_type<I, T>(
        &mut self,
        ident: I,
        type_: T,
        allow_overwrite: bool,
    ) -> Result<(), Error>
    where
        I: Into<Ident>,
        T: Into<Type>,
    {
        match self.types.entry(ident.into()) {
            Entry::Vacant(e) => {
                e.insert(type_.into());

                Ok(())
            }
            Entry::Occupied(mut e) if allow_overwrite => {
                e.insert(type_.into());

                Ok(())
            }
            Entry::Occupied(e) => Err(Error::TypeAlreadyDefined(e.key().clone())),
        }
    }

    pub(super) fn get_node(
        &mut self,
        schemas: &'a Schemas,
        schema: &'a Schema,
        ident: Ident,
    ) -> Option<Node<'a>> {
        match self.node_cache.entry(ident) {
            Entry::Occupied(e) => Some(*e.get()),
            Entry::Vacant(e) => {
                let node = search_in_schemas(schemas, schema, e.key())?;

                Some(*e.insert(node))
            }
        }
    }
}

fn search_in_schemas<'a>(
    schemas: &'a Schemas,
    schema: &'a Schema,
    ident: &Ident,
) -> Option<Node<'a>> {
    let name = ident.name.as_named_str()?;
    let type_ = ident.type_;

    if let Some(ns) = &ident.ns {
        let ns_info = schemas.get_namespace_info(ns)?;

        for id in &ns_info.schemas {
            if let Some(schema) = schemas.get_schema(id) {
                if let Some(node) = search_in_schema(schema, name, type_) {
                    return Some(node);
                }
            }
        }

        None
    } else {
        search_in_schema(schema, name, type_)
    }
}

fn search_in_schema<'a>(schema: &'a Schema, name: &str, type_: IdentType) -> Option<Node<'a>> {
    for c in &schema.content {
        match (type_, c) {
            (IdentType::Element, SchemaContent::Element(x)) if matches!(&x.name, Some(n) if n == name) =>
            {
                return Some(Node::Element(x));
            }
            (IdentType::Type, SchemaContent::SimpleType(x)) if matches!(&x.name, Some(n) if n == name) =>
            {
                return Some(Node::SimpleType(x));
            }
            (IdentType::Type, SchemaContent::ComplexType(x)) if matches!(&x.name, Some(n) if n == name) =>
            {
                return Some(Node::ComplexType(x));
            }
            (IdentType::Group, SchemaContent::Group(x)) if matches!(&x.name, Some(n) if n == name) =>
            {
                return Some(Node::Group(x));
            }
            (IdentType::AttributeGroup, SchemaContent::AttributeGroup(x)) if matches!(&x.name, Some(n) if n == name) =>
            {
                return Some(Node::AttributeGroup(x));
            }
            (_, _) => (),
        }
    }

    None
}