use crate::ids::{
AttributeGroupKey, AttributeKey, ComplexTypeKey, ElementKey, ModelGroupKey, NameId,
SimpleTypeKey, TypeKey,
};
use crate::parser::location::SourceRef;
use crate::schema::model::{DerivationSet, XsdVersion};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum ContentKind {
#[default]
Empty,
Simple,
ElementOnly,
Mixed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DerivationMethod {
Restriction,
Extension,
}
#[derive(Debug, Clone)]
pub enum TypeRef {
Simple(SimpleTypeRef),
Complex(ComplexTypeRef),
Unresolved {
namespace: Option<NameId>,
local_name: NameId,
},
AnyType,
}
#[derive(Debug, Clone)]
pub enum SimpleTypeRef {
Resolved(SimpleTypeKey),
BuiltIn(super::simple::BuiltInType),
}
#[derive(Debug, Clone)]
pub enum ComplexTypeRef {
Resolved(ComplexTypeKey),
AnyType,
}
#[derive(Debug, Clone, Default)]
pub enum ComplexTypeContent {
#[default]
Empty,
Simple(SimpleContentDef),
Complex(Box<ComplexContentDef>),
}
#[derive(Debug, Clone)]
pub struct SimpleContentDef {
pub base_type: SimpleTypeRef,
pub derivation: DerivationMethod,
pub source: Option<SourceRef>,
}
#[derive(Debug, Clone)]
pub struct ComplexContentDef {
pub particle: Option<ContentParticle>,
pub derivation: DerivationMethod,
pub mixed: bool,
pub source: Option<SourceRef>,
pub open_content: Option<OpenContent>,
}
#[derive(Debug, Clone)]
pub struct ContentParticle {
pub term: ContentTerm,
pub min_occurs: u32,
pub max_occurs: Option<u32>,
pub source: Option<SourceRef>,
}
impl ContentParticle {
pub fn new(term: ContentTerm) -> Self {
Self {
term,
min_occurs: 1,
max_occurs: Some(1),
source: None,
}
}
pub fn with_occurs(term: ContentTerm, min: u32, max: Option<u32>) -> Self {
Self {
term,
min_occurs: min,
max_occurs: max,
source: None,
}
}
pub fn is_optional(&self) -> bool {
self.min_occurs == 0
}
pub fn is_repeating(&self) -> bool {
self.max_occurs.is_none_or(|max| max > 1)
}
pub fn is_unbounded(&self) -> bool {
self.max_occurs.is_none()
}
}
#[derive(Debug, Clone)]
pub enum ContentTerm {
Element(ElementRef),
Group(ModelGroupDef),
GroupRef(ModelGroupKey),
Wildcard(WildcardRef),
}
#[derive(Debug, Clone)]
pub enum ElementRef {
Local(ElementKey),
Global {
namespace: Option<NameId>,
local_name: NameId,
resolved: Option<ElementKey>,
},
}
#[derive(Debug, Clone)]
pub struct ModelGroupDef {
pub compositor: Compositor,
pub particles: Vec<ContentParticle>,
pub source: Option<SourceRef>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Compositor {
Sequence,
Choice,
All,
}
#[derive(Debug, Clone)]
pub struct WildcardRef {
pub namespace_constraint: NamespaceConstraint,
pub process_contents: ProcessContents,
pub not_qnames: Vec<(Option<NameId>, NameId)>,
pub has_defined_sibling: bool,
pub source: Option<SourceRef>,
}
#[derive(Debug, Clone, Default)]
pub enum NamespaceConstraint {
#[default]
Any,
Other,
TargetNamespace,
Local,
List(Vec<Option<NameId>>),
Not(Vec<Option<NameId>>),
}
impl NamespaceConstraint {
pub fn matches(
&self,
element_namespace: Option<NameId>,
target_namespace: Option<NameId>,
xsd_version: XsdVersion,
) -> bool {
match self {
NamespaceConstraint::Any => true,
NamespaceConstraint::Other => {
other_matches_namespace(element_namespace, target_namespace, xsd_version)
}
NamespaceConstraint::TargetNamespace => element_namespace == target_namespace,
NamespaceConstraint::Local => element_namespace.is_none(),
NamespaceConstraint::List(list) => list.contains(&element_namespace),
NamespaceConstraint::Not(excluded) => !excluded.contains(&element_namespace),
}
}
}
pub fn other_matches_namespace(
element_namespace: Option<NameId>,
target_namespace: Option<NameId>,
_xsd_version: XsdVersion,
) -> bool {
element_namespace.is_some() && element_namespace != target_namespace
}
pub fn not_qnames_exclude(
not_qnames: &[(Option<NameId>, NameId)],
namespace: Option<NameId>,
name: NameId,
) -> bool {
not_qnames
.iter()
.any(|&(ns, n)| ns == namespace && n == name)
}
pub use crate::schema::wildcard::ProcessContents;
#[derive(Debug, Clone)]
pub struct OpenContent {
pub mode: OpenContentMode,
pub wildcard: Option<WildcardRef>,
pub source: Option<SourceRef>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OpenContentMode {
None,
#[default]
Interleave,
Suffix,
}
impl From<crate::parser::frames::OpenContentMode> for OpenContentMode {
fn from(m: crate::parser::frames::OpenContentMode) -> Self {
use crate::parser::frames::OpenContentMode as Src;
match m {
Src::None => Self::None,
Src::Interleave => Self::Interleave,
Src::Suffix => Self::Suffix,
}
}
}
impl From<crate::schema::model::OpenContentMode> for OpenContentMode {
fn from(m: crate::schema::model::OpenContentMode) -> Self {
use crate::schema::model::OpenContentMode as Src;
match m {
Src::None => Self::None,
Src::Interleave => Self::Interleave,
Src::Suffix => Self::Suffix,
}
}
}
#[derive(Debug, Clone)]
pub struct AttributeUse {
pub attribute: AttributeRef,
pub use_kind: AttributeUseKind,
pub default_value: Option<String>,
pub fixed_value: Option<String>,
pub inheritable: bool,
pub source: Option<SourceRef>,
}
#[derive(Debug, Clone)]
pub enum AttributeRef {
Local(AttributeKey),
Global {
namespace: Option<NameId>,
local_name: NameId,
resolved: Option<AttributeKey>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AttributeUseKind {
Required,
#[default]
Optional,
Prohibited,
}
#[derive(Debug, Clone)]
pub struct AttributeWildcard {
pub namespace_constraint: NamespaceConstraint,
pub process_contents: ProcessContents,
pub source: Option<SourceRef>,
}
#[derive(Debug, Clone)]
pub struct ComplexTypeDef {
pub name: Option<NameId>,
pub target_namespace: Option<NameId>,
pub source: Option<SourceRef>,
pub base_type: Option<TypeRef>,
pub derivation_method: Option<DerivationMethod>,
pub content: ComplexTypeContent,
pub content_kind: ContentKind,
pub attributes: Vec<AttributeUse>,
pub attribute_groups: Vec<AttributeGroupKey>,
pub attribute_wildcard: Option<AttributeWildcard>,
pub final_derivation: DerivationSet,
pub block: DerivationSet,
pub is_abstract: bool,
pub mixed: bool,
pub id: Option<String>,
pub default_attributes_apply: bool,
}
impl ComplexTypeDef {
pub fn new(name: Option<NameId>, target_namespace: Option<NameId>) -> Self {
Self {
name,
target_namespace,
source: None,
base_type: None,
derivation_method: None,
content: ComplexTypeContent::Empty,
content_kind: ContentKind::Empty,
attributes: Vec::new(),
attribute_groups: Vec::new(),
attribute_wildcard: None,
final_derivation: DerivationSet::empty(),
block: DerivationSet::empty(),
is_abstract: false,
mixed: false,
id: None,
default_attributes_apply: true,
}
}
pub fn is_anonymous(&self) -> bool {
self.name.is_none()
}
pub fn is_global(&self) -> bool {
self.name.is_some()
}
pub fn has_simple_content(&self) -> bool {
matches!(self.content, ComplexTypeContent::Simple(_))
}
pub fn has_complex_content(&self) -> bool {
matches!(self.content, ComplexTypeContent::Complex(_))
}
pub fn allows_mixed(&self) -> bool {
self.mixed
}
pub fn type_key(&self, key: ComplexTypeKey) -> TypeKey {
TypeKey::Complex(key)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_complex_type_creation() {
let ct = ComplexTypeDef::new(Some(NameId(1)), Some(NameId(2)));
assert!(ct.is_global());
assert!(!ct.is_anonymous());
assert!(!ct.is_abstract);
assert_eq!(ct.content_kind, ContentKind::Empty);
}
#[test]
fn test_anonymous_complex_type() {
let ct = ComplexTypeDef::new(None, None);
assert!(ct.is_anonymous());
assert!(!ct.is_global());
}
#[test]
fn test_content_particle_default() {
let particle = ContentParticle::new(ContentTerm::Group(ModelGroupDef {
compositor: Compositor::Sequence,
particles: vec![],
source: None,
}));
assert_eq!(particle.min_occurs, 1);
assert_eq!(particle.max_occurs, Some(1));
assert!(!particle.is_optional());
assert!(!particle.is_repeating());
}
#[test]
fn test_content_particle_unbounded() {
let particle = ContentParticle::with_occurs(
ContentTerm::Group(ModelGroupDef {
compositor: Compositor::Sequence,
particles: vec![],
source: None,
}),
0,
None,
);
assert!(particle.is_optional());
assert!(particle.is_repeating());
assert!(particle.is_unbounded());
}
#[test]
fn test_compositor_types() {
assert_eq!(Compositor::Sequence, Compositor::Sequence);
assert_ne!(Compositor::Sequence, Compositor::Choice);
}
#[test]
fn test_attribute_use_kind() {
assert_eq!(AttributeUseKind::default(), AttributeUseKind::Optional);
}
#[test]
fn test_process_contents() {
assert_eq!(ProcessContents::default(), ProcessContents::Strict);
}
}