use std::borrow::Cow;
use std::ops::Deref;
use bitflags::bitflags;
use proc_macro2::{Ident as Ident2, Literal, TokenStream};
use crate::{
models::{
data::TagName,
meta::{AttributeMeta, ElementMeta},
schema::{MaxOccurs, MinOccurs},
TypeIdent,
},
pipeline::renderer::ValueRendererBox,
};
use super::{Occurs, PathData};
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum ComplexData<'types> {
Enum {
type_: ComplexDataEnum<'types>,
content_type: Option<Box<ComplexData<'types>>>,
},
Struct {
type_: ComplexDataStruct<'types>,
content_type: Option<Box<ComplexData<'types>>>,
},
}
#[derive(Debug)]
#[allow(clippy::struct_excessive_bools)]
pub struct ComplexBase<'types> {
pub flags: ComplexFlags,
pub type_ident: Ident2,
pub trait_impls: Vec<TokenStream>,
pub tag_name: Option<TagName<'types>>,
pub serializer_ident: Ident2,
pub serializer_state_ident: Ident2,
pub deserializer_ident: Ident2,
pub deserializer_state_ident: Ident2,
}
#[derive(Debug)]
pub struct ComplexDataEnum<'types> {
pub base: ComplexBase<'types>,
pub elements: Vec<ComplexDataElement<'types>>,
pub allow_any: bool,
pub allow_any_attribute: bool,
}
#[derive(Debug)]
pub struct ComplexDataStruct<'types> {
pub base: ComplexBase<'types>,
pub mode: StructMode<'types>,
pub attributes: Vec<ComplexDataAttribute<'types>>,
pub allow_any_attribute: bool,
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum StructMode<'types> {
Empty {
allow_any: bool,
},
Content {
content: ComplexDataContent<'types>,
},
All {
elements: Vec<ComplexDataElement<'types>>,
allow_any: bool,
},
Sequence {
elements: Vec<ComplexDataElement<'types>>,
allow_any: bool,
},
}
#[derive(Debug)]
pub struct ComplexDataContent<'types> {
pub occurs: Occurs,
pub simple_type: Option<&'types TypeIdent>,
pub min_occurs: MinOccurs,
pub max_occurs: MaxOccurs,
pub target_type: PathData,
pub extra_attributes: Vec<TokenStream>,
}
#[derive(Debug)]
pub struct ComplexDataElement<'types> {
pub origin: ComplexDataElementOrigin<'types>,
pub occurs: Occurs,
pub s_name: String,
pub b_name: Literal,
pub tag_name: TagName<'types>,
pub field_ident: Ident2,
pub variant_ident: Ident2,
pub target_type: PathData,
pub need_indirection: bool,
pub target_is_dynamic: bool,
pub extra_attributes: Vec<TokenStream>,
}
#[derive(Debug)]
pub enum ComplexDataElementOrigin<'types> {
Meta(&'types ElementMeta),
Generated(Box<ElementMeta>),
}
#[derive(Debug)]
pub struct ComplexDataAttribute<'types> {
pub meta: Cow<'types, AttributeMeta>,
pub ident: Ident2,
pub s_name: String,
pub b_name: Literal,
pub tag_name: TagName<'types>,
pub is_option: bool,
pub target_type: PathData,
pub default_value: Option<ValueRendererBox>,
pub extra_attributes: Vec<TokenStream>,
}
bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct ComplexFlags: u32 {
const HAS_ANY = 1 << 0;
const IS_MIXED = 1 << 1;
const IS_COMPLEX = 1 << 2;
const IS_DYNAMIC = 1 << 3;
const IS_CONTENT = 1 << 4;
}
}
impl<'types> ComplexBase<'types> {
#[must_use]
pub fn has_any(&self) -> bool {
self.flags.contains(ComplexFlags::HAS_ANY)
}
#[must_use]
pub fn is_mixed(&self) -> bool {
self.flags.contains(ComplexFlags::IS_MIXED)
}
#[must_use]
pub fn is_complex(&self) -> bool {
self.flags.contains(ComplexFlags::IS_COMPLEX)
}
#[must_use]
pub fn is_dynamic(&self) -> bool {
self.flags.contains(ComplexFlags::IS_DYNAMIC)
}
#[must_use]
pub fn is_content(&self) -> bool {
self.flags.contains(ComplexFlags::IS_CONTENT)
}
#[must_use]
pub fn element_tag(&self) -> Option<&TagName<'types>> {
(self.is_complex() && !self.is_dynamic())
.then_some(self.tag_name.as_ref())
.flatten()
}
#[must_use]
pub fn represents_element(&self) -> bool {
self.is_complex() && self.tag_name.is_some() && !self.is_dynamic()
}
}
impl<'types> Deref for ComplexDataEnum<'types> {
type Target = ComplexBase<'types>;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl<'types> ComplexDataStruct<'types> {
#[must_use]
pub fn is_unit_struct(&self) -> bool {
matches!(&self.mode, StructMode::Empty { .. }) && !self.has_attributes()
}
#[must_use]
pub fn has_attributes(&self) -> bool {
!self.attributes.is_empty()
}
#[must_use]
pub fn has_content(&self) -> bool {
match &self.mode {
StructMode::All { elements, .. } | StructMode::Sequence { elements, .. } => {
!elements.is_empty()
}
StructMode::Content { .. } => true,
StructMode::Empty { .. } => false,
}
}
#[must_use]
pub fn elements(&self) -> &[ComplexDataElement<'_>] {
if let StructMode::All { elements, .. } | StructMode::Sequence { elements, .. } = &self.mode
{
elements
} else {
&[]
}
}
#[must_use]
pub fn allow_any(&self) -> bool {
if let StructMode::Empty { allow_any }
| StructMode::All { allow_any, .. }
| StructMode::Sequence { allow_any, .. } = &self.mode
{
*allow_any
} else {
false
}
}
#[must_use]
pub fn content(&self) -> Option<&ComplexDataContent<'types>> {
if let StructMode::Content { content, .. } = &self.mode {
Some(content)
} else {
None
}
}
}
impl<'types> Deref for ComplexDataStruct<'types> {
type Target = ComplexBase<'types>;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl ComplexDataContent<'_> {
#[must_use]
pub fn is_simple(&self) -> bool {
self.simple_type.is_some()
}
#[must_use]
pub fn is_complex(&self) -> bool {
self.simple_type.is_none()
}
}
impl ComplexDataElement<'_> {
#[inline]
#[must_use]
pub fn meta(&self) -> &ElementMeta {
match &self.origin {
ComplexDataElementOrigin::Generated(meta) => meta,
ComplexDataElementOrigin::Meta(meta) => meta,
}
}
}
impl ComplexFlags {
#[must_use]
pub fn with(mut self, other: ComplexFlags, value: bool) -> Self {
self.set(other, value);
self
}
#[must_use]
pub fn extract(&mut self, other: ComplexFlags) -> Self {
let ret = self.intersection(other);
self.remove(other);
ret
}
}