Skip to main content

xsd_schema/types/
mod.rs

1//! XSD type definitions and facets
2//!
3//! This module contains type definitions, facets, and the type system.
4//!
5//! ## Module Structure
6//!
7//! - `facets` - Constraining facets (length, pattern, enumeration, etc.)
8//! - `simple` - Simple type definitions (atomic, list, union)
9//! - `complex` - Complex type definitions with content models
10//!
11//! ## Type Code Enums
12//!
13//! The module provides three core enums for type identification:
14//!
15//! - `XmlTypeCode` - Complete type codes matching .NET XmlTypeCode (60+ codes)
16//! - `PrimitiveTypeCode` - The 19 primitive XSD types for validator dispatch
17//! - `ValueKind` - Runtime value kind for type discrimination
18
19pub mod builtin;
20pub mod complex;
21pub mod convert;
22pub mod equality;
23pub mod facets;
24#[cfg(feature = "xsd11")]
25pub mod sequence;
26pub mod simple;
27pub mod validators;
28pub mod value;
29
30// Re-exports
31pub use builtin::BuiltinTypes;
32pub use complex::{
33    AttributeUse, AttributeUseKind, AttributeWildcard, ComplexTypeContent, ComplexTypeDef,
34    Compositor, ContentKind, ContentParticle, ContentTerm, DerivationMethod, ModelGroupDef,
35    NamespaceConstraint, ProcessContents,
36};
37pub use convert::{ConversionError, ConversionResult, IntoXmlValue, TypeConverter};
38pub use facets::{
39    facet_applicable, facet_applicable_for_type, normalize_whitespace, AssertionFacet,
40    EnumerationFacet, ExplicitTimezone, ExplicitTimezoneFacet, FacetApplicability, FacetFixed,
41    FacetKind, FacetSet, FractionDigitsFacet, LengthFacet, MaxExclusiveFacet, MaxInclusiveFacet,
42    MaxLengthFacet, MinExclusiveFacet, MinInclusiveFacet, MinLengthFacet, PatternFacet,
43    TotalDigitsFacet, WhitespaceFacet, WhitespaceMode,
44};
45#[cfg(feature = "xsd11")]
46pub use sequence::{
47    resolve_list_item_schema_type, ItemType, NameTest, SequenceType, XmlTypeCardinality,
48};
49pub use simple::{
50    BuiltInType, SimpleTypeDef, SimpleTypeDerivationMethod, SimpleTypeRef, SimpleTypeVariety,
51};
52pub use validators::{
53    TypeValidator, ValidationError, ValidationResult, ValidatorRegistry, VALIDATOR_REGISTRY,
54};
55pub use value::{
56    DateTimeValue, DateValue, DayTimeDurationValue, DurationValue, GDayValue, GMonthDayValue,
57    GMonthValue, GYearMonthValue, GYearValue, TimeValue, TimezoneOffset, XmlAtomicValue, XmlValue,
58    XmlValueKind, YearMonthDurationValue,
59};
60
61// ============================================================================
62// XmlTypeCode - Complete type codes matching .NET XmlTypeCode
63// ============================================================================
64
65/// XSD type codes for type identification and dispatch.
66///
67/// Ordered to match .NET `XmlTypeCode` for interoperability.
68/// See XSD_TYPE_DESIGN.md §3.2 for full specification.
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
70#[repr(u8)]
71#[derive(Default)]
72pub enum XmlTypeCode {
73    // Special types (0-9)
74    /// No type information
75    #[default]
76    None = 0,
77    /// Any item (XPath2)
78    Item = 1,
79    /// Any node
80    Node = 2,
81    /// Document node
82    Document = 3,
83    /// Element node
84    Element = 4,
85    /// Attribute node
86    Attribute = 5,
87    /// Namespace node
88    Namespace = 6,
89    /// Processing instruction node
90    ProcessingInstruction = 7,
91    /// Comment node
92    Comment = 8,
93    /// Text node
94    Text = 9,
95
96    // Schema abstract types (10-13)
97    /// xs:anyType - the ur-type
98    AnyType = 10,
99    /// xs:anySimpleType - base of all simple types
100    AnySimpleType = 11,
101    /// xs:anyAtomicType - base of all atomic types (XSD 1.1)
102    AnyAtomicType = 12,
103    /// xs:untypedAtomic - untyped atomic value
104    UntypedAtomic = 13,
105
106    // String types (14-23)
107    /// xs:string
108    String = 14,
109    /// xs:normalizedString
110    NormalizedString = 15,
111    /// xs:token
112    Token = 16,
113    /// xs:language
114    Language = 17,
115    /// xs:NMTOKEN
116    NmToken = 18,
117    /// xs:Name
118    Name = 19,
119    /// xs:NCName
120    NCName = 20,
121    /// xs:ID
122    Id = 21,
123    /// xs:IDREF
124    IdRef = 22,
125    /// xs:ENTITY
126    Entity = 23,
127
128    // Numeric types (24-40)
129    /// xs:boolean
130    Boolean = 24,
131    /// xs:decimal
132    Decimal = 25,
133    /// xs:float
134    Float = 26,
135    /// xs:double
136    Double = 27,
137    /// xs:integer
138    Integer = 28,
139    /// xs:nonPositiveInteger
140    NonPositiveInteger = 29,
141    /// xs:negativeInteger
142    NegativeInteger = 30,
143    /// xs:long
144    Long = 31,
145    /// xs:int
146    Int = 32,
147    /// xs:short
148    Short = 33,
149    /// xs:byte
150    Byte = 34,
151    /// xs:nonNegativeInteger
152    NonNegativeInteger = 35,
153    /// xs:unsignedLong
154    UnsignedLong = 36,
155    /// xs:unsignedInt
156    UnsignedInt = 37,
157    /// xs:unsignedShort
158    UnsignedShort = 38,
159    /// xs:unsignedByte
160    UnsignedByte = 39,
161    /// xs:positiveInteger
162    PositiveInteger = 40,
163
164    // Date/time types (41-52)
165    /// xs:duration
166    Duration = 41,
167    /// xs:dateTime
168    DateTime = 42,
169    /// xs:time
170    Time = 43,
171    /// xs:date
172    Date = 44,
173    /// xs:gYearMonth
174    GYearMonth = 45,
175    /// xs:gYear
176    GYear = 46,
177    /// xs:gMonthDay
178    GMonthDay = 47,
179    /// xs:gDay
180    GDay = 48,
181    /// xs:gMonth
182    GMonth = 49,
183    /// xs:yearMonthDuration (XSD 1.1)
184    YearMonthDuration = 50,
185    /// xs:dayTimeDuration (XSD 1.1)
186    DayTimeDuration = 51,
187    /// xs:dateTimeStamp (XSD 1.1)
188    DateTimeStamp = 52,
189
190    // Binary types (53-54)
191    /// xs:hexBinary
192    HexBinary = 53,
193    /// xs:base64Binary
194    Base64Binary = 54,
195
196    // Other types (55-57)
197    /// xs:anyURI
198    AnyUri = 55,
199    /// xs:QName
200    QName = 56,
201    /// xs:NOTATION
202    Notation = 57,
203
204    // List types (58-60)
205    /// xs:NMTOKENS (list of NMTOKEN)
206    NmTokens = 58,
207    /// xs:IDREFS (list of IDREF)
208    IdRefs = 59,
209    /// xs:ENTITIES (list of ENTITY)
210    Entities = 60,
211
212    // XSD 1.1 bottom type (61)
213    /// xs:error - the bottom type (union of no members); has no valid values (XSD 1.1)
214    Error = 61,
215}
216
217impl XmlTypeCode {
218    /// Returns true if this is a node type code.
219    #[inline]
220    pub fn is_node(&self) -> bool {
221        matches!(
222            self,
223            Self::Node
224                | Self::Document
225                | Self::Element
226                | Self::Attribute
227                | Self::Namespace
228                | Self::ProcessingInstruction
229                | Self::Comment
230                | Self::Text
231        )
232    }
233
234    /// Returns true if this is an atomic type (not node, not list, not ur-type).
235    #[inline]
236    pub fn is_atomic(&self) -> bool {
237        (*self as u8) >= Self::UntypedAtomic as u8
238            && !matches!(
239                self,
240                Self::NmTokens | Self::IdRefs | Self::Entities | Self::Error
241            )
242    }
243
244    /// Returns true if this is a list type (NMTOKENS, IDREFS, ENTITIES).
245    #[inline]
246    pub fn is_list(&self) -> bool {
247        matches!(self, Self::NmTokens | Self::IdRefs | Self::Entities)
248    }
249
250    /// Returns true if this is one of the 19 XSD primitive atomic types
251    /// that derive from `xs:anyAtomicType` in XSD 1.1 (§3.16.7.3).
252    #[inline]
253    pub fn is_primitive_atomic(&self) -> bool {
254        matches!(
255            self,
256            Self::String
257                | Self::Boolean
258                | Self::Decimal
259                | Self::Float
260                | Self::Double
261                | Self::Duration
262                | Self::DateTime
263                | Self::Time
264                | Self::Date
265                | Self::GYearMonth
266                | Self::GYear
267                | Self::GMonthDay
268                | Self::GDay
269                | Self::GMonth
270                | Self::HexBinary
271                | Self::Base64Binary
272                | Self::AnyUri
273                | Self::QName
274                | Self::Notation
275        )
276    }
277
278    /// Returns true if this is a numeric type.
279    #[inline]
280    pub fn is_numeric(&self) -> bool {
281        matches!(
282            self,
283            Self::Decimal
284                | Self::Float
285                | Self::Double
286                | Self::Integer
287                | Self::NonPositiveInteger
288                | Self::NegativeInteger
289                | Self::Long
290                | Self::Int
291                | Self::Short
292                | Self::Byte
293                | Self::NonNegativeInteger
294                | Self::UnsignedLong
295                | Self::UnsignedInt
296                | Self::UnsignedShort
297                | Self::UnsignedByte
298                | Self::PositiveInteger
299        )
300    }
301
302    /// Returns true if this is a date/time type.
303    #[inline]
304    pub fn is_date_time(&self) -> bool {
305        matches!(
306            self,
307            Self::Duration
308                | Self::DateTime
309                | Self::Time
310                | Self::Date
311                | Self::GYearMonth
312                | Self::GYear
313                | Self::GMonthDay
314                | Self::GDay
315                | Self::GMonth
316                | Self::YearMonthDuration
317                | Self::DayTimeDuration
318                | Self::DateTimeStamp
319        )
320    }
321
322    /// Returns true if this is a string-derived type.
323    #[inline]
324    pub fn is_string_derived(&self) -> bool {
325        matches!(
326            self,
327            Self::String
328                | Self::NormalizedString
329                | Self::Token
330                | Self::Language
331                | Self::NmToken
332                | Self::Name
333                | Self::NCName
334                | Self::Id
335                | Self::IdRef
336                | Self::Entity
337        )
338    }
339
340    /// Returns true if this is an XSD 1.1 type.
341    #[inline]
342    pub fn is_xsd11(&self) -> bool {
343        matches!(
344            self,
345            Self::AnyAtomicType
346                | Self::YearMonthDuration
347                | Self::DayTimeDuration
348                | Self::DateTimeStamp
349                | Self::Error
350        )
351    }
352
353    /// Returns the item type for list types, or None for non-list types.
354    #[inline]
355    pub fn list_item_type(&self) -> Option<XmlTypeCode> {
356        match self {
357            Self::NmTokens => Some(Self::NmToken),
358            Self::IdRefs => Some(Self::IdRef),
359            Self::Entities => Some(Self::Entity),
360            _ => None,
361        }
362    }
363
364    /// Get the local name of this type code (XSD type name).
365    pub fn local_name(&self) -> Option<&'static str> {
366        match self {
367            Self::None
368            | Self::Item
369            | Self::Node
370            | Self::Document
371            | Self::Element
372            | Self::Attribute
373            | Self::Namespace
374            | Self::ProcessingInstruction
375            | Self::Comment
376            | Self::Text => None,
377
378            Self::AnyType => Some("anyType"),
379            Self::AnySimpleType => Some("anySimpleType"),
380            Self::AnyAtomicType => Some("anyAtomicType"),
381            Self::UntypedAtomic => Some("untypedAtomic"),
382            Self::String => Some("string"),
383            Self::NormalizedString => Some("normalizedString"),
384            Self::Token => Some("token"),
385            Self::Language => Some("language"),
386            Self::NmToken => Some("NMTOKEN"),
387            Self::Name => Some("Name"),
388            Self::NCName => Some("NCName"),
389            Self::Id => Some("ID"),
390            Self::IdRef => Some("IDREF"),
391            Self::Entity => Some("ENTITY"),
392            Self::Boolean => Some("boolean"),
393            Self::Decimal => Some("decimal"),
394            Self::Float => Some("float"),
395            Self::Double => Some("double"),
396            Self::Integer => Some("integer"),
397            Self::NonPositiveInteger => Some("nonPositiveInteger"),
398            Self::NegativeInteger => Some("negativeInteger"),
399            Self::Long => Some("long"),
400            Self::Int => Some("int"),
401            Self::Short => Some("short"),
402            Self::Byte => Some("byte"),
403            Self::NonNegativeInteger => Some("nonNegativeInteger"),
404            Self::UnsignedLong => Some("unsignedLong"),
405            Self::UnsignedInt => Some("unsignedInt"),
406            Self::UnsignedShort => Some("unsignedShort"),
407            Self::UnsignedByte => Some("unsignedByte"),
408            Self::PositiveInteger => Some("positiveInteger"),
409            Self::Duration => Some("duration"),
410            Self::DateTime => Some("dateTime"),
411            Self::Time => Some("time"),
412            Self::Date => Some("date"),
413            Self::GYearMonth => Some("gYearMonth"),
414            Self::GYear => Some("gYear"),
415            Self::GMonthDay => Some("gMonthDay"),
416            Self::GDay => Some("gDay"),
417            Self::GMonth => Some("gMonth"),
418            Self::YearMonthDuration => Some("yearMonthDuration"),
419            Self::DayTimeDuration => Some("dayTimeDuration"),
420            Self::DateTimeStamp => Some("dateTimeStamp"),
421            Self::HexBinary => Some("hexBinary"),
422            Self::Base64Binary => Some("base64Binary"),
423            Self::AnyUri => Some("anyURI"),
424            Self::QName => Some("QName"),
425            Self::Notation => Some("NOTATION"),
426            Self::NmTokens => Some("NMTOKENS"),
427            Self::IdRefs => Some("IDREFS"),
428            Self::Entities => Some("ENTITIES"),
429            Self::Error => Some("error"),
430        }
431    }
432
433    /// Parse type code from XSD local name.
434    pub fn from_local_name(name: &str) -> Option<XmlTypeCode> {
435        match name {
436            "anyType" => Some(Self::AnyType),
437            "anySimpleType" => Some(Self::AnySimpleType),
438            "anyAtomicType" => Some(Self::AnyAtomicType),
439            "untypedAtomic" => Some(Self::UntypedAtomic),
440            "string" => Some(Self::String),
441            "normalizedString" => Some(Self::NormalizedString),
442            "token" => Some(Self::Token),
443            "language" => Some(Self::Language),
444            "NMTOKEN" => Some(Self::NmToken),
445            "Name" => Some(Self::Name),
446            "NCName" => Some(Self::NCName),
447            "ID" => Some(Self::Id),
448            "IDREF" => Some(Self::IdRef),
449            "ENTITY" => Some(Self::Entity),
450            "boolean" => Some(Self::Boolean),
451            "decimal" => Some(Self::Decimal),
452            "float" => Some(Self::Float),
453            "double" => Some(Self::Double),
454            "integer" => Some(Self::Integer),
455            "nonPositiveInteger" => Some(Self::NonPositiveInteger),
456            "negativeInteger" => Some(Self::NegativeInteger),
457            "long" => Some(Self::Long),
458            "int" => Some(Self::Int),
459            "short" => Some(Self::Short),
460            "byte" => Some(Self::Byte),
461            "nonNegativeInteger" => Some(Self::NonNegativeInteger),
462            "unsignedLong" => Some(Self::UnsignedLong),
463            "unsignedInt" => Some(Self::UnsignedInt),
464            "unsignedShort" => Some(Self::UnsignedShort),
465            "unsignedByte" => Some(Self::UnsignedByte),
466            "positiveInteger" => Some(Self::PositiveInteger),
467            "duration" => Some(Self::Duration),
468            "dateTime" => Some(Self::DateTime),
469            "time" => Some(Self::Time),
470            "date" => Some(Self::Date),
471            "gYearMonth" => Some(Self::GYearMonth),
472            "gYear" => Some(Self::GYear),
473            "gMonthDay" => Some(Self::GMonthDay),
474            "gDay" => Some(Self::GDay),
475            "gMonth" => Some(Self::GMonth),
476            "yearMonthDuration" => Some(Self::YearMonthDuration),
477            "dayTimeDuration" => Some(Self::DayTimeDuration),
478            "dateTimeStamp" => Some(Self::DateTimeStamp),
479            "hexBinary" => Some(Self::HexBinary),
480            "base64Binary" => Some(Self::Base64Binary),
481            "anyURI" => Some(Self::AnyUri),
482            "QName" => Some(Self::QName),
483            "NOTATION" => Some(Self::Notation),
484            "NMTOKENS" => Some(Self::NmTokens),
485            "IDREFS" => Some(Self::IdRefs),
486            "ENTITIES" => Some(Self::Entities),
487            "error" => Some(Self::Error),
488            _ => None,
489        }
490    }
491}
492
493// ============================================================================
494// PrimitiveTypeCode - The 19 primitive XSD types
495// ============================================================================
496
497/// Primitive type codes identifying the fundamental XSD types.
498///
499/// Used for validator dispatch and value space identification.
500/// These are the 19 primitive types from which all other simple types derive.
501/// See XSD_TYPE_DESIGN.md §3.3 for specification.
502#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
503pub enum PrimitiveTypeCode {
504    /// xs:string - character sequences
505    String,
506    /// xs:boolean - true/false values
507    Boolean,
508    /// xs:decimal - arbitrary precision decimal numbers
509    Decimal,
510    /// xs:float - IEEE 754 single-precision float
511    Float,
512    /// xs:double - IEEE 754 double-precision float
513    Double,
514    /// xs:duration - time duration (PnYnMnDTnHnMnS)
515    Duration,
516    /// xs:dateTime - date and time
517    DateTime,
518    /// xs:time - time of day
519    Time,
520    /// xs:date - calendar date
521    Date,
522    /// xs:gYearMonth - Gregorian year and month
523    GYearMonth,
524    /// xs:gYear - Gregorian year
525    GYear,
526    /// xs:gMonthDay - Gregorian month and day
527    GMonthDay,
528    /// xs:gDay - Gregorian day
529    GDay,
530    /// xs:gMonth - Gregorian month
531    GMonth,
532    /// xs:hexBinary - hex-encoded binary data
533    HexBinary,
534    /// xs:base64Binary - base64-encoded binary data
535    Base64Binary,
536    /// xs:anyURI - URI reference
537    AnyUri,
538    /// xs:QName - qualified name (namespace + local name)
539    QName,
540    /// xs:NOTATION - notation reference
541    Notation,
542}
543
544impl PrimitiveTypeCode {
545    /// Get the primitive type code for any XmlTypeCode.
546    ///
547    /// Returns the primitive type from which the given type derives,
548    /// or `None` for non-atomic types (nodes, lists, ur-types).
549    pub fn from_type_code(code: XmlTypeCode) -> Option<Self> {
550        match code {
551            // String hierarchy -> String
552            XmlTypeCode::String
553            | XmlTypeCode::NormalizedString
554            | XmlTypeCode::Token
555            | XmlTypeCode::Language
556            | XmlTypeCode::NmToken
557            | XmlTypeCode::Name
558            | XmlTypeCode::NCName
559            | XmlTypeCode::Id
560            | XmlTypeCode::IdRef
561            | XmlTypeCode::Entity
562            | XmlTypeCode::UntypedAtomic => Some(Self::String),
563
564            XmlTypeCode::Boolean => Some(Self::Boolean),
565
566            // Decimal hierarchy -> Decimal
567            XmlTypeCode::Decimal
568            | XmlTypeCode::Integer
569            | XmlTypeCode::NonPositiveInteger
570            | XmlTypeCode::NegativeInteger
571            | XmlTypeCode::Long
572            | XmlTypeCode::Int
573            | XmlTypeCode::Short
574            | XmlTypeCode::Byte
575            | XmlTypeCode::NonNegativeInteger
576            | XmlTypeCode::UnsignedLong
577            | XmlTypeCode::UnsignedInt
578            | XmlTypeCode::UnsignedShort
579            | XmlTypeCode::UnsignedByte
580            | XmlTypeCode::PositiveInteger => Some(Self::Decimal),
581
582            XmlTypeCode::Float => Some(Self::Float),
583            XmlTypeCode::Double => Some(Self::Double),
584
585            // Duration hierarchy
586            XmlTypeCode::Duration
587            | XmlTypeCode::YearMonthDuration
588            | XmlTypeCode::DayTimeDuration => Some(Self::Duration),
589
590            // DateTime hierarchy
591            XmlTypeCode::DateTime | XmlTypeCode::DateTimeStamp => Some(Self::DateTime),
592
593            XmlTypeCode::Time => Some(Self::Time),
594            XmlTypeCode::Date => Some(Self::Date),
595            XmlTypeCode::GYearMonth => Some(Self::GYearMonth),
596            XmlTypeCode::GYear => Some(Self::GYear),
597            XmlTypeCode::GMonthDay => Some(Self::GMonthDay),
598            XmlTypeCode::GDay => Some(Self::GDay),
599            XmlTypeCode::GMonth => Some(Self::GMonth),
600            XmlTypeCode::HexBinary => Some(Self::HexBinary),
601            XmlTypeCode::Base64Binary => Some(Self::Base64Binary),
602            XmlTypeCode::AnyUri => Some(Self::AnyUri),
603            XmlTypeCode::QName => Some(Self::QName),
604            XmlTypeCode::Notation => Some(Self::Notation),
605
606            // Non-atomic types have no primitive
607            _ => None,
608        }
609    }
610
611    /// Get the XmlTypeCode for this primitive type.
612    pub fn to_type_code(&self) -> XmlTypeCode {
613        match self {
614            Self::String => XmlTypeCode::String,
615            Self::Boolean => XmlTypeCode::Boolean,
616            Self::Decimal => XmlTypeCode::Decimal,
617            Self::Float => XmlTypeCode::Float,
618            Self::Double => XmlTypeCode::Double,
619            Self::Duration => XmlTypeCode::Duration,
620            Self::DateTime => XmlTypeCode::DateTime,
621            Self::Time => XmlTypeCode::Time,
622            Self::Date => XmlTypeCode::Date,
623            Self::GYearMonth => XmlTypeCode::GYearMonth,
624            Self::GYear => XmlTypeCode::GYear,
625            Self::GMonthDay => XmlTypeCode::GMonthDay,
626            Self::GDay => XmlTypeCode::GDay,
627            Self::GMonth => XmlTypeCode::GMonth,
628            Self::HexBinary => XmlTypeCode::HexBinary,
629            Self::Base64Binary => XmlTypeCode::Base64Binary,
630            Self::AnyUri => XmlTypeCode::AnyUri,
631            Self::QName => XmlTypeCode::QName,
632            Self::Notation => XmlTypeCode::Notation,
633        }
634    }
635
636    /// Get the local name of this primitive type.
637    pub fn local_name(&self) -> &'static str {
638        match self {
639            Self::String => "string",
640            Self::Boolean => "boolean",
641            Self::Decimal => "decimal",
642            Self::Float => "float",
643            Self::Double => "double",
644            Self::Duration => "duration",
645            Self::DateTime => "dateTime",
646            Self::Time => "time",
647            Self::Date => "date",
648            Self::GYearMonth => "gYearMonth",
649            Self::GYear => "gYear",
650            Self::GMonthDay => "gMonthDay",
651            Self::GDay => "gDay",
652            Self::GMonth => "gMonth",
653            Self::HexBinary => "hexBinary",
654            Self::Base64Binary => "base64Binary",
655            Self::AnyUri => "anyURI",
656            Self::QName => "QName",
657            Self::Notation => "NOTATION",
658        }
659    }
660
661    /// Returns true if this is a numeric primitive type.
662    pub fn is_numeric(&self) -> bool {
663        matches!(self, Self::Decimal | Self::Float | Self::Double)
664    }
665
666    /// Returns an iterator over all primitive type codes.
667    pub fn all() -> impl Iterator<Item = PrimitiveTypeCode> {
668        [
669            Self::String,
670            Self::Boolean,
671            Self::Decimal,
672            Self::Float,
673            Self::Double,
674            Self::Duration,
675            Self::DateTime,
676            Self::Time,
677            Self::Date,
678            Self::GYearMonth,
679            Self::GYear,
680            Self::GMonthDay,
681            Self::GDay,
682            Self::GMonth,
683            Self::HexBinary,
684            Self::Base64Binary,
685            Self::AnyUri,
686            Self::QName,
687            Self::Notation,
688        ]
689        .into_iter()
690    }
691}
692
693// ============================================================================
694// ValueKind - Runtime value kind for type discrimination
695// ============================================================================
696
697/// Runtime value kind for type discrimination.
698///
699/// Used to identify the category of a value at runtime,
700/// enabling efficient dispatch in XPath2 operations.
701/// See XSD_TYPE_DESIGN.md §3.4 for specification.
702#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
703pub enum ValueKind {
704    /// Atomic value (single indivisible value)
705    #[default]
706    Atomic,
707    /// List value (sequence of atomic values)
708    List,
709    /// Union value (one of multiple possible types)
710    Union,
711}
712
713// ============================================================================
714// Tests
715// ============================================================================
716
717#[cfg(test)]
718mod tests {
719    use super::*;
720
721    #[test]
722    fn test_xml_type_code_ordering() {
723        // Verify .NET-compatible ordering
724        assert_eq!(XmlTypeCode::None as u8, 0);
725        assert_eq!(XmlTypeCode::Item as u8, 1);
726        assert_eq!(XmlTypeCode::Node as u8, 2);
727        assert_eq!(XmlTypeCode::AnyType as u8, 10);
728        assert_eq!(XmlTypeCode::AnySimpleType as u8, 11);
729        assert_eq!(XmlTypeCode::AnyAtomicType as u8, 12);
730        assert_eq!(XmlTypeCode::UntypedAtomic as u8, 13);
731        assert_eq!(XmlTypeCode::String as u8, 14);
732        assert_eq!(XmlTypeCode::Boolean as u8, 24);
733        assert_eq!(XmlTypeCode::Decimal as u8, 25);
734        assert_eq!(XmlTypeCode::Duration as u8, 41);
735        assert_eq!(XmlTypeCode::DateTime as u8, 42);
736        assert_eq!(XmlTypeCode::HexBinary as u8, 53);
737        assert_eq!(XmlTypeCode::AnyUri as u8, 55);
738        assert_eq!(XmlTypeCode::QName as u8, 56);
739        assert_eq!(XmlTypeCode::Notation as u8, 57);
740        assert_eq!(XmlTypeCode::NmTokens as u8, 58);
741        assert_eq!(XmlTypeCode::IdRefs as u8, 59);
742        assert_eq!(XmlTypeCode::Entities as u8, 60);
743    }
744
745    #[test]
746    fn test_xml_type_code_is_node() {
747        assert!(XmlTypeCode::Node.is_node());
748        assert!(XmlTypeCode::Document.is_node());
749        assert!(XmlTypeCode::Element.is_node());
750        assert!(XmlTypeCode::Attribute.is_node());
751        assert!(XmlTypeCode::Text.is_node());
752        assert!(XmlTypeCode::Comment.is_node());
753        assert!(XmlTypeCode::ProcessingInstruction.is_node());
754        assert!(XmlTypeCode::Namespace.is_node());
755
756        assert!(!XmlTypeCode::String.is_node());
757        assert!(!XmlTypeCode::Integer.is_node());
758        assert!(!XmlTypeCode::AnyType.is_node());
759    }
760
761    #[test]
762    fn test_xml_type_code_is_atomic() {
763        assert!(XmlTypeCode::String.is_atomic());
764        assert!(XmlTypeCode::Integer.is_atomic());
765        assert!(XmlTypeCode::DateTime.is_atomic());
766        assert!(XmlTypeCode::UntypedAtomic.is_atomic());
767        assert!(XmlTypeCode::Boolean.is_atomic());
768
769        // List types are not atomic
770        assert!(!XmlTypeCode::NmTokens.is_atomic());
771        assert!(!XmlTypeCode::IdRefs.is_atomic());
772        assert!(!XmlTypeCode::Entities.is_atomic());
773
774        // Abstract/node types are not atomic
775        assert!(!XmlTypeCode::None.is_atomic());
776        assert!(!XmlTypeCode::Node.is_atomic());
777        assert!(!XmlTypeCode::AnyType.is_atomic());
778        assert!(!XmlTypeCode::AnySimpleType.is_atomic());
779    }
780
781    #[test]
782    fn test_xml_type_code_is_list() {
783        assert!(XmlTypeCode::NmTokens.is_list());
784        assert!(XmlTypeCode::IdRefs.is_list());
785        assert!(XmlTypeCode::Entities.is_list());
786
787        assert!(!XmlTypeCode::NmToken.is_list());
788        assert!(!XmlTypeCode::IdRef.is_list());
789        assert!(!XmlTypeCode::Entity.is_list());
790        assert!(!XmlTypeCode::String.is_list());
791    }
792
793    #[test]
794    fn test_xml_type_code_is_numeric() {
795        assert!(XmlTypeCode::Decimal.is_numeric());
796        assert!(XmlTypeCode::Integer.is_numeric());
797        assert!(XmlTypeCode::Float.is_numeric());
798        assert!(XmlTypeCode::Double.is_numeric());
799        assert!(XmlTypeCode::Long.is_numeric());
800        assert!(XmlTypeCode::UnsignedByte.is_numeric());
801
802        assert!(!XmlTypeCode::String.is_numeric());
803        assert!(!XmlTypeCode::Boolean.is_numeric());
804        assert!(!XmlTypeCode::DateTime.is_numeric());
805    }
806
807    #[test]
808    fn test_xml_type_code_is_date_time() {
809        assert!(XmlTypeCode::DateTime.is_date_time());
810        assert!(XmlTypeCode::Date.is_date_time());
811        assert!(XmlTypeCode::Time.is_date_time());
812        assert!(XmlTypeCode::Duration.is_date_time());
813        assert!(XmlTypeCode::GYear.is_date_time());
814        assert!(XmlTypeCode::YearMonthDuration.is_date_time());
815        assert!(XmlTypeCode::DayTimeDuration.is_date_time());
816        assert!(XmlTypeCode::DateTimeStamp.is_date_time());
817
818        assert!(!XmlTypeCode::String.is_date_time());
819        assert!(!XmlTypeCode::Integer.is_date_time());
820    }
821
822    #[test]
823    fn test_xml_type_code_is_xsd11() {
824        assert!(XmlTypeCode::AnyAtomicType.is_xsd11());
825        assert!(XmlTypeCode::YearMonthDuration.is_xsd11());
826        assert!(XmlTypeCode::DayTimeDuration.is_xsd11());
827        assert!(XmlTypeCode::DateTimeStamp.is_xsd11());
828
829        assert!(!XmlTypeCode::String.is_xsd11());
830        assert!(!XmlTypeCode::DateTime.is_xsd11());
831        assert!(!XmlTypeCode::Duration.is_xsd11());
832    }
833
834    #[test]
835    fn test_xml_type_code_list_item_type() {
836        assert_eq!(
837            XmlTypeCode::NmTokens.list_item_type(),
838            Some(XmlTypeCode::NmToken)
839        );
840        assert_eq!(
841            XmlTypeCode::IdRefs.list_item_type(),
842            Some(XmlTypeCode::IdRef)
843        );
844        assert_eq!(
845            XmlTypeCode::Entities.list_item_type(),
846            Some(XmlTypeCode::Entity)
847        );
848        assert_eq!(XmlTypeCode::String.list_item_type(), None);
849    }
850
851    #[test]
852    fn test_xml_type_code_local_name() {
853        assert_eq!(XmlTypeCode::String.local_name(), Some("string"));
854        assert_eq!(XmlTypeCode::Integer.local_name(), Some("integer"));
855        assert_eq!(XmlTypeCode::DateTime.local_name(), Some("dateTime"));
856        assert_eq!(XmlTypeCode::AnyUri.local_name(), Some("anyURI"));
857        assert_eq!(XmlTypeCode::QName.local_name(), Some("QName"));
858        assert_eq!(XmlTypeCode::NmToken.local_name(), Some("NMTOKEN"));
859        assert_eq!(XmlTypeCode::NmTokens.local_name(), Some("NMTOKENS"));
860        assert_eq!(XmlTypeCode::None.local_name(), None);
861        assert_eq!(XmlTypeCode::Element.local_name(), None);
862    }
863
864    #[test]
865    fn test_xml_type_code_from_local_name() {
866        assert_eq!(
867            XmlTypeCode::from_local_name("string"),
868            Some(XmlTypeCode::String)
869        );
870        assert_eq!(
871            XmlTypeCode::from_local_name("integer"),
872            Some(XmlTypeCode::Integer)
873        );
874        assert_eq!(
875            XmlTypeCode::from_local_name("dateTime"),
876            Some(XmlTypeCode::DateTime)
877        );
878        assert_eq!(
879            XmlTypeCode::from_local_name("anyURI"),
880            Some(XmlTypeCode::AnyUri)
881        );
882        assert_eq!(
883            XmlTypeCode::from_local_name("QName"),
884            Some(XmlTypeCode::QName)
885        );
886        assert_eq!(
887            XmlTypeCode::from_local_name("NMTOKEN"),
888            Some(XmlTypeCode::NmToken)
889        );
890        assert_eq!(
891            XmlTypeCode::from_local_name("NMTOKENS"),
892            Some(XmlTypeCode::NmTokens)
893        );
894        assert_eq!(XmlTypeCode::from_local_name("unknown"), None);
895    }
896
897    #[test]
898    fn test_xml_type_code_roundtrip() {
899        // All XSD types should round-trip through local_name/from_local_name
900        for code_val in 10..=60u8 {
901            let code: XmlTypeCode = unsafe { std::mem::transmute(code_val) };
902            if let Some(name) = code.local_name() {
903                assert_eq!(
904                    XmlTypeCode::from_local_name(name),
905                    Some(code),
906                    "Round-trip failed for {:?}",
907                    code
908                );
909            }
910        }
911    }
912
913    #[test]
914    fn test_primitive_type_code_count() {
915        // There should be exactly 19 primitive types
916        assert_eq!(PrimitiveTypeCode::all().count(), 19);
917    }
918
919    #[test]
920    fn test_primitive_type_code_from_type_code() {
921        // String hierarchy
922        assert_eq!(
923            PrimitiveTypeCode::from_type_code(XmlTypeCode::String),
924            Some(PrimitiveTypeCode::String)
925        );
926        assert_eq!(
927            PrimitiveTypeCode::from_type_code(XmlTypeCode::NormalizedString),
928            Some(PrimitiveTypeCode::String)
929        );
930        assert_eq!(
931            PrimitiveTypeCode::from_type_code(XmlTypeCode::Token),
932            Some(PrimitiveTypeCode::String)
933        );
934        assert_eq!(
935            PrimitiveTypeCode::from_type_code(XmlTypeCode::NCName),
936            Some(PrimitiveTypeCode::String)
937        );
938
939        // Decimal hierarchy
940        assert_eq!(
941            PrimitiveTypeCode::from_type_code(XmlTypeCode::Decimal),
942            Some(PrimitiveTypeCode::Decimal)
943        );
944        assert_eq!(
945            PrimitiveTypeCode::from_type_code(XmlTypeCode::Integer),
946            Some(PrimitiveTypeCode::Decimal)
947        );
948        assert_eq!(
949            PrimitiveTypeCode::from_type_code(XmlTypeCode::Long),
950            Some(PrimitiveTypeCode::Decimal)
951        );
952        assert_eq!(
953            PrimitiveTypeCode::from_type_code(XmlTypeCode::UnsignedInt),
954            Some(PrimitiveTypeCode::Decimal)
955        );
956
957        // Duration hierarchy
958        assert_eq!(
959            PrimitiveTypeCode::from_type_code(XmlTypeCode::Duration),
960            Some(PrimitiveTypeCode::Duration)
961        );
962        assert_eq!(
963            PrimitiveTypeCode::from_type_code(XmlTypeCode::YearMonthDuration),
964            Some(PrimitiveTypeCode::Duration)
965        );
966        assert_eq!(
967            PrimitiveTypeCode::from_type_code(XmlTypeCode::DayTimeDuration),
968            Some(PrimitiveTypeCode::Duration)
969        );
970
971        // DateTime hierarchy
972        assert_eq!(
973            PrimitiveTypeCode::from_type_code(XmlTypeCode::DateTime),
974            Some(PrimitiveTypeCode::DateTime)
975        );
976        assert_eq!(
977            PrimitiveTypeCode::from_type_code(XmlTypeCode::DateTimeStamp),
978            Some(PrimitiveTypeCode::DateTime)
979        );
980
981        // Primitives
982        assert_eq!(
983            PrimitiveTypeCode::from_type_code(XmlTypeCode::Boolean),
984            Some(PrimitiveTypeCode::Boolean)
985        );
986        assert_eq!(
987            PrimitiveTypeCode::from_type_code(XmlTypeCode::Float),
988            Some(PrimitiveTypeCode::Float)
989        );
990        assert_eq!(
991            PrimitiveTypeCode::from_type_code(XmlTypeCode::Double),
992            Some(PrimitiveTypeCode::Double)
993        );
994        assert_eq!(
995            PrimitiveTypeCode::from_type_code(XmlTypeCode::HexBinary),
996            Some(PrimitiveTypeCode::HexBinary)
997        );
998        assert_eq!(
999            PrimitiveTypeCode::from_type_code(XmlTypeCode::Base64Binary),
1000            Some(PrimitiveTypeCode::Base64Binary)
1001        );
1002        assert_eq!(
1003            PrimitiveTypeCode::from_type_code(XmlTypeCode::AnyUri),
1004            Some(PrimitiveTypeCode::AnyUri)
1005        );
1006        assert_eq!(
1007            PrimitiveTypeCode::from_type_code(XmlTypeCode::QName),
1008            Some(PrimitiveTypeCode::QName)
1009        );
1010        assert_eq!(
1011            PrimitiveTypeCode::from_type_code(XmlTypeCode::Notation),
1012            Some(PrimitiveTypeCode::Notation)
1013        );
1014
1015        // Non-atomic types return None
1016        assert_eq!(PrimitiveTypeCode::from_type_code(XmlTypeCode::None), None);
1017        assert_eq!(PrimitiveTypeCode::from_type_code(XmlTypeCode::Node), None);
1018        assert_eq!(
1019            PrimitiveTypeCode::from_type_code(XmlTypeCode::AnyType),
1020            None
1021        );
1022        assert_eq!(
1023            PrimitiveTypeCode::from_type_code(XmlTypeCode::NmTokens),
1024            None
1025        );
1026    }
1027
1028    #[test]
1029    fn test_primitive_type_code_to_type_code() {
1030        for prim in PrimitiveTypeCode::all() {
1031            let code = prim.to_type_code();
1032            assert_eq!(
1033                PrimitiveTypeCode::from_type_code(code),
1034                Some(prim),
1035                "Round-trip failed for {:?}",
1036                prim
1037            );
1038        }
1039    }
1040
1041    #[test]
1042    fn test_primitive_type_code_local_name() {
1043        assert_eq!(PrimitiveTypeCode::String.local_name(), "string");
1044        assert_eq!(PrimitiveTypeCode::Boolean.local_name(), "boolean");
1045        assert_eq!(PrimitiveTypeCode::Decimal.local_name(), "decimal");
1046        assert_eq!(PrimitiveTypeCode::DateTime.local_name(), "dateTime");
1047        assert_eq!(PrimitiveTypeCode::AnyUri.local_name(), "anyURI");
1048        assert_eq!(PrimitiveTypeCode::QName.local_name(), "QName");
1049        assert_eq!(PrimitiveTypeCode::Notation.local_name(), "NOTATION");
1050    }
1051
1052    #[test]
1053    fn test_value_kind_default() {
1054        assert_eq!(ValueKind::default(), ValueKind::Atomic);
1055    }
1056
1057    #[test]
1058    fn test_value_kind_variants() {
1059        // Ensure all variants are distinct
1060        assert_ne!(ValueKind::Atomic, ValueKind::List);
1061        assert_ne!(ValueKind::Atomic, ValueKind::Union);
1062        assert_ne!(ValueKind::List, ValueKind::Union);
1063    }
1064}