xsd-parser 1.5.2

Rust code generator for XML schema files
Documentation
use std::borrow::Cow;

use proc_macro2::Literal;
use quote::format_ident;

use crate::{
    config::GeneratorFlags,
    models::{
        data::{ConstrainsData, EnumerationData, EnumerationDataVariant, EnumerationVariantValue},
        meta::{BuildInMeta, EnumerationMeta, EnumerationMetaVariant, MetaTypeVariant, MetaTypes},
        schema::xs::Use,
        Name, TypeIdent,
    },
    pipeline::generator::ValueGeneratorMode,
};

use super::super::{Context, Error};

impl<'types> EnumerationData<'types> {
    pub(super) fn new(
        meta: &'types EnumerationMeta,
        ctx: &mut Context<'_, 'types>,
    ) -> Result<Self, Error> {
        let mut unknown = 0usize;
        let constrains = ConstrainsData::new(&meta.constrains, None, ctx)?;
        let type_ident = ctx.current_type_ref().path.ident().clone();
        let trait_impls = ctx.make_trait_impls()?;

        let simple_base_type = ctx
            .check_generator_flags(GeneratorFlags::ADVANCED_ENUMS)
            .then_some(())
            .and_then(|()| meta.base.as_ident())
            .and_then(|base| ctx.types.get_simple_enum_base_type(base));

        let variants = meta
            .variants
            .iter()
            .filter_map(|var| var.make_variant(ctx, &mut unknown, simple_base_type.as_ref()))
            .collect::<Result<Vec<_>, _>>()?;

        let simple_base_type = simple_base_type
            .map(|base| ctx.get_or_create_type_ref_for_value(&base, true))
            .transpose()?
            .map(|x| x.path.clone());

        let meta = Cow::Borrowed(meta);

        Ok(EnumerationData {
            meta,
            constrains,
            type_ident,
            variants,
            trait_impls,
            simple_base_type,
        })
    }
}

impl EnumerationMetaVariant {
    fn make_variant<'types>(
        &'types self,
        ctx: &mut Context<'_, 'types>,
        unknown: &mut usize,
        simple_base: Option<&TypeIdent>,
    ) -> Option<Result<EnumerationDataVariant<'types>, Error>> {
        match self.use_ {
            Use::Prohibited => None,
            Use::Required | Use::Optional => {
                let types = ctx.types;
                let type_ref = if let Some(t) = &self.type_ {
                    match ctx.get_or_create_type_ref_for_value(t, true) {
                        Ok(target_ref) => Some(target_ref),
                        Err(error) => return Some(Err(error)),
                    }
                } else {
                    None
                };

                let variant_ident = if let Some(display_name) = self.display_name.as_deref() {
                    format_ident!("{display_name}")
                } else if let (Some(type_ref), true) = (type_ref, self.ident.name.is_generated()) {
                    type_ref.path.ident().clone()
                } else if self.ident.name.as_str().is_empty() {
                    *unknown += 1;

                    types.naming.make_unknown_variant(*unknown)
                } else {
                    types
                        .naming
                        .format_variant_ident(&self.ident.name, self.display_name.as_deref())
                };

                let s_name = self.ident.name.to_string();
                let b_name = Literal::byte_string(s_name.as_bytes());
                let target_type = type_ref.map(|x| x.path.clone());
                let extra_attributes = Vec::new();

                if let Some(namespaces) = &self.namespaces {
                    ctx.push_namespaces(namespaces.clone());
                }

                let value = self.ident.name.as_str();
                let value = simple_base
                    .and_then(|base| {
                        ctx.make_value_renderer(base, value, ValueGeneratorMode::Literal)
                            .ok()
                            .map(|code| {
                                let ident = types.naming.format_constant_ident(
                                    &Name::new_named(format!("VALUE_{variant_ident}")),
                                    None,
                                );

                                EnumerationVariantValue::ByteLiteral(ident, code)
                            })
                    })
                    .or_else(|| {
                        simple_base
                            .and_then(|base| {
                                ctx.make_value_renderer(base, value, ValueGeneratorMode::Constant)
                                    .ok()
                            })
                            .map(|code| {
                                let ident = types.naming.format_constant_ident(
                                    &Name::new_named(format!("VALUE_{variant_ident}")),
                                    None,
                                );

                                EnumerationVariantValue::Constant(ident, code)
                            })
                    })
                    .unwrap_or(EnumerationVariantValue::None);

                if self.namespaces.is_some() {
                    ctx.pop_namespaces();
                }

                let meta = Cow::Borrowed(self);

                Some(Ok(EnumerationDataVariant {
                    meta,
                    s_name,
                    b_name,
                    value,
                    variant_ident,
                    target_type,
                    extra_attributes,
                }))
            }
        }
    }
}

impl MetaTypes {
    fn get_simple_enum_base_type(&self, ident: &TypeIdent) -> Option<TypeIdent> {
        match self.get_variant(ident)? {
            MetaTypeVariant::BuildIn(BuildInMeta::String) => Some(TypeIdent::STR),
            MetaTypeVariant::BuildIn(_)
            | MetaTypeVariant::Custom(_)
            | MetaTypeVariant::SimpleType(_) => Some(ident.clone()),
            MetaTypeVariant::Enumeration(x) => {
                let base = x.base.as_ident()?;
                self.get_simple_enum_base_type(base)
            }
            _ => None,
        }
    }
}