1use crate::ids::{
7 AttributeGroupKey, AttributeKey, ComplexTypeKey, ElementKey, ModelGroupKey, NameId,
8 SimpleTypeKey, TypeKey,
9};
10use crate::parser::location::SourceRef;
11use crate::schema::model::{DerivationSet, XsdVersion};
12
13#[derive(Debug, Clone, PartialEq, Eq, Default)]
15pub enum ContentKind {
16 #[default]
18 Empty,
19 Simple,
21 ElementOnly,
23 Mixed,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum DerivationMethod {
30 Restriction,
32 Extension,
34}
35
36#[derive(Debug, Clone)]
38pub enum TypeRef {
39 Simple(SimpleTypeRef),
41 Complex(ComplexTypeRef),
43 Unresolved {
45 namespace: Option<NameId>,
46 local_name: NameId,
47 },
48 AnyType,
50}
51
52#[derive(Debug, Clone)]
54pub enum SimpleTypeRef {
55 Resolved(SimpleTypeKey),
57 BuiltIn(super::simple::BuiltInType),
59}
60
61#[derive(Debug, Clone)]
63pub enum ComplexTypeRef {
64 Resolved(ComplexTypeKey),
66 AnyType,
68}
69
70#[derive(Debug, Clone, Default)]
72pub enum ComplexTypeContent {
73 #[default]
75 Empty,
76
77 Simple(SimpleContentDef),
79
80 Complex(Box<ComplexContentDef>),
82}
83
84#[derive(Debug, Clone)]
86pub struct SimpleContentDef {
87 pub base_type: SimpleTypeRef,
89
90 pub derivation: DerivationMethod,
92
93 pub source: Option<SourceRef>,
95}
96
97#[derive(Debug, Clone)]
99pub struct ComplexContentDef {
100 pub particle: Option<ContentParticle>,
102
103 pub derivation: DerivationMethod,
105
106 pub mixed: bool,
108
109 pub source: Option<SourceRef>,
111
112 pub open_content: Option<OpenContent>,
114}
115
116#[derive(Debug, Clone)]
118pub struct ContentParticle {
119 pub term: ContentTerm,
121
122 pub min_occurs: u32,
124
125 pub max_occurs: Option<u32>,
127
128 pub source: Option<SourceRef>,
130}
131
132impl ContentParticle {
133 pub fn new(term: ContentTerm) -> Self {
135 Self {
136 term,
137 min_occurs: 1,
138 max_occurs: Some(1),
139 source: None,
140 }
141 }
142
143 pub fn with_occurs(term: ContentTerm, min: u32, max: Option<u32>) -> Self {
145 Self {
146 term,
147 min_occurs: min,
148 max_occurs: max,
149 source: None,
150 }
151 }
152
153 pub fn is_optional(&self) -> bool {
155 self.min_occurs == 0
156 }
157
158 pub fn is_repeating(&self) -> bool {
160 self.max_occurs.is_none_or(|max| max > 1)
161 }
162
163 pub fn is_unbounded(&self) -> bool {
165 self.max_occurs.is_none()
166 }
167}
168
169#[derive(Debug, Clone)]
171pub enum ContentTerm {
172 Element(ElementRef),
174
175 Group(ModelGroupDef),
177
178 GroupRef(ModelGroupKey),
180
181 Wildcard(WildcardRef),
183}
184
185#[derive(Debug, Clone)]
187pub enum ElementRef {
188 Local(ElementKey),
190 Global {
192 namespace: Option<NameId>,
193 local_name: NameId,
194 resolved: Option<ElementKey>,
195 },
196}
197
198#[derive(Debug, Clone)]
200pub struct ModelGroupDef {
201 pub compositor: Compositor,
203
204 pub particles: Vec<ContentParticle>,
206
207 pub source: Option<SourceRef>,
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
213pub enum Compositor {
214 Sequence,
216 Choice,
218 All,
220}
221
222#[derive(Debug, Clone)]
224pub struct WildcardRef {
225 pub namespace_constraint: NamespaceConstraint,
227
228 pub process_contents: ProcessContents,
230
231 pub not_qnames: Vec<(Option<NameId>, NameId)>,
233
234 pub has_defined_sibling: bool,
238
239 pub source: Option<SourceRef>,
241}
242
243#[derive(Debug, Clone, Default)]
245pub enum NamespaceConstraint {
246 #[default]
248 Any,
249 Other,
251 TargetNamespace,
253 Local,
255 List(Vec<Option<NameId>>),
257 Not(Vec<Option<NameId>>),
259}
260
261impl NamespaceConstraint {
262 pub fn matches(
264 &self,
265 element_namespace: Option<NameId>,
266 target_namespace: Option<NameId>,
267 xsd_version: XsdVersion,
268 ) -> bool {
269 match self {
270 NamespaceConstraint::Any => true,
271 NamespaceConstraint::Other => {
272 other_matches_namespace(element_namespace, target_namespace, xsd_version)
273 }
274 NamespaceConstraint::TargetNamespace => element_namespace == target_namespace,
275 NamespaceConstraint::Local => element_namespace.is_none(),
276 NamespaceConstraint::List(list) => list.contains(&element_namespace),
277 NamespaceConstraint::Not(excluded) => !excluded.contains(&element_namespace),
278 }
279 }
280}
281
282pub fn other_matches_namespace(
291 element_namespace: Option<NameId>,
292 target_namespace: Option<NameId>,
293 _xsd_version: XsdVersion,
294) -> bool {
295 element_namespace.is_some() && element_namespace != target_namespace
296}
297
298pub fn not_qnames_exclude(
300 not_qnames: &[(Option<NameId>, NameId)],
301 namespace: Option<NameId>,
302 name: NameId,
303) -> bool {
304 not_qnames
305 .iter()
306 .any(|&(ns, n)| ns == namespace && n == name)
307}
308
309pub use crate::schema::wildcard::ProcessContents;
311
312#[derive(Debug, Clone)]
317pub struct OpenContent {
318 pub mode: OpenContentMode,
320
321 pub wildcard: Option<WildcardRef>,
323
324 pub source: Option<SourceRef>,
326}
327
328#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
330pub enum OpenContentMode {
331 None,
333 #[default]
335 Interleave,
336 Suffix,
338}
339
340impl From<crate::parser::frames::OpenContentMode> for OpenContentMode {
341 fn from(m: crate::parser::frames::OpenContentMode) -> Self {
342 use crate::parser::frames::OpenContentMode as Src;
343 match m {
344 Src::None => Self::None,
345 Src::Interleave => Self::Interleave,
346 Src::Suffix => Self::Suffix,
347 }
348 }
349}
350
351impl From<crate::schema::model::OpenContentMode> for OpenContentMode {
352 fn from(m: crate::schema::model::OpenContentMode) -> Self {
353 use crate::schema::model::OpenContentMode as Src;
354 match m {
355 Src::None => Self::None,
356 Src::Interleave => Self::Interleave,
357 Src::Suffix => Self::Suffix,
358 }
359 }
360}
361
362#[derive(Debug, Clone)]
364pub struct AttributeUse {
365 pub attribute: AttributeRef,
367
368 pub use_kind: AttributeUseKind,
370
371 pub default_value: Option<String>,
373
374 pub fixed_value: Option<String>,
376
377 pub inheritable: bool,
379
380 pub source: Option<SourceRef>,
382}
383
384#[derive(Debug, Clone)]
386pub enum AttributeRef {
387 Local(AttributeKey),
389 Global {
391 namespace: Option<NameId>,
392 local_name: NameId,
393 resolved: Option<AttributeKey>,
394 },
395}
396
397#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
399pub enum AttributeUseKind {
400 Required,
402 #[default]
404 Optional,
405 Prohibited,
407}
408
409#[derive(Debug, Clone)]
411pub struct AttributeWildcard {
412 pub namespace_constraint: NamespaceConstraint,
414
415 pub process_contents: ProcessContents,
417
418 pub source: Option<SourceRef>,
420}
421
422#[derive(Debug, Clone)]
426pub struct ComplexTypeDef {
427 pub name: Option<NameId>,
429
430 pub target_namespace: Option<NameId>,
432
433 pub source: Option<SourceRef>,
435
436 pub base_type: Option<TypeRef>,
438
439 pub derivation_method: Option<DerivationMethod>,
441
442 pub content: ComplexTypeContent,
444
445 pub content_kind: ContentKind,
447
448 pub attributes: Vec<AttributeUse>,
450
451 pub attribute_groups: Vec<AttributeGroupKey>,
453
454 pub attribute_wildcard: Option<AttributeWildcard>,
456
457 pub final_derivation: DerivationSet,
459
460 pub block: DerivationSet,
462
463 pub is_abstract: bool,
465
466 pub mixed: bool,
468
469 pub id: Option<String>,
471
472 pub default_attributes_apply: bool,
476}
477
478impl ComplexTypeDef {
479 pub fn new(name: Option<NameId>, target_namespace: Option<NameId>) -> Self {
481 Self {
482 name,
483 target_namespace,
484 source: None,
485 base_type: None,
486 derivation_method: None,
487 content: ComplexTypeContent::Empty,
488 content_kind: ContentKind::Empty,
489 attributes: Vec::new(),
490 attribute_groups: Vec::new(),
491 attribute_wildcard: None,
492 final_derivation: DerivationSet::empty(),
493 block: DerivationSet::empty(),
494 is_abstract: false,
495 mixed: false,
496 id: None,
497 default_attributes_apply: true,
498 }
499 }
500
501 pub fn is_anonymous(&self) -> bool {
503 self.name.is_none()
504 }
505
506 pub fn is_global(&self) -> bool {
508 self.name.is_some()
509 }
510
511 pub fn has_simple_content(&self) -> bool {
513 matches!(self.content, ComplexTypeContent::Simple(_))
514 }
515
516 pub fn has_complex_content(&self) -> bool {
518 matches!(self.content, ComplexTypeContent::Complex(_))
519 }
520
521 pub fn allows_mixed(&self) -> bool {
523 self.mixed
524 }
525
526 pub fn type_key(&self, key: ComplexTypeKey) -> TypeKey {
528 TypeKey::Complex(key)
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 #[test]
537 fn test_complex_type_creation() {
538 let ct = ComplexTypeDef::new(Some(NameId(1)), Some(NameId(2)));
539
540 assert!(ct.is_global());
541 assert!(!ct.is_anonymous());
542 assert!(!ct.is_abstract);
543 assert_eq!(ct.content_kind, ContentKind::Empty);
544 }
545
546 #[test]
547 fn test_anonymous_complex_type() {
548 let ct = ComplexTypeDef::new(None, None);
549 assert!(ct.is_anonymous());
550 assert!(!ct.is_global());
551 }
552
553 #[test]
554 fn test_content_particle_default() {
555 let particle = ContentParticle::new(ContentTerm::Group(ModelGroupDef {
556 compositor: Compositor::Sequence,
557 particles: vec![],
558 source: None,
559 }));
560
561 assert_eq!(particle.min_occurs, 1);
562 assert_eq!(particle.max_occurs, Some(1));
563 assert!(!particle.is_optional());
564 assert!(!particle.is_repeating());
565 }
566
567 #[test]
568 fn test_content_particle_unbounded() {
569 let particle = ContentParticle::with_occurs(
570 ContentTerm::Group(ModelGroupDef {
571 compositor: Compositor::Sequence,
572 particles: vec![],
573 source: None,
574 }),
575 0,
576 None,
577 );
578
579 assert!(particle.is_optional());
580 assert!(particle.is_repeating());
581 assert!(particle.is_unbounded());
582 }
583
584 #[test]
585 fn test_compositor_types() {
586 assert_eq!(Compositor::Sequence, Compositor::Sequence);
587 assert_ne!(Compositor::Sequence, Compositor::Choice);
588 }
589
590 #[test]
591 fn test_attribute_use_kind() {
592 assert_eq!(AttributeUseKind::default(), AttributeUseKind::Optional);
593 }
594
595 #[test]
596 fn test_process_contents() {
597 assert_eq!(ProcessContents::default(), ProcessContents::Strict);
598 }
599}