Skip to main content

xsd_schema/types/
simple.rs

1//! Simple type definitions
2//!
3//! This module implements XSD simple type definitions: atomic, list, and union types.
4
5use super::facets::FacetSet;
6use super::{PrimitiveTypeCode, XmlTypeCode};
7use crate::ids::{NameId, SimpleTypeKey, TypeKey};
8use crate::parser::location::SourceRef;
9use crate::schema::model::DerivationSet;
10
11/// Simple type variety (atomic, list, or union)
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum SimpleTypeVariety {
14    /// Atomic type (single value)
15    Atomic,
16    /// List type (whitespace-separated values of item type)
17    List,
18    /// Union type (value satisfies one of member types)
19    Union,
20}
21
22/// Derivation method for simple types
23///
24/// This enum specifies how a simple type was derived from its base type.
25/// Note: This is different from complex type derivation which uses
26/// Restriction/Extension. Simple types use Restriction/List/Union.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
28pub enum SimpleTypeDerivationMethod {
29    /// Derived by restriction (constraining base type with facets)
30    #[default]
31    Restriction,
32    /// Derived as a list type (whitespace-separated values)
33    List,
34    /// Derived as a union type (one of multiple member types)
35    Union,
36}
37
38/// Reference to a simple type (by key or built-in name)
39#[derive(Debug, Clone)]
40pub enum SimpleTypeRef {
41    /// Reference to a defined simple type
42    Resolved(SimpleTypeKey),
43    /// Unresolved reference (QName to be resolved later)
44    Unresolved {
45        namespace: Option<NameId>,
46        local_name: NameId,
47    },
48    /// Built-in type reference
49    BuiltIn(BuiltInType),
50}
51
52/// Built-in XSD simple types
53///
54/// This enum represents all built-in simple types in XSD 1.0 and 1.1.
55/// Use [`XmlTypeCode`] for a more comprehensive type code enumeration
56/// that includes node types and complex types.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub enum BuiltInType {
59    // Special abstract types
60    /// xs:anySimpleType - base of all simple types
61    AnySimpleType,
62    /// xs:anyAtomicType - base of all atomic types (XSD 1.1)
63    AnyAtomicType,
64    /// xs:untypedAtomic - untyped atomic value (XSD 1.1)
65    UntypedAtomic,
66
67    // Primitive types (19 types)
68    /// xs:string
69    String,
70    /// xs:boolean
71    Boolean,
72    /// xs:decimal
73    Decimal,
74    /// xs:float
75    Float,
76    /// xs:double
77    Double,
78    /// xs:duration
79    Duration,
80    /// xs:dateTime
81    DateTime,
82    /// xs:time
83    Time,
84    /// xs:date
85    Date,
86    /// xs:gYearMonth
87    GYearMonth,
88    /// xs:gYear
89    GYear,
90    /// xs:gMonthDay
91    GMonthDay,
92    /// xs:gDay
93    GDay,
94    /// xs:gMonth
95    GMonth,
96    /// xs:hexBinary
97    HexBinary,
98    /// xs:base64Binary
99    Base64Binary,
100    /// xs:anyURI
101    AnyURI,
102    /// xs:QName
103    QName,
104    /// xs:NOTATION
105    NOTATION,
106
107    // Derived string types
108    /// xs:normalizedString
109    NormalizedString,
110    /// xs:token
111    Token,
112    /// xs:language
113    Language,
114    /// xs:NMTOKEN
115    NMTOKEN,
116    /// xs:NMTOKENS (list type)
117    NMTOKENS,
118    /// xs:Name
119    Name,
120    /// xs:NCName
121    NCName,
122    /// xs:ID
123    ID,
124    /// xs:IDREF
125    IDREF,
126    /// xs:IDREFS (list type)
127    IDREFS,
128    /// xs:ENTITY
129    ENTITY,
130    /// xs:ENTITIES (list type)
131    ENTITIES,
132
133    // Derived numeric types (integer hierarchy)
134    /// xs:integer
135    Integer,
136    /// xs:nonPositiveInteger
137    NonPositiveInteger,
138    /// xs:negativeInteger
139    NegativeInteger,
140    /// xs:long
141    Long,
142    /// xs:int
143    Int,
144    /// xs:short
145    Short,
146    /// xs:byte
147    Byte,
148    /// xs:nonNegativeInteger
149    NonNegativeInteger,
150    /// xs:unsignedLong
151    UnsignedLong,
152    /// xs:unsignedInt
153    UnsignedInt,
154    /// xs:unsignedShort
155    UnsignedShort,
156    /// xs:unsignedByte
157    UnsignedByte,
158    /// xs:positiveInteger
159    PositiveInteger,
160
161    // XSD 1.1 derived types
162    /// xs:yearMonthDuration (XSD 1.1)
163    YearMonthDuration,
164    /// xs:dayTimeDuration (XSD 1.1)
165    DayTimeDuration,
166    /// xs:dateTimeStamp (XSD 1.1)
167    DateTimeStamp,
168    /// xs:error - the bottom type (union of no members); has no valid values (XSD 1.1)
169    XsError,
170}
171
172impl BuiltInType {
173    /// Get the local name of this built-in type.
174    ///
175    /// Delegates to [`XmlTypeCode::local_name`] for consistency.
176    #[inline]
177    pub fn local_name(&self) -> &'static str {
178        // Delegate to XmlTypeCode which has the canonical names
179        self.type_code()
180            .local_name()
181            .expect("BuiltInType always has a local name")
182    }
183
184    /// Get the corresponding [`XmlTypeCode`] for this built-in type.
185    #[inline]
186    pub fn type_code(&self) -> XmlTypeCode {
187        XmlTypeCode::from(*self)
188    }
189
190    /// Get the primitive type code for this built-in type.
191    ///
192    /// Returns `None` for abstract types (AnySimpleType, AnyAtomicType)
193    /// and list types (NMTOKENS, IDREFS, ENTITIES).
194    #[inline]
195    pub fn primitive_type_code(&self) -> Option<PrimitiveTypeCode> {
196        PrimitiveTypeCode::from_type_code(self.type_code())
197    }
198
199    /// Check if this is a primitive type (one of the 19 fundamental types).
200    #[inline]
201    pub fn is_primitive(&self) -> bool {
202        matches!(
203            self,
204            Self::String
205                | Self::Boolean
206                | Self::Decimal
207                | Self::Float
208                | Self::Double
209                | Self::Duration
210                | Self::DateTime
211                | Self::Time
212                | Self::Date
213                | Self::GYearMonth
214                | Self::GYear
215                | Self::GMonthDay
216                | Self::GDay
217                | Self::GMonth
218                | Self::HexBinary
219                | Self::Base64Binary
220                | Self::AnyURI
221                | Self::QName
222                | Self::NOTATION
223        )
224    }
225
226    /// Check if this is a list type (NMTOKENS, IDREFS, ENTITIES).
227    #[inline]
228    pub fn is_list(&self) -> bool {
229        matches!(self, Self::NMTOKENS | Self::IDREFS | Self::ENTITIES)
230    }
231
232    /// Check if this is an XSD 1.1 type.
233    #[inline]
234    pub fn is_xsd11(&self) -> bool {
235        matches!(
236            self,
237            Self::AnyAtomicType
238                | Self::UntypedAtomic
239                | Self::YearMonthDuration
240                | Self::DayTimeDuration
241                | Self::DateTimeStamp
242                | Self::XsError
243        )
244    }
245
246    /// Parse a built-in type from its local name.
247    ///
248    /// Delegates to [`XmlTypeCode::from_local_name`] for consistency.
249    pub fn from_local_name(name: &str) -> Option<BuiltInType> {
250        XmlTypeCode::from_local_name(name).and_then(|code| Self::try_from(code).ok())
251    }
252
253    /// Returns an iterator over all built-in types.
254    pub fn all() -> impl Iterator<Item = BuiltInType> {
255        [
256            Self::AnySimpleType,
257            Self::AnyAtomicType,
258            Self::UntypedAtomic,
259            Self::String,
260            Self::Boolean,
261            Self::Decimal,
262            Self::Float,
263            Self::Double,
264            Self::Duration,
265            Self::DateTime,
266            Self::Time,
267            Self::Date,
268            Self::GYearMonth,
269            Self::GYear,
270            Self::GMonthDay,
271            Self::GDay,
272            Self::GMonth,
273            Self::HexBinary,
274            Self::Base64Binary,
275            Self::AnyURI,
276            Self::QName,
277            Self::NOTATION,
278            Self::NormalizedString,
279            Self::Token,
280            Self::Language,
281            Self::NMTOKEN,
282            Self::NMTOKENS,
283            Self::Name,
284            Self::NCName,
285            Self::ID,
286            Self::IDREF,
287            Self::IDREFS,
288            Self::ENTITY,
289            Self::ENTITIES,
290            Self::Integer,
291            Self::NonPositiveInteger,
292            Self::NegativeInteger,
293            Self::Long,
294            Self::Int,
295            Self::Short,
296            Self::Byte,
297            Self::NonNegativeInteger,
298            Self::UnsignedLong,
299            Self::UnsignedInt,
300            Self::UnsignedShort,
301            Self::UnsignedByte,
302            Self::PositiveInteger,
303            Self::YearMonthDuration,
304            Self::DayTimeDuration,
305            Self::DateTimeStamp,
306            Self::XsError,
307        ]
308        .into_iter()
309    }
310}
311
312// ============================================================================
313// Conversion between BuiltInType and XmlTypeCode
314// ============================================================================
315
316impl From<BuiltInType> for XmlTypeCode {
317    fn from(builtin: BuiltInType) -> Self {
318        match builtin {
319            BuiltInType::AnySimpleType => XmlTypeCode::AnySimpleType,
320            BuiltInType::AnyAtomicType => XmlTypeCode::AnyAtomicType,
321            BuiltInType::UntypedAtomic => XmlTypeCode::UntypedAtomic,
322            BuiltInType::String => XmlTypeCode::String,
323            BuiltInType::Boolean => XmlTypeCode::Boolean,
324            BuiltInType::Decimal => XmlTypeCode::Decimal,
325            BuiltInType::Float => XmlTypeCode::Float,
326            BuiltInType::Double => XmlTypeCode::Double,
327            BuiltInType::Duration => XmlTypeCode::Duration,
328            BuiltInType::DateTime => XmlTypeCode::DateTime,
329            BuiltInType::Time => XmlTypeCode::Time,
330            BuiltInType::Date => XmlTypeCode::Date,
331            BuiltInType::GYearMonth => XmlTypeCode::GYearMonth,
332            BuiltInType::GYear => XmlTypeCode::GYear,
333            BuiltInType::GMonthDay => XmlTypeCode::GMonthDay,
334            BuiltInType::GDay => XmlTypeCode::GDay,
335            BuiltInType::GMonth => XmlTypeCode::GMonth,
336            BuiltInType::HexBinary => XmlTypeCode::HexBinary,
337            BuiltInType::Base64Binary => XmlTypeCode::Base64Binary,
338            BuiltInType::AnyURI => XmlTypeCode::AnyUri,
339            BuiltInType::QName => XmlTypeCode::QName,
340            BuiltInType::NOTATION => XmlTypeCode::Notation,
341            BuiltInType::NormalizedString => XmlTypeCode::NormalizedString,
342            BuiltInType::Token => XmlTypeCode::Token,
343            BuiltInType::Language => XmlTypeCode::Language,
344            BuiltInType::NMTOKEN => XmlTypeCode::NmToken,
345            BuiltInType::NMTOKENS => XmlTypeCode::NmTokens,
346            BuiltInType::Name => XmlTypeCode::Name,
347            BuiltInType::NCName => XmlTypeCode::NCName,
348            BuiltInType::ID => XmlTypeCode::Id,
349            BuiltInType::IDREF => XmlTypeCode::IdRef,
350            BuiltInType::IDREFS => XmlTypeCode::IdRefs,
351            BuiltInType::ENTITY => XmlTypeCode::Entity,
352            BuiltInType::ENTITIES => XmlTypeCode::Entities,
353            BuiltInType::Integer => XmlTypeCode::Integer,
354            BuiltInType::NonPositiveInteger => XmlTypeCode::NonPositiveInteger,
355            BuiltInType::NegativeInteger => XmlTypeCode::NegativeInteger,
356            BuiltInType::Long => XmlTypeCode::Long,
357            BuiltInType::Int => XmlTypeCode::Int,
358            BuiltInType::Short => XmlTypeCode::Short,
359            BuiltInType::Byte => XmlTypeCode::Byte,
360            BuiltInType::NonNegativeInteger => XmlTypeCode::NonNegativeInteger,
361            BuiltInType::UnsignedLong => XmlTypeCode::UnsignedLong,
362            BuiltInType::UnsignedInt => XmlTypeCode::UnsignedInt,
363            BuiltInType::UnsignedShort => XmlTypeCode::UnsignedShort,
364            BuiltInType::UnsignedByte => XmlTypeCode::UnsignedByte,
365            BuiltInType::PositiveInteger => XmlTypeCode::PositiveInteger,
366            BuiltInType::YearMonthDuration => XmlTypeCode::YearMonthDuration,
367            BuiltInType::DayTimeDuration => XmlTypeCode::DayTimeDuration,
368            BuiltInType::DateTimeStamp => XmlTypeCode::DateTimeStamp,
369            BuiltInType::XsError => XmlTypeCode::Error,
370        }
371    }
372}
373
374impl TryFrom<XmlTypeCode> for BuiltInType {
375    type Error = ();
376
377    /// Convert from XmlTypeCode to BuiltInType.
378    ///
379    /// Returns `Err(())` for node types, AnyType, and other non-simple type codes.
380    fn try_from(code: XmlTypeCode) -> Result<Self, Self::Error> {
381        match code {
382            XmlTypeCode::AnySimpleType => Ok(BuiltInType::AnySimpleType),
383            XmlTypeCode::AnyAtomicType => Ok(BuiltInType::AnyAtomicType),
384            XmlTypeCode::UntypedAtomic => Ok(BuiltInType::UntypedAtomic),
385            XmlTypeCode::String => Ok(BuiltInType::String),
386            XmlTypeCode::Boolean => Ok(BuiltInType::Boolean),
387            XmlTypeCode::Decimal => Ok(BuiltInType::Decimal),
388            XmlTypeCode::Float => Ok(BuiltInType::Float),
389            XmlTypeCode::Double => Ok(BuiltInType::Double),
390            XmlTypeCode::Duration => Ok(BuiltInType::Duration),
391            XmlTypeCode::DateTime => Ok(BuiltInType::DateTime),
392            XmlTypeCode::Time => Ok(BuiltInType::Time),
393            XmlTypeCode::Date => Ok(BuiltInType::Date),
394            XmlTypeCode::GYearMonth => Ok(BuiltInType::GYearMonth),
395            XmlTypeCode::GYear => Ok(BuiltInType::GYear),
396            XmlTypeCode::GMonthDay => Ok(BuiltInType::GMonthDay),
397            XmlTypeCode::GDay => Ok(BuiltInType::GDay),
398            XmlTypeCode::GMonth => Ok(BuiltInType::GMonth),
399            XmlTypeCode::HexBinary => Ok(BuiltInType::HexBinary),
400            XmlTypeCode::Base64Binary => Ok(BuiltInType::Base64Binary),
401            XmlTypeCode::AnyUri => Ok(BuiltInType::AnyURI),
402            XmlTypeCode::QName => Ok(BuiltInType::QName),
403            XmlTypeCode::Notation => Ok(BuiltInType::NOTATION),
404            XmlTypeCode::NormalizedString => Ok(BuiltInType::NormalizedString),
405            XmlTypeCode::Token => Ok(BuiltInType::Token),
406            XmlTypeCode::Language => Ok(BuiltInType::Language),
407            XmlTypeCode::NmToken => Ok(BuiltInType::NMTOKEN),
408            XmlTypeCode::NmTokens => Ok(BuiltInType::NMTOKENS),
409            XmlTypeCode::Name => Ok(BuiltInType::Name),
410            XmlTypeCode::NCName => Ok(BuiltInType::NCName),
411            XmlTypeCode::Id => Ok(BuiltInType::ID),
412            XmlTypeCode::IdRef => Ok(BuiltInType::IDREF),
413            XmlTypeCode::IdRefs => Ok(BuiltInType::IDREFS),
414            XmlTypeCode::Entity => Ok(BuiltInType::ENTITY),
415            XmlTypeCode::Entities => Ok(BuiltInType::ENTITIES),
416            XmlTypeCode::Integer => Ok(BuiltInType::Integer),
417            XmlTypeCode::NonPositiveInteger => Ok(BuiltInType::NonPositiveInteger),
418            XmlTypeCode::NegativeInteger => Ok(BuiltInType::NegativeInteger),
419            XmlTypeCode::Long => Ok(BuiltInType::Long),
420            XmlTypeCode::Int => Ok(BuiltInType::Int),
421            XmlTypeCode::Short => Ok(BuiltInType::Short),
422            XmlTypeCode::Byte => Ok(BuiltInType::Byte),
423            XmlTypeCode::NonNegativeInteger => Ok(BuiltInType::NonNegativeInteger),
424            XmlTypeCode::UnsignedLong => Ok(BuiltInType::UnsignedLong),
425            XmlTypeCode::UnsignedInt => Ok(BuiltInType::UnsignedInt),
426            XmlTypeCode::UnsignedShort => Ok(BuiltInType::UnsignedShort),
427            XmlTypeCode::UnsignedByte => Ok(BuiltInType::UnsignedByte),
428            XmlTypeCode::PositiveInteger => Ok(BuiltInType::PositiveInteger),
429            XmlTypeCode::YearMonthDuration => Ok(BuiltInType::YearMonthDuration),
430            XmlTypeCode::DayTimeDuration => Ok(BuiltInType::DayTimeDuration),
431            XmlTypeCode::DateTimeStamp => Ok(BuiltInType::DateTimeStamp),
432            XmlTypeCode::Error => Ok(BuiltInType::XsError),
433            // Node types, AnyType, None, Item are not simple types
434            _ => Err(()),
435        }
436    }
437}
438
439/// Simple type definition
440///
441/// Represents an XSD simple type which can be atomic (restriction of base),
442/// list (whitespace-separated items), or union (one of multiple types).
443#[derive(Debug, Clone)]
444pub struct SimpleTypeDef {
445    /// Name (None for anonymous types)
446    pub name: Option<NameId>,
447
448    /// Target namespace
449    pub target_namespace: Option<NameId>,
450
451    /// Source location for error reporting
452    pub source: Option<SourceRef>,
453
454    /// Type variety (atomic, list, or union)
455    pub variety: SimpleTypeVariety,
456
457    /// Derivation method (restriction, list, or union)
458    pub derivation_method: SimpleTypeDerivationMethod,
459
460    /// Base type definition (for atomic types derived by restriction)
461    pub base_type: Option<SimpleTypeRef>,
462
463    /// Item type (for list types)
464    pub item_type: Option<SimpleTypeRef>,
465
466    /// Member types (for union types)
467    pub member_types: Vec<SimpleTypeRef>,
468
469    /// Constraining facets
470    pub facets: FacetSet,
471
472    /// Type code for built-in types (or derived types)
473    ///
474    /// For built-in types, this is the corresponding XmlTypeCode.
475    /// For user-defined types derived from built-in types, this
476    /// may be set to the base type's code for quick type checking.
477    pub type_code: XmlTypeCode,
478
479    /// Primitive type code for atomic types
480    ///
481    /// For atomic types, this indicates which primitive type they
482    /// ultimately derive from (one of the 19 XSD primitive types).
483    /// This is `None` for list types, union types, and abstract types.
484    pub primitive_type: Option<PrimitiveTypeCode>,
485
486    /// Final derivation control
487    pub final_derivation: DerivationSet,
488
489    /// ID attribute value (for identity)
490    pub id: Option<String>,
491}
492
493impl SimpleTypeDef {
494    /// Create a new simple type with restriction variety
495    pub fn new_restriction(
496        name: Option<NameId>,
497        target_namespace: Option<NameId>,
498        base_type: SimpleTypeRef,
499    ) -> Self {
500        Self {
501            name,
502            target_namespace,
503            source: None,
504            variety: SimpleTypeVariety::Atomic,
505            derivation_method: SimpleTypeDerivationMethod::Restriction,
506            base_type: Some(base_type),
507            item_type: None,
508            member_types: Vec::new(),
509            facets: FacetSet::new(),
510            type_code: XmlTypeCode::None,
511            primitive_type: None,
512            final_derivation: DerivationSet::empty(),
513            id: None,
514        }
515    }
516
517    /// Create a new list type
518    pub fn new_list(
519        name: Option<NameId>,
520        target_namespace: Option<NameId>,
521        item_type: SimpleTypeRef,
522    ) -> Self {
523        Self {
524            name,
525            target_namespace,
526            source: None,
527            variety: SimpleTypeVariety::List,
528            derivation_method: SimpleTypeDerivationMethod::List,
529            base_type: None,
530            item_type: Some(item_type),
531            member_types: Vec::new(),
532            facets: FacetSet::new(),
533            type_code: XmlTypeCode::None,
534            primitive_type: None, // List types have no primitive type
535            final_derivation: DerivationSet::empty(),
536            id: None,
537        }
538    }
539
540    /// Create a new union type
541    pub fn new_union(
542        name: Option<NameId>,
543        target_namespace: Option<NameId>,
544        member_types: Vec<SimpleTypeRef>,
545    ) -> Self {
546        Self {
547            name,
548            target_namespace,
549            source: None,
550            variety: SimpleTypeVariety::Union,
551            derivation_method: SimpleTypeDerivationMethod::Union,
552            base_type: None,
553            item_type: None,
554            member_types,
555            facets: FacetSet::new(),
556            type_code: XmlTypeCode::None,
557            primitive_type: None, // Union types have no primitive type
558            final_derivation: DerivationSet::empty(),
559            id: None,
560        }
561    }
562
563    /// Create a simple type for a built-in type
564    ///
565    /// This constructor is used when registering built-in types with their
566    /// proper type code and primitive type information.
567    pub fn new_builtin(
568        name: NameId,
569        target_namespace: Option<NameId>,
570        builtin: BuiltInType,
571    ) -> Self {
572        let type_code = builtin.type_code();
573        let primitive_type = builtin.primitive_type_code();
574        let variety = if builtin.is_list() {
575            SimpleTypeVariety::List
576        } else {
577            SimpleTypeVariety::Atomic
578        };
579        let derivation_method = if builtin.is_list() {
580            SimpleTypeDerivationMethod::List
581        } else {
582            SimpleTypeDerivationMethod::Restriction
583        };
584
585        Self {
586            name: Some(name),
587            target_namespace,
588            source: None,
589            variety,
590            derivation_method,
591            base_type: None, // Will be set during registry initialization
592            item_type: None, // Will be set for list types
593            member_types: Vec::new(),
594            facets: default_facets_for_builtin(builtin),
595            type_code,
596            primitive_type,
597            final_derivation: DerivationSet::empty(),
598            id: None,
599        }
600    }
601
602    /// Check if this is an anonymous type
603    pub fn is_anonymous(&self) -> bool {
604        self.name.is_none()
605    }
606
607    /// Check if this is a global (named) type
608    pub fn is_global(&self) -> bool {
609        self.name.is_some()
610    }
611
612    /// Check if this is an atomic type
613    pub fn is_atomic(&self) -> bool {
614        self.variety == SimpleTypeVariety::Atomic
615    }
616
617    /// Check if this is a list type
618    pub fn is_list(&self) -> bool {
619        self.variety == SimpleTypeVariety::List
620    }
621
622    /// Check if this is a union type
623    pub fn is_union(&self) -> bool {
624        self.variety == SimpleTypeVariety::Union
625    }
626
627    /// Check if this type was derived by restriction
628    pub fn is_restriction(&self) -> bool {
629        self.derivation_method == SimpleTypeDerivationMethod::Restriction
630    }
631
632    /// Get the primitive type code for this simple type
633    ///
634    /// Returns the primitive type from which this type ultimately derives.
635    /// Returns `None` for list types, union types, and abstract types.
636    pub fn get_primitive_type(&self) -> Option<PrimitiveTypeCode> {
637        self.primitive_type
638    }
639
640    /// Get the type code for this simple type
641    pub fn get_type_code(&self) -> XmlTypeCode {
642        self.type_code
643    }
644
645    /// Get the TypeKey for this simple type (requires its key)
646    pub fn type_key(&self, key: SimpleTypeKey) -> TypeKey {
647        TypeKey::Simple(key)
648    }
649}
650
651/// Default facets for built-in types — delegates to
652/// [`effective_arena_facets_for_builtin`] to keep the parser-frame
653/// `SimpleTypeDef` and the arena `SimpleTypeDefData` paths in sync.
654pub fn default_facets_for_builtin(builtin: BuiltInType) -> FacetSet {
655    effective_arena_facets_for_builtin(builtin)
656}
657
658/// Effective {facets} for a built-in type after walking the entire derivation
659/// chain, per XSD Datatypes Part 2. Bakes whiteSpace, integer-hierarchy
660/// fractionDigits=0, bounded-integer min/maxInclusive, and list minLength=1
661/// into the arena's `SimpleTypeDefData.facets` so user-derived types see
662/// the proper inherited facets through `merge_with_base`. The runtime
663/// equivalent for user-derived types is `validation::simple::collect_facets`.
664pub fn effective_arena_facets_for_builtin(builtin: BuiltInType) -> FacetSet {
665    use super::facets::{FacetFixed, WhitespaceMode};
666
667    let mut facets = FacetSet::new();
668
669    // String/NormalizedString leave whiteSpace unfixed so descendants can
670    // tighten preserve→replace→collapse; AnySimpleType/AnyAtomicType/XsError
671    // omit it entirely (cos-applicable-facets rejects whiteSpace on unions).
672    match builtin {
673        BuiltInType::String => {
674            facets.set_whitespace(WhitespaceMode::Preserve, FacetFixed::Default, None);
675        }
676        BuiltInType::NormalizedString => {
677            facets.set_whitespace(WhitespaceMode::Replace, FacetFixed::Default, None);
678        }
679        BuiltInType::AnySimpleType | BuiltInType::AnyAtomicType | BuiltInType::XsError => {}
680        _ => {
681            facets.set_whitespace(WhitespaceMode::Collapse, FacetFixed::Fixed, None);
682        }
683    }
684
685    if let Some((lo, hi)) = integer_hierarchy_bounds(builtin) {
686        facets.set_fraction_digits(0, FacetFixed::Fixed, None);
687        if let Some(v) = lo {
688            facets.set_min_inclusive(v, FacetFixed::Default, None);
689        }
690        if let Some(v) = hi {
691            facets.set_max_inclusive(v, FacetFixed::Default, None);
692        }
693    }
694
695    if matches!(
696        builtin,
697        BuiltInType::IDREFS | BuiltInType::NMTOKENS | BuiltInType::ENTITIES
698    ) {
699        facets.set_min_length(1, FacetFixed::Default, None);
700    }
701
702    facets
703}
704
705/// Returns `Some((minInclusive?, maxInclusive?))` if `builtin` is in the
706/// xs:integer derivation chain, `None` otherwise. Bounds derive from the
707/// std numeric constants so they cannot drift from the underlying type.
708fn integer_hierarchy_bounds(builtin: BuiltInType) -> Option<(Option<String>, Option<String>)> {
709    let bounds = match builtin {
710        BuiltInType::Integer => (None, None),
711        BuiltInType::NonPositiveInteger => (None, Some("0".to_string())),
712        BuiltInType::NegativeInteger => (None, Some("-1".to_string())),
713        BuiltInType::NonNegativeInteger => (Some("0".to_string()), None),
714        BuiltInType::PositiveInteger => (Some("1".to_string()), None),
715        BuiltInType::Long => (Some(i64::MIN.to_string()), Some(i64::MAX.to_string())),
716        BuiltInType::Int => (Some(i32::MIN.to_string()), Some(i32::MAX.to_string())),
717        BuiltInType::Short => (Some(i16::MIN.to_string()), Some(i16::MAX.to_string())),
718        BuiltInType::Byte => (Some(i8::MIN.to_string()), Some(i8::MAX.to_string())),
719        BuiltInType::UnsignedLong => (Some("0".to_string()), Some(u64::MAX.to_string())),
720        BuiltInType::UnsignedInt => (Some("0".to_string()), Some(u32::MAX.to_string())),
721        BuiltInType::UnsignedShort => (Some("0".to_string()), Some(u16::MAX.to_string())),
722        BuiltInType::UnsignedByte => (Some("0".to_string()), Some(u8::MAX.to_string())),
723        _ => return None,
724    };
725    Some(bounds)
726}
727
728#[cfg(test)]
729mod tests {
730    use super::*;
731
732    #[test]
733    fn test_builtin_type_names() {
734        assert_eq!(BuiltInType::String.local_name(), "string");
735        assert_eq!(BuiltInType::Integer.local_name(), "integer");
736        assert_eq!(BuiltInType::DateTime.local_name(), "dateTime");
737        assert_eq!(BuiltInType::AnyURI.local_name(), "anyURI");
738        assert_eq!(BuiltInType::NMTOKEN.local_name(), "NMTOKEN");
739        assert_eq!(BuiltInType::UntypedAtomic.local_name(), "untypedAtomic");
740        assert_eq!(
741            BuiltInType::YearMonthDuration.local_name(),
742            "yearMonthDuration"
743        );
744    }
745
746    #[test]
747    fn test_builtin_type_parsing() {
748        assert_eq!(
749            BuiltInType::from_local_name("string"),
750            Some(BuiltInType::String)
751        );
752        assert_eq!(
753            BuiltInType::from_local_name("integer"),
754            Some(BuiltInType::Integer)
755        );
756        assert_eq!(
757            BuiltInType::from_local_name("untypedAtomic"),
758            Some(BuiltInType::UntypedAtomic)
759        );
760        assert_eq!(
761            BuiltInType::from_local_name("yearMonthDuration"),
762            Some(BuiltInType::YearMonthDuration)
763        );
764        assert_eq!(BuiltInType::from_local_name("nonExistent"), None);
765        // anyType is not a simple type
766        assert_eq!(BuiltInType::from_local_name("anyType"), None);
767    }
768
769    #[test]
770    fn test_builtin_is_primitive() {
771        assert!(BuiltInType::String.is_primitive());
772        assert!(BuiltInType::Decimal.is_primitive());
773        assert!(BuiltInType::Duration.is_primitive());
774        assert!(!BuiltInType::Integer.is_primitive()); // Derived from decimal
775        assert!(!BuiltInType::NCName.is_primitive()); // Derived from Name
776        assert!(!BuiltInType::AnySimpleType.is_primitive()); // Abstract
777        assert!(!BuiltInType::NMTOKENS.is_primitive()); // List type
778    }
779
780    #[test]
781    fn test_builtin_is_list() {
782        assert!(BuiltInType::NMTOKENS.is_list());
783        assert!(BuiltInType::IDREFS.is_list());
784        assert!(BuiltInType::ENTITIES.is_list());
785        assert!(!BuiltInType::NMTOKEN.is_list());
786        assert!(!BuiltInType::String.is_list());
787    }
788
789    #[test]
790    fn test_builtin_is_xsd11() {
791        assert!(BuiltInType::AnyAtomicType.is_xsd11());
792        assert!(BuiltInType::UntypedAtomic.is_xsd11());
793        assert!(BuiltInType::YearMonthDuration.is_xsd11());
794        assert!(BuiltInType::DayTimeDuration.is_xsd11());
795        assert!(BuiltInType::DateTimeStamp.is_xsd11());
796        assert!(!BuiltInType::String.is_xsd11());
797        assert!(!BuiltInType::DateTime.is_xsd11());
798    }
799
800    #[test]
801    fn test_builtin_type_code() {
802        assert_eq!(BuiltInType::String.type_code(), XmlTypeCode::String);
803        assert_eq!(BuiltInType::Integer.type_code(), XmlTypeCode::Integer);
804        assert_eq!(BuiltInType::NMTOKEN.type_code(), XmlTypeCode::NmToken);
805        assert_eq!(BuiltInType::AnyURI.type_code(), XmlTypeCode::AnyUri);
806    }
807
808    #[test]
809    fn test_builtin_primitive_type_code() {
810        assert_eq!(
811            BuiltInType::String.primitive_type_code(),
812            Some(PrimitiveTypeCode::String)
813        );
814        assert_eq!(
815            BuiltInType::NCName.primitive_type_code(),
816            Some(PrimitiveTypeCode::String)
817        );
818        assert_eq!(
819            BuiltInType::Integer.primitive_type_code(),
820            Some(PrimitiveTypeCode::Decimal)
821        );
822        assert_eq!(
823            BuiltInType::Duration.primitive_type_code(),
824            Some(PrimitiveTypeCode::Duration)
825        );
826        assert_eq!(
827            BuiltInType::YearMonthDuration.primitive_type_code(),
828            Some(PrimitiveTypeCode::Duration)
829        );
830        // Abstract types and list types have no primitive
831        assert_eq!(BuiltInType::AnySimpleType.primitive_type_code(), None);
832        assert_eq!(BuiltInType::NMTOKENS.primitive_type_code(), None);
833    }
834
835    #[test]
836    fn test_builtin_to_xml_type_code_conversion() {
837        // Test From<BuiltInType> for XmlTypeCode
838        assert_eq!(XmlTypeCode::from(BuiltInType::String), XmlTypeCode::String);
839        assert_eq!(
840            XmlTypeCode::from(BuiltInType::NOTATION),
841            XmlTypeCode::Notation
842        );
843        assert_eq!(XmlTypeCode::from(BuiltInType::AnyURI), XmlTypeCode::AnyUri);
844        assert_eq!(
845            XmlTypeCode::from(BuiltInType::NMTOKEN),
846            XmlTypeCode::NmToken
847        );
848    }
849
850    #[test]
851    fn test_xml_type_code_to_builtin_conversion() {
852        // Test TryFrom<XmlTypeCode> for BuiltInType
853        assert_eq!(
854            BuiltInType::try_from(XmlTypeCode::String),
855            Ok(BuiltInType::String)
856        );
857        assert_eq!(
858            BuiltInType::try_from(XmlTypeCode::Notation),
859            Ok(BuiltInType::NOTATION)
860        );
861        assert_eq!(
862            BuiltInType::try_from(XmlTypeCode::AnyUri),
863            Ok(BuiltInType::AnyURI)
864        );
865        assert_eq!(
866            BuiltInType::try_from(XmlTypeCode::NmToken),
867            Ok(BuiltInType::NMTOKEN)
868        );
869
870        // Node types and AnyType should fail
871        assert!(BuiltInType::try_from(XmlTypeCode::None).is_err());
872        assert!(BuiltInType::try_from(XmlTypeCode::Element).is_err());
873        assert!(BuiltInType::try_from(XmlTypeCode::AnyType).is_err());
874    }
875
876    #[test]
877    fn test_builtin_roundtrip_conversion() {
878        // All BuiltInTypes should roundtrip through XmlTypeCode
879        for builtin in BuiltInType::all() {
880            let code = XmlTypeCode::from(builtin);
881            let back = BuiltInType::try_from(code).expect("Should convert back");
882            assert_eq!(back, builtin, "Roundtrip failed for {:?}", builtin);
883        }
884    }
885
886    #[test]
887    fn test_builtin_all_count() {
888        assert_eq!(BuiltInType::all().count(), 51);
889    }
890
891    #[test]
892    fn test_simple_type_restriction() {
893        let st = SimpleTypeDef::new_restriction(
894            Some(NameId(1)),
895            Some(NameId(2)),
896            SimpleTypeRef::BuiltIn(BuiltInType::String),
897        );
898
899        assert_eq!(st.variety, SimpleTypeVariety::Atomic);
900        assert_eq!(
901            st.derivation_method,
902            SimpleTypeDerivationMethod::Restriction
903        );
904        assert!(st.is_global());
905        assert!(st.is_atomic());
906        assert!(st.is_restriction());
907        assert!(st.base_type.is_some());
908        assert_eq!(st.type_code, XmlTypeCode::None); // Not set by default
909        assert!(st.primitive_type.is_none()); // Not set by default
910    }
911
912    #[test]
913    fn test_simple_type_list() {
914        let st = SimpleTypeDef::new_list(None, None, SimpleTypeRef::BuiltIn(BuiltInType::Integer));
915
916        assert_eq!(st.variety, SimpleTypeVariety::List);
917        assert_eq!(st.derivation_method, SimpleTypeDerivationMethod::List);
918        assert!(st.is_anonymous());
919        assert!(st.is_list());
920        assert!(st.item_type.is_some());
921        assert!(st.primitive_type.is_none()); // List types have no primitive
922    }
923
924    #[test]
925    fn test_simple_type_union() {
926        let st = SimpleTypeDef::new_union(
927            Some(NameId(1)),
928            None,
929            vec![
930                SimpleTypeRef::BuiltIn(BuiltInType::String),
931                SimpleTypeRef::BuiltIn(BuiltInType::Integer),
932            ],
933        );
934
935        assert_eq!(st.variety, SimpleTypeVariety::Union);
936        assert_eq!(st.derivation_method, SimpleTypeDerivationMethod::Union);
937        assert!(st.is_union());
938        assert_eq!(st.member_types.len(), 2);
939        assert!(st.primitive_type.is_none()); // Union types have no primitive
940    }
941
942    #[test]
943    fn test_simple_type_builtin_atomic() {
944        let st = SimpleTypeDef::new_builtin(NameId(1), Some(NameId(2)), BuiltInType::String);
945
946        assert_eq!(st.variety, SimpleTypeVariety::Atomic);
947        assert_eq!(
948            st.derivation_method,
949            SimpleTypeDerivationMethod::Restriction
950        );
951        assert!(st.is_atomic());
952        assert_eq!(st.type_code, XmlTypeCode::String);
953        assert_eq!(st.primitive_type, Some(PrimitiveTypeCode::String));
954    }
955
956    #[test]
957    fn test_simple_type_builtin_list() {
958        let st = SimpleTypeDef::new_builtin(NameId(1), Some(NameId(2)), BuiltInType::NMTOKENS);
959
960        assert_eq!(st.variety, SimpleTypeVariety::List);
961        assert_eq!(st.derivation_method, SimpleTypeDerivationMethod::List);
962        assert!(st.is_list());
963        assert_eq!(st.type_code, XmlTypeCode::NmTokens);
964        assert!(st.primitive_type.is_none()); // List types have no primitive
965    }
966
967    #[test]
968    fn test_simple_type_builtin_derived() {
969        // Test a derived type like integer (derived from decimal)
970        let st = SimpleTypeDef::new_builtin(NameId(1), Some(NameId(2)), BuiltInType::Integer);
971
972        assert_eq!(st.type_code, XmlTypeCode::Integer);
973        assert_eq!(st.primitive_type, Some(PrimitiveTypeCode::Decimal));
974    }
975
976    #[test]
977    fn test_simple_type_derivation_method_default() {
978        assert_eq!(
979            SimpleTypeDerivationMethod::default(),
980            SimpleTypeDerivationMethod::Restriction
981        );
982    }
983
984    #[test]
985    fn test_simple_type_variety_copy() {
986        let variety = SimpleTypeVariety::Atomic;
987        let copy = variety;
988        assert_eq!(variety, copy);
989    }
990
991    #[test]
992    fn test_default_facets() {
993        let string_facets = default_facets_for_builtin(BuiltInType::String);
994        assert!(string_facets.whitespace.is_some());
995
996        let int_facets = default_facets_for_builtin(BuiltInType::Integer);
997        assert!(int_facets.whitespace.is_some());
998    }
999
1000    #[test]
1001    fn test_simple_type_helper_methods() {
1002        let restriction = SimpleTypeDef::new_restriction(
1003            Some(NameId(1)),
1004            None,
1005            SimpleTypeRef::BuiltIn(BuiltInType::String),
1006        );
1007        assert!(restriction.is_atomic());
1008        assert!(!restriction.is_list());
1009        assert!(!restriction.is_union());
1010        assert!(restriction.is_restriction());
1011
1012        let list = SimpleTypeDef::new_list(None, None, SimpleTypeRef::BuiltIn(BuiltInType::String));
1013        assert!(!list.is_atomic());
1014        assert!(list.is_list());
1015        assert!(!list.is_union());
1016
1017        let union = SimpleTypeDef::new_union(
1018            None,
1019            None,
1020            vec![SimpleTypeRef::BuiltIn(BuiltInType::String)],
1021        );
1022        assert!(!union.is_atomic());
1023        assert!(!union.is_list());
1024        assert!(union.is_union());
1025    }
1026
1027    #[test]
1028    fn test_simple_type_get_type_code() {
1029        let st = SimpleTypeDef::new_builtin(NameId(1), None, BuiltInType::DateTime);
1030        assert_eq!(st.get_type_code(), XmlTypeCode::DateTime);
1031    }
1032
1033    #[test]
1034    fn test_simple_type_get_primitive_type() {
1035        let st = SimpleTypeDef::new_builtin(NameId(1), None, BuiltInType::NCName);
1036        assert_eq!(st.get_primitive_type(), Some(PrimitiveTypeCode::String));
1037
1038        let list_st = SimpleTypeDef::new_builtin(NameId(2), None, BuiltInType::IDREFS);
1039        assert_eq!(list_st.get_primitive_type(), None);
1040    }
1041}