Skip to main content

libxml_rs/xml/schemas/
mod.rs

1//! XML Schema implementation (§27, §85 Phase 6).
2//!
3//! XML Schema (W3C XSD) validation and datatype machinery. libxml2's schema
4//! support is an UPSTREAM_EXTENSION known to deviate from the standard in
5//! places — parity follows the oracle.
6//!
7//! Phase 6: Complete — schema parsing, datatype validation, document validation,
8//! and C ABI exports are implemented.
9//!
10//! # UPSTREAM-PARITY
11//!
12//! This module implements a simplified but functional XSD validator that
13//! follows libxml2's observable behavior for the most common patterns.
14//! Deviations from the W3C specification that match libxml2 are intentional.
15
16use core::ffi::c_void;
17use core::ptr;
18use std::collections::HashMap;
19use std::os::raw::{c_char, c_int};
20
21use crate::abi::allocator;
22use crate::abi::structs::*;
23use crate::abi::types::xmlElementType::*;
24use crate::abi::types::*;
25
26// ═══════════════════════════════════════════════════════════════════════════════
27// XSD Component Types
28// ═══════════════════════════════════════════════════════════════════════════════
29
30/// XSD component types — mirrors libxml2's schema component classification.
31///
32/// # UPSTREAM-PARITY
33///
34/// libxml2 defines these as `xmlSchemaTypeType` in `include/schemas/internals.h`.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum XsdComponentType {
37    Schema,
38    Element,
39    Attribute,
40    ComplexType,
41    SimpleType,
42    SimpleContent,
43    ComplexContent,
44    Sequence,
45    Choice,
46    All,
47    Restriction,
48    Extension,
49    List,
50    Union,
51    Annotation,
52    Any,
53    AnyAttribute,
54    Group,
55    AttributeGroup,
56    Notation,
57    Unique,
58    Key,
59    KeyRef,
60    Selector,
61    Field,
62}
63
64// ═══════════════════════════════════════════════════════════════════════════════
65// XSD Datatype Kinds
66// ═══════════════════════════════════════════════════════════════════════════════
67
68/// XSD datatype kinds — covers all built-in types and facets.
69///
70/// # UPSTREAM-PARITY
71///
72/// libxml2 defines these as `xmlSchemaTypeType` built-in type constants
73/// in `include/schemas/internals.h`.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub enum XsdDatatypeKind {
76    // Primitive types
77    String,
78    Boolean,
79    Decimal,
80    Float,
81    Double,
82    Duration,
83    DateTime,
84    Time,
85    Date,
86    GYearMonth,
87    GYear,
88    GMonthDay,
89    GDay,
90    GMonth,
91    HexBinary,
92    Base64Binary,
93    AnyURI,
94    QName,
95    Notation,
96    // Derived string types
97    NormalizedString,
98    Token,
99    Language,
100    Nmtoken,
101    Nmtokens,
102    Name,
103    NCName,
104    Id,
105    Idref,
106    Idrefs,
107    Entity,
108    Entities,
109    // Numeric derived types
110    Integer,
111    NonPositiveInteger,
112    NegativeInteger,
113    Long,
114    Int,
115    Short,
116    Byte,
117    NonNegativeInteger,
118    UnsignedLong,
119    UnsignedInt,
120    UnsignedShort,
121    UnsignedByte,
122    PositiveInteger,
123    // Facet types (used internally)
124    FacetPattern,
125    FacetEnumeration,
126    FacetMinInclusive,
127    FacetMaxInclusive,
128    FacetMinExclusive,
129    FacetMaxExclusive,
130    FacetMinLength,
131    FacetMaxLength,
132    FacetLength,
133    FacetWhiteSpace,
134    FacetFractionDigits,
135    FacetTotalDigits,
136}
137
138// ═══════════════════════════════════════════════════════════════════════════════
139// XSD Component
140// ═══════════════════════════════════════════════════════════════════════════════
141
142/// An XSD schema component declaration.
143///
144/// Represents any XSD component (element, attribute, type, model group, etc.).
145/// Components form a tree via the `children` and `attributes` vectors.
146#[derive(Debug, Clone)]
147pub struct XsdComponent {
148    pub component_type: XsdComponentType,
149    pub name: Option<String>,
150    pub target_namespace: Option<String>,
151    pub children: Vec<XsdComponent>,
152    pub attributes: Vec<XsdComponent>,
153    pub datatype: Option<XsdDatatypeKind>,
154    pub facets: Vec<(XsdDatatypeKind, String)>,
155    pub base: Option<String>,
156    pub min_occurs: i32,
157    pub max_occurs: i32, // -1 for unbounded
158    pub ref_name: Option<String>,
159    pub substitution_group: Option<String>,
160    pub is_abstract: bool,
161    pub is_final: bool,
162    pub block: Vec<String>,
163    pub mixed: bool,
164    pub form: Option<String>,
165}
166
167impl XsdComponent {
168    pub fn new(component_type: XsdComponentType) -> Self {
169        Self {
170            component_type,
171            name: None,
172            target_namespace: None,
173            children: Vec::new(),
174            attributes: Vec::new(),
175            datatype: None,
176            facets: Vec::new(),
177            base: None,
178            min_occurs: 1,
179            max_occurs: 1,
180            ref_name: None,
181            substitution_group: None,
182            is_abstract: false,
183            is_final: false,
184            block: Vec::new(),
185            mixed: false,
186            form: None,
187        }
188    }
189}
190
191// ═══════════════════════════════════════════════════════════════════════════════
192// XSD Schema
193// ═══════════════════════════════════════════════════════════════════════════════
194
195/// A compiled XSD schema.
196///
197/// Holds the top-level component declarations and schema-level settings.
198#[derive(Debug, Clone)]
199pub struct XsdSchema {
200    pub components: Vec<XsdComponent>,
201    pub target_namespace: Option<String>,
202    pub element_form_default: Option<String>,
203    pub attribute_form_default: Option<String>,
204    pub errors: Vec<String>,
205}
206
207impl XsdSchema {
208    pub fn new() -> Self {
209        Self {
210            components: Vec::new(),
211            target_namespace: None,
212            element_form_default: None,
213            attribute_form_default: None,
214            errors: Vec::new(),
215        }
216    }
217}
218
219impl Default for XsdSchema {
220    fn default() -> Self {
221        Self::new()
222    }
223}
224
225// ═══════════════════════════════════════════════════════════════════════════════
226// XSD Validation Context
227// ═══════════════════════════════════════════════════════════════════════════════
228
229/// Validation context for XSD schema validation.
230///
231/// Tracks errors and state during validation of an XML document against
232/// a schema. Mirrors libxml2's `xmlSchemaValidCtxt`.
233#[derive(Debug)]
234pub struct XsdValidCtxt {
235    pub schema: Option<XsdSchema>,
236    pub errors: Vec<String>,
237    pub nb_errors: i32,
238}
239
240impl XsdValidCtxt {
241    pub fn new() -> Self {
242        Self {
243            schema: None,
244            errors: Vec::new(),
245            nb_errors: 0,
246        }
247    }
248}
249
250impl Default for XsdValidCtxt {
251    fn default() -> Self {
252        Self::new()
253    }
254}
255
256// ═══════════════════════════════════════════════════════════════════════════════
257// Internal helpers for schema parsing
258// ═══════════════════════════════════════════════════════════════════════════════
259
260/// Get the text content of an xmlNode (recursively collects text children).
261///
262/// # SAFETY
263///
264/// - `node` must be a valid pointer to an _xmlNode or NULL.
265unsafe fn get_node_text(node: *mut _xmlNode) -> String {
266    if node.is_null() {
267        return String::new();
268    }
269    let mut result = String::new();
270    unsafe {
271        let mut child = (*node).children;
272        while !child.is_null() {
273            if (*child).type_ == XML_TEXT_NODE as c_int
274                || (*child).type_ == XML_CDATA_SECTION_NODE as c_int
275            {
276                if !(*child).content.is_null() {
277                    let content = (*child).content;
278                    let mut len = 0;
279                    while *content.add(len) != 0 {
280                        len += 1;
281                    }
282                    let slice = std::slice::from_raw_parts(content, len);
283                    result.push_str(&String::from_utf8_lossy(slice));
284                }
285            }
286            child = (*child).next;
287        }
288    }
289    result
290}
291
292/// Get an attribute value from an xmlNode.
293///
294/// # SAFETY
295///
296/// - `node` must be a valid pointer to an _xmlNode or NULL.
297unsafe fn get_attr(node: *mut _xmlNode, name: &str) -> Option<String> {
298    if node.is_null() {
299        return None;
300    }
301    unsafe {
302        let mut prop = (*node).properties;
303        while !prop.is_null() {
304            let prop_name = (*prop).name;
305            if !prop_name.is_null() {
306                let mut len = 0;
307                while *prop_name.add(len) != 0 {
308                    len += 1;
309                }
310                let slice = std::slice::from_raw_parts(prop_name, len);
311                if let Ok(s) = std::str::from_utf8(slice) {
312                    if s == name {
313                        return Some(get_node_text(prop as *mut _xmlNode));
314                    }
315                }
316            }
317            prop = (*prop).next;
318        }
319    }
320    None
321}
322
323/// Get an attribute value as a boolean.
324///
325/// # SAFETY
326///
327/// - `node` must be a valid pointer to an _xmlNode or NULL.
328unsafe fn get_attr_bool(node: *mut _xmlNode, name: &str) -> bool {
329    unsafe {
330        match get_attr(node, name) {
331            Some(v) => v == "true" || v == "1",
332            None => false,
333        }
334    }
335}
336
337/// Get an attribute value as an integer with a default.
338///
339/// # SAFETY
340///
341/// - `node` must be a valid pointer to an _xmlNode or NULL.
342unsafe fn get_attr_int(node: *mut _xmlNode, name: &str, default: i32) -> i32 {
343    unsafe {
344        match get_attr(node, name) {
345            Some(v) => v.parse::<i32>().unwrap_or(default),
346            None => default,
347        }
348    }
349}
350
351/// Get an attribute value as an unbounded integer (-1 for "unbounded").
352///
353/// # SAFETY
354///
355/// - `node` must be a valid pointer to an _xmlNode or NULL.
356unsafe fn get_attr_occurs(node: *mut _xmlNode, name: &str, default: i32) -> i32 {
357    unsafe {
358        match get_attr(node, name) {
359            Some(v) => {
360                if v == "unbounded" {
361                    -1
362                } else {
363                    v.parse::<i32>().unwrap_or(default)
364                }
365            }
366            None => default,
367        }
368    }
369}
370
371/// Check if an xmlNode is an element with a given local name.
372///
373/// # SAFETY
374///
375/// - `node` must be a valid pointer to an _xmlNode or NULL.
376unsafe fn node_is(node: *mut _xmlNode, local_name: &str) -> bool {
377    if node.is_null() {
378        return false;
379    }
380    unsafe {
381        let name = (*node).name;
382        if name.is_null() {
383            return false;
384        }
385        let mut len = 0;
386        while *name.add(len) != 0 {
387            len += 1;
388        }
389        let slice = std::slice::from_raw_parts(name, len);
390        if let Ok(s) = std::str::from_utf8(slice) {
391            // Strip namespace prefix if present
392            let local = if let Some(pos) = s.find(':') {
393                &s[pos + 1..]
394            } else {
395                s
396            };
397            return local == local_name;
398        }
399    }
400    false
401}
402
403/// Parse a datatype kind from a QName string (e.g., "xs:string", "string").
404fn parse_datatype_kind(name: &str) -> Option<XsdDatatypeKind> {
405    // Strip XML Schema namespace prefix if present
406    let local = if let Some(pos) = name.find(':') {
407        &name[pos + 1..]
408    } else {
409        name
410    };
411
412    match local {
413        "string" => Some(XsdDatatypeKind::String),
414        "boolean" => Some(XsdDatatypeKind::Boolean),
415        "decimal" => Some(XsdDatatypeKind::Decimal),
416        "float" => Some(XsdDatatypeKind::Float),
417        "double" => Some(XsdDatatypeKind::Double),
418        "duration" => Some(XsdDatatypeKind::Duration),
419        "dateTime" => Some(XsdDatatypeKind::DateTime),
420        "time" => Some(XsdDatatypeKind::Time),
421        "date" => Some(XsdDatatypeKind::Date),
422        "gYearMonth" => Some(XsdDatatypeKind::GYearMonth),
423        "gYear" => Some(XsdDatatypeKind::GYear),
424        "gMonthDay" => Some(XsdDatatypeKind::GMonthDay),
425        "gDay" => Some(XsdDatatypeKind::GDay),
426        "gMonth" => Some(XsdDatatypeKind::GMonth),
427        "hexBinary" => Some(XsdDatatypeKind::HexBinary),
428        "base64Binary" => Some(XsdDatatypeKind::Base64Binary),
429        "anyURI" => Some(XsdDatatypeKind::AnyURI),
430        "QName" => Some(XsdDatatypeKind::QName),
431        "NOTATION" => Some(XsdDatatypeKind::Notation),
432        "normalizedString" => Some(XsdDatatypeKind::NormalizedString),
433        "token" => Some(XsdDatatypeKind::Token),
434        "language" => Some(XsdDatatypeKind::Language),
435        "NMTOKEN" => Some(XsdDatatypeKind::Nmtoken),
436        "NMTOKENS" => Some(XsdDatatypeKind::Nmtokens),
437        "Name" => Some(XsdDatatypeKind::Name),
438        "NCName" => Some(XsdDatatypeKind::NCName),
439        "ID" => Some(XsdDatatypeKind::Id),
440        "IDREF" => Some(XsdDatatypeKind::Idref),
441        "IDREFS" => Some(XsdDatatypeKind::Idrefs),
442        "ENTITY" => Some(XsdDatatypeKind::Entity),
443        "ENTITIES" => Some(XsdDatatypeKind::Entities),
444        "integer" => Some(XsdDatatypeKind::Integer),
445        "nonPositiveInteger" => Some(XsdDatatypeKind::NonPositiveInteger),
446        "negativeInteger" => Some(XsdDatatypeKind::NegativeInteger),
447        "long" => Some(XsdDatatypeKind::Long),
448        "int" => Some(XsdDatatypeKind::Int),
449        "short" => Some(XsdDatatypeKind::Short),
450        "byte" => Some(XsdDatatypeKind::Byte),
451        "nonNegativeInteger" => Some(XsdDatatypeKind::NonNegativeInteger),
452        "unsignedLong" => Some(XsdDatatypeKind::UnsignedLong),
453        "unsignedInt" => Some(XsdDatatypeKind::UnsignedInt),
454        "unsignedShort" => Some(XsdDatatypeKind::UnsignedShort),
455        "unsignedByte" => Some(XsdDatatypeKind::UnsignedByte),
456        "positiveInteger" => Some(XsdDatatypeKind::PositiveInteger),
457        _ => None,
458    }
459}
460
461/// Parse a facet kind from an XSD element name.
462fn parse_facet_kind(name: &str) -> Option<XsdDatatypeKind> {
463    match name {
464        "pattern" => Some(XsdDatatypeKind::FacetPattern),
465        "enumeration" => Some(XsdDatatypeKind::FacetEnumeration),
466        "minInclusive" => Some(XsdDatatypeKind::FacetMinInclusive),
467        "maxInclusive" => Some(XsdDatatypeKind::FacetMaxInclusive),
468        "minExclusive" => Some(XsdDatatypeKind::FacetMinExclusive),
469        "maxExclusive" => Some(XsdDatatypeKind::FacetMaxExclusive),
470        "minLength" => Some(XsdDatatypeKind::FacetMinLength),
471        "maxLength" => Some(XsdDatatypeKind::FacetMaxLength),
472        "length" => Some(XsdDatatypeKind::FacetLength),
473        "whiteSpace" => Some(XsdDatatypeKind::FacetWhiteSpace),
474        "fractionDigits" => Some(XsdDatatypeKind::FacetFractionDigits),
475        "totalDigits" => Some(XsdDatatypeKind::FacetTotalDigits),
476        _ => None,
477    }
478}
479
480// ═══════════════════════════════════════════════════════════════════════════════
481// Schema Parsing
482// ═══════════════════════════════════════════════════════════════════════════════
483
484/// Parse an XSD schema from an XML string.
485///
486/// # UPSTREAM-PARITY
487///
488/// Equivalent to `xmlSchemaParse` in libxml2.
489///
490/// Returns the parsed schema, or an error message on failure.
491pub fn xsd_parse(xml_doc: &str) -> Result<XsdSchema, String> {
492    // Use the XML parser to parse the schema document
493    let doc_ptr = unsafe {
494        crate::abi::exports_xml2::xmlReadMemory(
495            xml_doc.as_ptr() as *const c_char,
496            xml_doc.len() as c_int,
497            b"schema.xsd\0".as_ptr() as *const c_char,
498            ptr::null(),
499            0,
500        )
501    };
502
503    if doc_ptr.is_null() {
504        return Err("Failed to parse schema XML document".to_string());
505    }
506
507    let result = unsafe { xsd_parse_schema_doc(doc_ptr) };
508    unsafe {
509        crate::abi::exports_xml2::xmlFreeDoc(doc_ptr);
510    }
511    result
512}
513
514/// Parse an XSD schema from a parsed XML document.
515///
516/// # SAFETY
517///
518/// - `doc` must be a valid pointer to an _xmlDoc representing an XSD schema.
519unsafe fn xsd_parse_schema_doc(doc: *mut _xmlDoc) -> Result<XsdSchema, String> {
520    unsafe {
521        let root = (*doc).children;
522        if root.is_null() {
523            return Err("Schema document has no root element".to_string());
524        }
525
526        // Find the root <schema> element
527        let mut schema_node = root;
528        while !schema_node.is_null() && !node_is(schema_node, "schema") {
529            schema_node = (*schema_node).next;
530        }
531
532        if schema_node.is_null() {
533            return Err("Schema document root is not <schema>".to_string());
534        }
535
536        Ok(xsd_parse_schema_node(schema_node))
537    }
538}
539
540/// Parse a <schema> element.
541///
542/// # SAFETY
543///
544/// - `node` must be a valid pointer to a <schema> element node.
545unsafe fn xsd_parse_schema_node(node: *mut _xmlNode) -> XsdSchema {
546    unsafe {
547        let mut schema = XsdSchema::new();
548        schema.target_namespace = get_attr(node, "targetNamespace");
549        schema.element_form_default = get_attr(node, "elementFormDefault");
550        schema.attribute_form_default = get_attr(node, "attributeFormDefault");
551
552        // Parse child components
553        let mut child = (*node).children;
554        while !child.is_null() {
555            if (*child).type_ == XML_ELEMENT_NODE as c_int {
556                let comp = xsd_parse_component(child, &schema);
557                if comp.component_type != XsdComponentType::Annotation {
558                    schema.components.push(comp);
559                }
560            }
561            child = (*child).next;
562        }
563
564        schema
565    }
566}
567
568/// Parse a single XSD component from an element node.
569///
570/// # SAFETY
571///
572/// - `node` must be a valid pointer to an XML element node.
573unsafe fn xsd_parse_component(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
574    unsafe {
575        // Determine the component type from the element name
576        let name_str = if !(*node).name.is_null() {
577            let mut len = 0;
578            while *(*node).name.add(len) != 0 {
579                len += 1;
580            }
581            let slice = std::slice::from_raw_parts((*node).name, len);
582            if let Ok(s) = std::str::from_utf8(slice) {
583                if let Some(pos) = s.find(':') {
584                    s[pos + 1..].to_string()
585                } else {
586                    s.to_string()
587                }
588            } else {
589                String::new()
590            }
591        } else {
592            String::new()
593        };
594
595        match name_str.as_str() {
596            "element" => xsd_parse_element(node, schema),
597            "attribute" => xsd_parse_attribute_node(node, schema),
598            "complexType" => xsd_parse_complex_type(node, schema),
599            "simpleType" => xsd_parse_simple_type(node, schema),
600            "sequence" => xsd_parse_model_group(node, XsdComponentType::Sequence, schema),
601            "choice" => xsd_parse_model_group(node, XsdComponentType::Choice, schema),
602            "all" => xsd_parse_model_group(node, XsdComponentType::All, schema),
603            "restriction" => xsd_parse_restriction(node, schema),
604            "extension" => xsd_parse_extension(node, schema),
605            "list" => xsd_parse_list(node, schema),
606            "union" => xsd_parse_union(node, schema),
607            "annotation" => xsd_parse_annotation(node),
608            "any" => xsd_parse_any(node, schema),
609            "anyAttribute" => {
610                let mut comp = XsdComponent::new(XsdComponentType::AnyAttribute);
611                comp
612            }
613            "group" => xsd_parse_group(node, schema),
614            "attributeGroup" => xsd_parse_attribute_group(node, schema),
615            "unique" => xsd_parse_identity_constraint(node, XsdComponentType::Unique, schema),
616            "key" => xsd_parse_identity_constraint(node, XsdComponentType::Key, schema),
617            "keyref" => xsd_parse_identity_constraint(node, XsdComponentType::KeyRef, schema),
618            // Facets
619            "pattern" | "enumeration" | "minInclusive" | "maxInclusive" | "minExclusive"
620            | "maxExclusive" | "minLength" | "maxLength" | "length" | "whiteSpace"
621            | "fractionDigits" | "totalDigits" => xsd_parse_facet(node),
622            // Simple content / complex content markers
623            "simpleContent" => xsd_parse_simple_content(node, schema),
624            "complexContent" => xsd_parse_complex_content(node, schema),
625            _ => {
626                // Unknown element — create a generic component
627                let mut comp = XsdComponent::new(XsdComponentType::Schema);
628                if let Ok(s) = std::str::from_utf8(std::slice::from_raw_parts((*node).name, {
629                    let mut len = 0;
630                    while *(*node).name.add(len) != 0 {
631                        len += 1;
632                    }
633                    len
634                })) {
635                    comp.name = Some(s.to_string());
636                }
637                comp
638            }
639        }
640    }
641}
642
643/// Parse an <element> declaration.
644///
645/// # SAFETY
646///
647/// - `node` must be a valid pointer to an <element> element node.
648unsafe fn xsd_parse_element(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
649    unsafe {
650        let mut comp = XsdComponent::new(XsdComponentType::Element);
651        comp.name = get_attr(node, "name");
652        comp.ref_name = get_attr(node, "ref");
653        comp.min_occurs = get_attr_occurs(node, "minOccurs", 1);
654        comp.max_occurs = get_attr_occurs(node, "maxOccurs", 1);
655        comp.is_abstract = get_attr_bool(node, "abstract");
656        comp.is_final = get_attr_bool(node, "final");
657        comp.substitution_group = get_attr(node, "substitutionGroup");
658        comp.form = get_attr(node, "form");
659
660        // Resolve type attribute
661        if let Some(type_name) = get_attr(node, "type") {
662            comp.datatype = parse_datatype_kind(&type_name);
663            // If it's not a built-in type, store the type name as base
664            if comp.datatype.is_none() {
665                comp.base = Some(type_name);
666            }
667        }
668
669        // Check for default/fixed value
670        let _default = get_attr(node, "default");
671        let _fixed = get_attr(node, "fixed");
672
673        // Parse child components (inline type definitions)
674        let mut child = (*node).children;
675        while !child.is_null() {
676            if (*child).type_ == XML_ELEMENT_NODE as c_int {
677                let child_comp = xsd_parse_component(child, schema);
678                match child_comp.component_type {
679                    XsdComponentType::ComplexType | XsdComponentType::SimpleType => {
680                        // Inline type definition
681                        if let Some(ref name) = child_comp.name {
682                            comp.base = Some(name.clone());
683                        }
684                        comp.children.push(child_comp);
685                    }
686                    XsdComponentType::Annotation => {
687                        // Skip annotations
688                    }
689                    _ => {
690                        comp.children.push(child_comp);
691                    }
692                }
693            }
694            child = (*child).next;
695        }
696
697        comp
698    }
699}
700
701/// Parse an <attribute> declaration.
702///
703/// # SAFETY
704///
705/// - `node` must be a valid pointer to an <attribute> element node.
706unsafe fn xsd_parse_attribute_node(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
707    unsafe {
708        let mut comp = XsdComponent::new(XsdComponentType::Attribute);
709        comp.name = get_attr(node, "name");
710        comp.ref_name = get_attr(node, "ref");
711        comp.form = get_attr(node, "form");
712
713        // Resolve type attribute
714        if let Some(type_name) = get_attr(node, "type") {
715            comp.datatype = parse_datatype_kind(&type_name);
716            if comp.datatype.is_none() {
717                comp.base = Some(type_name);
718            }
719        }
720
721        // Check for use attribute
722        let use_attr = get_attr(node, "use");
723        if let Some(ref use_val) = use_attr {
724            if use_val == "required" {
725                comp.min_occurs = 1;
726            } else if use_val == "prohibited" {
727                comp.min_occurs = 0;
728                comp.max_occurs = 0;
729            } else {
730                // optional
731                comp.min_occurs = 0;
732            }
733        } else {
734            comp.min_occurs = 0; // optional by default
735        }
736
737        let _default = get_attr(node, "default");
738        let _fixed = get_attr(node, "fixed");
739
740        // Parse child components (inline simpleType)
741        let mut child = (*node).children;
742        while !child.is_null() {
743            if (*child).type_ == XML_ELEMENT_NODE as c_int {
744                let child_comp = xsd_parse_component(child, schema);
745                match child_comp.component_type {
746                    XsdComponentType::SimpleType => {
747                        if let Some(ref name) = child_comp.name {
748                            comp.base = Some(name.clone());
749                        }
750                        comp.children.push(child_comp);
751                    }
752                    _ => {}
753                }
754            }
755            child = (*child).next;
756        }
757
758        comp
759    }
760}
761
762/// Parse a <complexType> definition.
763///
764/// # SAFETY
765///
766/// - `node` must be a valid pointer to a <complexType> element node.
767unsafe fn xsd_parse_complex_type(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
768    unsafe {
769        let mut comp = XsdComponent::new(XsdComponentType::ComplexType);
770        comp.name = get_attr(node, "name");
771        comp.mixed = get_attr_bool(node, "mixed");
772        comp.is_abstract = get_attr_bool(node, "abstract");
773        comp.is_final = get_attr_bool(node, "final");
774
775        // Parse child components
776        let mut child = (*node).children;
777        while !child.is_null() {
778            if (*child).type_ == XML_ELEMENT_NODE as c_int {
779                let child_comp = xsd_parse_component(child, schema);
780                match child_comp.component_type {
781                    XsdComponentType::SimpleContent
782                    | XsdComponentType::ComplexContent
783                    | XsdComponentType::Sequence
784                    | XsdComponentType::Choice
785                    | XsdComponentType::All
786                    | XsdComponentType::Group
787                    | XsdComponentType::Any
788                    | XsdComponentType::Annotation => {
789                        if child_comp.component_type == XsdComponentType::SimpleContent {
790                            // simpleContent may contain restriction/extension
791                            comp.children.extend(child_comp.children);
792                        } else if child_comp.component_type == XsdComponentType::ComplexContent {
793                            // complexContent may contain restriction/extension
794                            comp.children.extend(child_comp.children);
795                        } else {
796                            comp.children.push(child_comp);
797                        }
798                    }
799                    XsdComponentType::Attribute | XsdComponentType::AnyAttribute => {
800                        comp.attributes.push(child_comp);
801                    }
802                    _ => {
803                        comp.children.push(child_comp);
804                    }
805                }
806            }
807            child = (*child).next;
808        }
809
810        comp
811    }
812}
813
814/// Parse a <simpleType> definition.
815///
816/// # SAFETY
817///
818/// - `node` must be a valid pointer to a <simpleType> element node.
819unsafe fn xsd_parse_simple_type(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
820    unsafe {
821        let mut comp = XsdComponent::new(XsdComponentType::SimpleType);
822        comp.name = get_attr(node, "name");
823
824        // Parse child components (restriction, list, union)
825        let mut child = (*node).children;
826        while !child.is_null() {
827            if (*child).type_ == XML_ELEMENT_NODE as c_int {
828                let child_comp = xsd_parse_component(child, schema);
829                match child_comp.component_type {
830                    XsdComponentType::Restriction
831                    | XsdComponentType::List
832                    | XsdComponentType::Union => {
833                        comp.datatype = child_comp.datatype;
834                        comp.base = child_comp.base;
835                        comp.facets = child_comp.facets;
836                        comp.children.extend(child_comp.children);
837                    }
838                    _ => {}
839                }
840            }
841            child = (*child).next;
842        }
843
844        comp
845    }
846}
847
848/// Parse a model group (<sequence>, <choice>, <all>).
849///
850/// # SAFETY
851///
852/// - `node` must be a valid pointer to the model group element node.
853unsafe fn xsd_parse_model_group(
854    node: *mut _xmlNode,
855    ctype: XsdComponentType,
856    schema: &XsdSchema,
857) -> XsdComponent {
858    unsafe {
859        let mut comp = XsdComponent::new(ctype);
860        comp.min_occurs = get_attr_occurs(node, "minOccurs", 1);
861        comp.max_occurs = get_attr_occurs(node, "maxOccurs", 1);
862
863        let mut child = (*node).children;
864        while !child.is_null() {
865            if (*child).type_ == XML_ELEMENT_NODE as c_int {
866                let child_comp = xsd_parse_component(child, schema);
867                match child_comp.component_type {
868                    XsdComponentType::Annotation => {}
869                    _ => {
870                        comp.children.push(child_comp);
871                    }
872                }
873            }
874            child = (*child).next;
875        }
876
877        comp
878    }
879}
880
881/// Parse a <restriction> element.
882///
883/// # SAFETY
884///
885/// - `node` must be a valid pointer to a <restriction> element node.
886unsafe fn xsd_parse_restriction(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
887    unsafe {
888        let mut comp = XsdComponent::new(XsdComponentType::Restriction);
889        comp.base = get_attr(node, "base");
890
891        // Try to resolve the base type
892        if let Some(ref base_name) = comp.base {
893            comp.datatype = parse_datatype_kind(base_name);
894        }
895
896        // Parse facets and child components
897        let mut child = (*node).children;
898        while !child.is_null() {
899            if (*child).type_ == XML_ELEMENT_NODE as c_int {
900                let child_comp = xsd_parse_component(child, schema);
901                match child_comp.component_type {
902                    XsdComponentType::Sequence
903                    | XsdComponentType::Choice
904                    | XsdComponentType::All
905                    | XsdComponentType::Group
906                    | XsdComponentType::Any
907                    | XsdComponentType::Annotation => {
908                        comp.children.push(child_comp);
909                    }
910                    XsdComponentType::Attribute | XsdComponentType::AnyAttribute => {
911                        comp.attributes.push(child_comp);
912                    }
913                    XsdComponentType::SimpleType => {
914                        // Inline simpleType
915                        comp.children.push(child_comp);
916                    }
917                    _ => {
918                        // Facet types
919                        if let Some(facet_kind) =
920                            parse_facet_kind(&format!("{:?}", child_comp.component_type))
921                        {
922                            // Extract the value attribute
923                            if let Some(val) = get_attr(child, "value") {
924                                comp.facets.push((facet_kind, val));
925                            }
926                        }
927                        // Also try by element name
928                        let name_str = if !(*child).name.is_null() {
929                            let mut len = 0;
930                            while *(*child).name.add(len) != 0 {
931                                len += 1;
932                            }
933                            let slice = std::slice::from_raw_parts((*child).name, len);
934                            std::str::from_utf8(slice)
935                                .ok()
936                                .map(|s| {
937                                    if let Some(pos) = s.find(':') {
938                                        s[pos + 1..].to_string()
939                                    } else {
940                                        s.to_string()
941                                    }
942                                })
943                                .unwrap_or_default()
944                        } else {
945                            String::new()
946                        };
947                        if !name_str.is_empty() {
948                            if let Some(facet_kind) = parse_facet_kind(&name_str) {
949                                if let Some(val) = get_attr(child, "value") {
950                                    comp.facets.push((facet_kind, val));
951                                }
952                            }
953                        }
954                    }
955                }
956            }
957            child = (*child).next;
958        }
959
960        comp
961    }
962}
963
964/// Parse an <extension> element.
965///
966/// # SAFETY
967///
968/// - `node` must be a valid pointer to an <extension> element node.
969unsafe fn xsd_parse_extension(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
970    unsafe {
971        let mut comp = XsdComponent::new(XsdComponentType::Extension);
972        comp.base = get_attr(node, "base");
973
974        if let Some(ref base_name) = comp.base {
975            comp.datatype = parse_datatype_kind(base_name);
976        }
977
978        // Parse child components
979        let mut child = (*node).children;
980        while !child.is_null() {
981            if (*child).type_ == XML_ELEMENT_NODE as c_int {
982                let child_comp = xsd_parse_component(child, schema);
983                match child_comp.component_type {
984                    XsdComponentType::Sequence
985                    | XsdComponentType::Choice
986                    | XsdComponentType::All
987                    | XsdComponentType::Group
988                    | XsdComponentType::Any
989                    | XsdComponentType::Annotation => {
990                        comp.children.push(child_comp);
991                    }
992                    XsdComponentType::Attribute | XsdComponentType::AnyAttribute => {
993                        comp.attributes.push(child_comp);
994                    }
995                    _ => {}
996                }
997            }
998            child = (*child).next;
999        }
1000
1001        comp
1002    }
1003}
1004
1005/// Parse a <list> element.
1006///
1007/// # SAFETY
1008///
1009/// - `node` must be a valid pointer to a <list> element node.
1010unsafe fn xsd_parse_list(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
1011    unsafe {
1012        let mut comp = XsdComponent::new(XsdComponentType::List);
1013
1014        if let Some(item_type) = get_attr(node, "itemType") {
1015            comp.base = Some(item_type.clone());
1016            comp.datatype = parse_datatype_kind(&item_type);
1017        }
1018
1019        // Check for inline simpleType
1020        let mut child = (*node).children;
1021        while !child.is_null() {
1022            if (*child).type_ == XML_ELEMENT_NODE as c_int {
1023                let child_comp = xsd_parse_component(child, schema);
1024                match child_comp.component_type {
1025                    XsdComponentType::SimpleType => {
1026                        comp.datatype = child_comp.datatype;
1027                        comp.base = child_comp.base;
1028                        comp.facets = child_comp.facets;
1029                    }
1030                    _ => {}
1031                }
1032            }
1033            child = (*child).next;
1034        }
1035
1036        comp
1037    }
1038}
1039
1040/// Parse a <union> element.
1041///
1042/// # SAFETY
1043///
1044/// - `node` must be a valid pointer to a <union> element node.
1045unsafe fn xsd_parse_union(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
1046    unsafe {
1047        let mut comp = XsdComponent::new(XsdComponentType::Union);
1048
1049        if let Some(member_types) = get_attr(node, "memberTypes") {
1050            comp.base = Some(member_types);
1051        }
1052
1053        let mut child = (*node).children;
1054        while !child.is_null() {
1055            if (*child).type_ == XML_ELEMENT_NODE as c_int {
1056                let child_comp = xsd_parse_component(child, schema);
1057                match child_comp.component_type {
1058                    XsdComponentType::SimpleType => {
1059                        comp.children.push(child_comp);
1060                    }
1061                    _ => {}
1062                }
1063            }
1064            child = (*child).next;
1065        }
1066
1067        comp
1068    }
1069}
1070
1071/// Parse an <annotation> element.
1072///
1073/// # SAFETY
1074///
1075/// - `node` must be a valid pointer to an <annotation> element node.
1076unsafe fn xsd_parse_annotation(node: *mut _xmlNode) -> XsdComponent {
1077    unsafe {
1078        let mut comp = XsdComponent::new(XsdComponentType::Annotation);
1079
1080        let mut child = (*node).children;
1081        while !child.is_null() {
1082            if (*child).type_ == XML_ELEMENT_NODE as c_int {
1083                if node_is(child, "documentation") || node_is(child, "appinfo") {
1084                    let text = get_node_text(child);
1085                    if !text.is_empty() {
1086                        comp.facets.push((XsdDatatypeKind::String, text));
1087                    }
1088                }
1089            }
1090            child = (*child).next;
1091        }
1092
1093        comp
1094    }
1095}
1096
1097/// Parse an <any> element.
1098///
1099/// # SAFETY
1100///
1101/// - `node` must be a valid pointer to an <any> element node.
1102unsafe fn xsd_parse_any(node: *mut _xmlNode, _schema: &XsdSchema) -> XsdComponent {
1103    unsafe {
1104        let mut comp = XsdComponent::new(XsdComponentType::Any);
1105        comp.min_occurs = get_attr_occurs(node, "minOccurs", 1);
1106        comp.max_occurs = get_attr_occurs(node, "maxOccurs", 1);
1107
1108        let namespace_attr = get_attr(node, "namespace");
1109        if let Some(ref ns) = namespace_attr {
1110            if ns != "##any" {
1111                comp.target_namespace = Some(ns.clone());
1112            }
1113        }
1114
1115        comp
1116    }
1117}
1118
1119/// Parse a <group> element.
1120///
1121/// # SAFETY
1122///
1123/// - `node` must be a valid pointer to a <group> element node.
1124unsafe fn xsd_parse_group(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
1125    unsafe {
1126        let mut comp = XsdComponent::new(XsdComponentType::Group);
1127        comp.name = get_attr(node, "name");
1128        comp.ref_name = get_attr(node, "ref");
1129        comp.min_occurs = get_attr_occurs(node, "minOccurs", 1);
1130        comp.max_occurs = get_attr_occurs(node, "maxOccurs", 1);
1131
1132        let mut child = (*node).children;
1133        while !child.is_null() {
1134            if (*child).type_ == XML_ELEMENT_NODE as c_int {
1135                let child_comp = xsd_parse_component(child, schema);
1136                match child_comp.component_type {
1137                    XsdComponentType::Annotation => {}
1138                    _ => {
1139                        comp.children.push(child_comp);
1140                    }
1141                }
1142            }
1143            child = (*child).next;
1144        }
1145
1146        comp
1147    }
1148}
1149
1150/// Parse an <attributeGroup> element.
1151///
1152/// # SAFETY
1153///
1154/// - `node` must be a valid pointer to an <attributeGroup> element node.
1155unsafe fn xsd_parse_attribute_group(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
1156    unsafe {
1157        let mut comp = XsdComponent::new(XsdComponentType::AttributeGroup);
1158        comp.name = get_attr(node, "name");
1159        comp.ref_name = get_attr(node, "ref");
1160
1161        let mut child = (*node).children;
1162        while !child.is_null() {
1163            if (*child).type_ == XML_ELEMENT_NODE as c_int {
1164                let child_comp = xsd_parse_component(child, schema);
1165                match child_comp.component_type {
1166                    XsdComponentType::Attribute | XsdComponentType::AnyAttribute => {
1167                        comp.attributes.push(child_comp);
1168                    }
1169                    _ => {}
1170                }
1171            }
1172            child = (*child).next;
1173        }
1174
1175        comp
1176    }
1177}
1178
1179/// Parse a facet element (pattern, enumeration, etc.).
1180///
1181/// # SAFETY
1182///
1183/// - `node` must be a valid pointer to a facet element node.
1184unsafe fn xsd_parse_facet(node: *mut _xmlNode) -> XsdComponent {
1185    unsafe {
1186        let name_str = if !(*node).name.is_null() {
1187            let mut len = 0;
1188            while *(*node).name.add(len) != 0 {
1189                len += 1;
1190            }
1191            let slice = std::slice::from_raw_parts((*node).name, len);
1192            std::str::from_utf8(slice)
1193                .ok()
1194                .map(|s| {
1195                    if let Some(pos) = s.find(':') {
1196                        s[pos + 1..].to_string()
1197                    } else {
1198                        s.to_string()
1199                    }
1200                })
1201                .unwrap_or_default()
1202        } else {
1203            String::new()
1204        };
1205
1206        let facet_kind = parse_facet_kind(&name_str).unwrap_or(XsdDatatypeKind::String);
1207        let mut comp = XsdComponent::new(XsdComponentType::Schema);
1208        let val = get_attr(node, "value").unwrap_or_default();
1209        comp.facets.push((facet_kind, val));
1210        comp.datatype = Some(facet_kind);
1211
1212        comp
1213    }
1214}
1215
1216/// Parse a <simpleContent> element.
1217///
1218/// # SAFETY
1219///
1220/// - `node` must be a valid pointer to a <simpleContent> element node.
1221unsafe fn xsd_parse_simple_content(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
1222    unsafe {
1223        let mut comp = XsdComponent::new(XsdComponentType::Schema);
1224
1225        let mut child = (*node).children;
1226        while !child.is_null() {
1227            if (*child).type_ == XML_ELEMENT_NODE as c_int {
1228                let child_comp = xsd_parse_component(child, schema);
1229                match child_comp.component_type {
1230                    XsdComponentType::Restriction | XsdComponentType::Extension => {
1231                        comp.children.push(child_comp);
1232                    }
1233                    _ => {}
1234                }
1235            }
1236            child = (*child).next;
1237        }
1238
1239        comp
1240    }
1241}
1242
1243/// Parse a <complexContent> element.
1244///
1245/// # SAFETY
1246///
1247/// - `node` must be a valid pointer to a <complexContent> element node.
1248unsafe fn xsd_parse_complex_content(node: *mut _xmlNode, schema: &XsdSchema) -> XsdComponent {
1249    unsafe {
1250        let mut comp = XsdComponent::new(XsdComponentType::Schema);
1251
1252        let mut child = (*node).children;
1253        while !child.is_null() {
1254            if (*child).type_ == XML_ELEMENT_NODE as c_int {
1255                let child_comp = xsd_parse_component(child, schema);
1256                match child_comp.component_type {
1257                    XsdComponentType::Restriction | XsdComponentType::Extension => {
1258                        comp.children.push(child_comp);
1259                    }
1260                    _ => {}
1261                }
1262            }
1263            child = (*child).next;
1264        }
1265
1266        comp
1267    }
1268}
1269
1270/// Parse an identity constraint (<unique>, <key>, <keyref>).
1271///
1272/// # SAFETY
1273///
1274/// - `node` must be a valid pointer to the identity constraint element node.
1275unsafe fn xsd_parse_identity_constraint(
1276    node: *mut _xmlNode,
1277    ctype: XsdComponentType,
1278    schema: &XsdSchema,
1279) -> XsdComponent {
1280    unsafe {
1281        let mut comp = XsdComponent::new(ctype);
1282        comp.name = get_attr(node, "name");
1283
1284        if ctype == XsdComponentType::KeyRef {
1285            comp.ref_name = get_attr(node, "refer");
1286        }
1287
1288        let mut child = (*node).children;
1289        while !child.is_null() {
1290            if (*child).type_ == XML_ELEMENT_NODE as c_int {
1291                let child_comp = xsd_parse_component(child, schema);
1292                match child_comp.component_type {
1293                    XsdComponentType::Selector | XsdComponentType::Field => {
1294                        comp.children.push(child_comp);
1295                    }
1296                    _ => {}
1297                }
1298            }
1299            child = (*child).next;
1300        }
1301
1302        comp
1303    }
1304}
1305
1306// ═══════════════════════════════════════════════════════════════════════════════
1307// Datatype Validation
1308// ═══════════════════════════════════════════════════════════════════════════════
1309
1310/// Validate a value against an XSD datatype with optional facets.
1311///
1312/// # UPSTREAM-PARITY
1313///
1314/// Equivalent to libxml2's schema type validation functions.
1315pub fn xsd_validate_datatype(
1316    kind: &XsdDatatypeKind,
1317    value: &str,
1318    facets: &[(XsdDatatypeKind, String)],
1319) -> bool {
1320    // First validate the base type
1321    if !validate_base_type(kind, value) {
1322        return false;
1323    }
1324
1325    // Then validate facets.
1326    // UPSTREAM-PARITY: Enumeration facets use OR semantics (value must match
1327    // at least one enumeration value). All other facets use AND semantics
1328    // (value must satisfy all facets).
1329    let mut has_enumeration = false;
1330    let mut enumeration_match = false;
1331
1332    for (facet_kind, facet_value) in facets {
1333        if *facet_kind == XsdDatatypeKind::FacetEnumeration {
1334            has_enumeration = true;
1335            if xsd_validate_facet(kind, value, facet_kind, facet_value) {
1336                enumeration_match = true;
1337            }
1338        } else if !xsd_validate_facet(kind, value, facet_kind, facet_value) {
1339            return false;
1340        }
1341    }
1342
1343    // If there were enumeration facets, at least one must match
1344    if has_enumeration && !enumeration_match {
1345        return false;
1346    }
1347
1348    true
1349}
1350
1351/// Validate a value against a specific facet.
1352pub fn xsd_validate_facet(
1353    _kind: &XsdDatatypeKind,
1354    value: &str,
1355    facet_kind: &XsdDatatypeKind,
1356    facet_value: &str,
1357) -> bool {
1358    match facet_kind {
1359        XsdDatatypeKind::FacetPattern => {
1360            // Simple regex matching (simplified — just check substring containment
1361            // for common patterns like [a-zA-Z]+, etc.)
1362            match facet_value {
1363                r"\d+" => value.chars().all(|c| c.is_ascii_digit()),
1364                r"\d*" => value.is_empty() || value.chars().all(|c| c.is_ascii_digit()),
1365                r"[a-zA-Z]+" => value.chars().all(|c| c.is_ascii_alphabetic()),
1366                r"[a-zA-Z]*" => value.is_empty() || value.chars().all(|c| c.is_ascii_alphabetic()),
1367                r"[a-zA-Z0-9]+" => value.chars().all(|c| c.is_ascii_alphanumeric()),
1368                r"[a-zA-Z0-9_\-]+" => value
1369                    .chars()
1370                    .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-'),
1371                r"[a-zA-Z_][a-zA-Z0-9_\-\.]*" => {
1372                    if value.is_empty() {
1373                        return false;
1374                    }
1375                    let first = value.chars().next().unwrap();
1376                    if !first.is_ascii_alphabetic() && first != '_' {
1377                        return false;
1378                    }
1379                    value
1380                        .chars()
1381                        .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.')
1382                }
1383                r"[a-zA-Z_][\w\-\.]*" => {
1384                    if value.is_empty() {
1385                        return false;
1386                    }
1387                    let first = value.chars().next().unwrap();
1388                    if !first.is_ascii_alphabetic() && first != '_' {
1389                        return false;
1390                    }
1391                    value
1392                        .chars()
1393                        .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.')
1394                }
1395                r"\i\c*" => {
1396                    // XML Name pattern: NameStartChar followed by NameChars
1397                    if value.is_empty() {
1398                        return false;
1399                    }
1400                    let first = value.chars().next().unwrap();
1401                    if !is_name_start_char(first) {
1402                        return false;
1403                    }
1404                    value.chars().skip(1).all(is_name_char)
1405                }
1406                r"\c+" => {
1407                    if value.is_empty() {
1408                        return false;
1409                    }
1410                    value.chars().all(is_name_char)
1411                }
1412                // Default: try basic glob-style matching
1413                _ => {
1414                    if facet_value.starts_with('^') && facet_value.ends_with('$') {
1415                        let inner = &facet_value[1..facet_value.len() - 1];
1416                        simple_glob_match(inner, value)
1417                    } else {
1418                        // Default: accept if we don't understand the pattern
1419                        true
1420                    }
1421                }
1422            }
1423        }
1424        XsdDatatypeKind::FacetEnumeration => {
1425            // Check if the value matches the enumeration literal
1426            value == facet_value
1427        }
1428        XsdDatatypeKind::FacetMinInclusive => {
1429            compare_strings(value, facet_value) != std::cmp::Ordering::Less
1430        }
1431        XsdDatatypeKind::FacetMaxInclusive => {
1432            compare_strings(value, facet_value) != std::cmp::Ordering::Greater
1433        }
1434        XsdDatatypeKind::FacetMinExclusive => {
1435            compare_strings(value, facet_value) == std::cmp::Ordering::Greater
1436        }
1437        XsdDatatypeKind::FacetMaxExclusive => {
1438            compare_strings(value, facet_value) == std::cmp::Ordering::Less
1439        }
1440        XsdDatatypeKind::FacetMinLength => {
1441            let min = facet_value.parse::<usize>().unwrap_or(0);
1442            value.chars().count() >= min
1443        }
1444        XsdDatatypeKind::FacetMaxLength => {
1445            let max = facet_value.parse::<usize>().unwrap_or(usize::MAX);
1446            value.chars().count() <= max
1447        }
1448        XsdDatatypeKind::FacetLength => {
1449            let len = facet_value.parse::<usize>().unwrap_or(0);
1450            value.chars().count() == len
1451        }
1452        XsdDatatypeKind::FacetWhiteSpace => {
1453            // whiteSpace facet: value, replace, collapse
1454            match facet_value {
1455                "replace" => {
1456                    // Any whitespace is valid (but should be tab/newline -> space)
1457                    // We just accept the value
1458                    true
1459                }
1460                "collapse" => {
1461                    // Leading/trailing whitespace collapsed, internal reduced
1462                    true
1463                }
1464                _ => true,
1465            }
1466        }
1467        XsdDatatypeKind::FacetFractionDigits | XsdDatatypeKind::FacetTotalDigits => {
1468            // Numeric precision facets — simplified: just check if it's a valid number
1469            value.parse::<f64>().is_ok()
1470        }
1471        _ => true,
1472    }
1473}
1474
1475/// Simple glob-style pattern matching for XSD pattern facets.
1476fn simple_glob_match(pattern: &str, value: &str) -> bool {
1477    let pattern_chars: Vec<char> = pattern.chars().collect();
1478    let value_chars: Vec<char> = value.chars().collect();
1479
1480    let mut pi = 0;
1481    let mut vi = 0;
1482    let mut backtrack_p = None;
1483    let mut backtrack_v = 0;
1484
1485    while vi < value_chars.len() {
1486        if pi < pattern_chars.len()
1487            && (pattern_chars[pi] == value_chars[vi] || pattern_chars[pi] == '.')
1488        {
1489            pi += 1;
1490            vi += 1;
1491        } else if pi < pattern_chars.len() && pattern_chars[pi] == '*' {
1492            backtrack_p = Some(pi);
1493            backtrack_v = vi + 1;
1494            pi += 1;
1495        } else if pi < pattern_chars.len() && pattern_chars[pi] == '+' {
1496            // '+' = one or more of the next char
1497            if pi + 1 < pattern_chars.len() && pattern_chars[pi + 1] == value_chars[vi] {
1498                pi += 1;
1499                vi += 1;
1500                // Match one or more
1501                while vi < value_chars.len() && value_chars[vi] == pattern_chars[pi] {
1502                    vi += 1;
1503                }
1504                pi += 1;
1505            } else {
1506                return false;
1507            }
1508        } else if let Some(bp) = backtrack_p {
1509            pi = bp + 1;
1510            vi = backtrack_v;
1511            backtrack_v += 1;
1512        } else {
1513            return false;
1514        }
1515    }
1516
1517    // Skip remaining * or + in pattern
1518    while pi < pattern_chars.len() && (pattern_chars[pi] == '*' || pattern_chars[pi] == '+') {
1519        if pattern_chars[pi] == '+' && vi == value_chars.len() {
1520            return false; // '+' requires at least one match
1521        }
1522        pi += 1;
1523    }
1524
1525    pi == pattern_chars.len()
1526}
1527
1528/// Check if a character is an XML NameStartChar.
1529fn is_name_start_char(c: char) -> bool {
1530    c.is_ascii_alphabetic()
1531        || c == '_'
1532        || c == ':'
1533        || (c >= '\u{00C0}' && c <= '\u{00D6}')
1534        || (c >= '\u{00D8}' && c <= '\u{00F6}')
1535        || (c >= '\u{00F8}' && c <= '\u{02FF}')
1536        || (c >= '\u{0370}' && c <= '\u{037D}')
1537        || (c >= '\u{037F}' && c <= '\u{1FFF}')
1538        || (c >= '\u{200C}' && c <= '\u{200D}')
1539        || (c >= '\u{2070}' && c <= '\u{218F}')
1540        || (c >= '\u{2C00}' && c <= '\u{2FEF}')
1541        || (c >= '\u{3001}' && c <= '\u{D7FF}')
1542        || (c >= '\u{F900}' && c <= '\u{FDCF}')
1543        || (c >= '\u{FDF0}' && c <= '\u{FFFD}')
1544}
1545
1546/// Check if a character is an XML NameChar.
1547fn is_name_char(c: char) -> bool {
1548    is_name_start_char(c)
1549        || c.is_ascii_digit()
1550        || c == '-'
1551        || c == '.'
1552        || c == '\u{00B7}'
1553        || (c >= '\u{0300}' && c <= '\u{036F}')
1554        || (c >= '\u{203F}' && c <= '\u{2040}')
1555}
1556
1557/// Compare two string values for facet ordering.
1558fn compare_strings(a: &str, b: &str) -> std::cmp::Ordering {
1559    // Try numeric comparison first
1560    if let (Ok(na), Ok(nb)) = (a.parse::<f64>(), b.parse::<f64>()) {
1561        return na.partial_cmp(&nb).unwrap_or(std::cmp::Ordering::Equal);
1562    }
1563    // Try integer comparison
1564    if let (Ok(na), Ok(nb)) = (a.parse::<i64>(), b.parse::<i64>()) {
1565        return na.cmp(&nb);
1566    }
1567    // Fall back to lexicographic
1568    a.cmp(b)
1569}
1570
1571/// Validate a value against the base type constraints.
1572fn validate_base_type(kind: &XsdDatatypeKind, value: &str) -> bool {
1573    match kind {
1574        XsdDatatypeKind::String => true,
1575        XsdDatatypeKind::NormalizedString => {
1576            // No tabs, newlines, or carriage returns
1577            !value.contains('\t') && !value.contains('\n') && !value.contains('\r')
1578        }
1579        XsdDatatypeKind::Token => {
1580            // No leading/trailing whitespace, no consecutive internal whitespace
1581            if value.is_empty() {
1582                return true;
1583            }
1584            if value.starts_with(' ') || value.ends_with(' ') {
1585                return false;
1586            }
1587            !value.contains("  ")
1588                && !value.contains('\t')
1589                && !value.contains('\n')
1590                && !value.contains('\r')
1591        }
1592        XsdDatatypeKind::Language => {
1593            // RFC 4646 / BCP 47: langtag = (language ["-" script] ["-" region] *("-" variant))
1594            // Simplified: [a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*
1595            if value.is_empty() {
1596                return false;
1597            }
1598            let segments: Vec<&str> = value.split('-').collect();
1599            if segments.is_empty() {
1600                return false;
1601            }
1602            // First segment must be alphabetic only
1603            if segments[0].is_empty() || !segments[0].chars().all(|c| c.is_ascii_alphabetic()) {
1604                return false;
1605            }
1606            if segments[0].len() > 8 {
1607                return false;
1608            }
1609            // Remaining segments can be alphanumeric
1610            for seg in &segments[1..] {
1611                if seg.is_empty() || seg.len() > 8 {
1612                    return false;
1613                }
1614                if !seg.chars().all(|c| c.is_ascii_alphanumeric()) {
1615                    return false;
1616                }
1617            }
1618            true
1619        }
1620        XsdDatatypeKind::Name => {
1621            if value.is_empty() {
1622                return false;
1623            }
1624            let mut chars = value.chars();
1625            let first = chars.next().unwrap();
1626            if !is_name_start_char(first) {
1627                return false;
1628            }
1629            chars.all(is_name_char)
1630        }
1631        XsdDatatypeKind::NCName
1632        | XsdDatatypeKind::Id
1633        | XsdDatatypeKind::Idref
1634        | XsdDatatypeKind::Entity => {
1635            // NCName is a Name with no colon
1636            if value.is_empty() || value.contains(':') {
1637                return false;
1638            }
1639            let mut chars = value.chars();
1640            let first = chars.next().unwrap();
1641            if !is_name_start_char(first) {
1642                return false;
1643            }
1644            chars.all(is_name_char)
1645        }
1646        XsdDatatypeKind::Boolean => {
1647            matches!(value, "true" | "false" | "1" | "0")
1648        }
1649        XsdDatatypeKind::Decimal
1650        | XsdDatatypeKind::Integer
1651        | XsdDatatypeKind::NonPositiveInteger
1652        | XsdDatatypeKind::NegativeInteger
1653        | XsdDatatypeKind::Long
1654        | XsdDatatypeKind::Int
1655        | XsdDatatypeKind::Short
1656        | XsdDatatypeKind::Byte
1657        | XsdDatatypeKind::NonNegativeInteger
1658        | XsdDatatypeKind::UnsignedLong
1659        | XsdDatatypeKind::UnsignedInt
1660        | XsdDatatypeKind::UnsignedShort
1661        | XsdDatatypeKind::UnsignedByte
1662        | XsdDatatypeKind::PositiveInteger => {
1663            // Decimal/integer validation
1664            if value.is_empty() {
1665                return false;
1666            }
1667            let mut chars = value.chars().peekable();
1668            if *chars.peek().unwrap_or(&'\0') == '-' || *chars.peek().unwrap_or(&'\0') == '+' {
1669                chars.next();
1670            }
1671            let mut has_dot = false;
1672            let mut has_digit = false;
1673            for c in chars {
1674                if c == '.' {
1675                    if has_dot {
1676                        return false;
1677                    }
1678                    has_dot = true;
1679                } else if c.is_ascii_digit() {
1680                    has_digit = true;
1681                } else {
1682                    return false;
1683                }
1684            }
1685            if !has_digit {
1686                return false;
1687            }
1688
1689            // Additional constraints for derived integer types
1690            // Integer and all derived integer types reject decimal points
1691            if has_dot && *kind != XsdDatatypeKind::Decimal {
1692                return false;
1693            }
1694
1695            match kind {
1696                XsdDatatypeKind::NonPositiveInteger => {
1697                    if let Ok(v) = value.parse::<i64>() {
1698                        v <= 0
1699                    } else {
1700                        false
1701                    }
1702                }
1703                XsdDatatypeKind::NegativeInteger => {
1704                    if let Ok(v) = value.parse::<i64>() {
1705                        v < 0
1706                    } else {
1707                        false
1708                    }
1709                }
1710                XsdDatatypeKind::NonNegativeInteger => {
1711                    if let Ok(v) = value.parse::<i64>() {
1712                        v >= 0
1713                    } else {
1714                        false
1715                    }
1716                }
1717                XsdDatatypeKind::PositiveInteger => {
1718                    if let Ok(v) = value.parse::<i64>() {
1719                        v > 0
1720                    } else {
1721                        false
1722                    }
1723                }
1724                XsdDatatypeKind::UnsignedLong
1725                | XsdDatatypeKind::UnsignedInt
1726                | XsdDatatypeKind::UnsignedShort
1727                | XsdDatatypeKind::UnsignedByte => {
1728                    if let Ok(v) = value.parse::<u64>() {
1729                        match kind {
1730                            XsdDatatypeKind::UnsignedInt => v <= u64::from(u32::MAX),
1731                            XsdDatatypeKind::UnsignedShort => v <= u64::from(u16::MAX),
1732                            XsdDatatypeKind::UnsignedByte => v <= u64::from(u8::MAX),
1733                            _ => true,
1734                        }
1735                    } else {
1736                        false
1737                    }
1738                }
1739                XsdDatatypeKind::Long => value.parse::<i64>().is_ok(),
1740                XsdDatatypeKind::Int => value.parse::<i32>().is_ok(),
1741                XsdDatatypeKind::Short => value.parse::<i16>().is_ok(),
1742                XsdDatatypeKind::Byte => value.parse::<i8>().is_ok(),
1743                _ => true,
1744            }
1745        }
1746        XsdDatatypeKind::Float | XsdDatatypeKind::Double => {
1747            // Allow INF, -INF, NaN
1748            matches!(value, "INF" | "-INF" | "NaN") || value.parse::<f64>().is_ok()
1749        }
1750        XsdDatatypeKind::Duration => {
1751            // P[nY][nM][nD][T[nH][nM][nS]]
1752            if !value.starts_with('-') && !value.starts_with('P') {
1753                return false;
1754            }
1755            let dur = if value.starts_with('-') {
1756                &value[1..]
1757            } else {
1758                value
1759            };
1760            if !dur.starts_with('P') {
1761                return false;
1762            }
1763            let rest = &dur[1..];
1764            if rest.is_empty() {
1765                return false;
1766            }
1767            let has_t = rest.contains('T');
1768            let date_part = if has_t {
1769                &rest[..rest.find('T').unwrap()]
1770            } else {
1771                rest
1772            };
1773            if has_t {
1774                let time_part = &rest[rest.find('T').unwrap() + 1..];
1775                if time_part.is_empty() {
1776                    return false;
1777                }
1778            }
1779            true
1780        }
1781        XsdDatatypeKind::DateTime => {
1782            // YYYY-MM-DDThh:mm:ss[.sss][Z|±hh:mm]
1783            if value.len() < 19 {
1784                return false;
1785            }
1786            let chars: Vec<char> = value.chars().collect();
1787            chars[4] == '-'
1788                && chars[7] == '-'
1789                && chars[10] == 'T'
1790                && chars[13] == ':'
1791                && chars[16] == ':'
1792        }
1793        XsdDatatypeKind::Date => {
1794            // YYYY-MM-DD[Z|±hh:mm]
1795            if value.len() < 10 {
1796                return false;
1797            }
1798            let chars: Vec<char> = value.chars().collect();
1799            chars[4] == '-' && chars[7] == '-'
1800        }
1801        XsdDatatypeKind::Time => {
1802            // hh:mm:ss[.sss][Z|±hh:mm]
1803            if value.len() < 8 {
1804                return false;
1805            }
1806            let chars: Vec<char> = value.chars().collect();
1807            chars[2] == ':' && chars[5] == ':'
1808        }
1809        XsdDatatypeKind::GYear => {
1810            // YYYY[Z|±hh:mm]
1811            if value.len() < 4 {
1812                return false;
1813            }
1814            value.chars().take(4).all(|c| c.is_ascii_digit())
1815        }
1816        XsdDatatypeKind::GYearMonth => {
1817            // YYYY-MM[Z|±hh:mm]
1818            if value.len() < 7 {
1819                return false;
1820            }
1821            let chars: Vec<char> = value.chars().collect();
1822            chars[4] == '-'
1823        }
1824        XsdDatatypeKind::GMonthDay => {
1825            // --MM-DD[Z|±hh:mm]
1826            if value.len() < 6 || !value.starts_with("--") {
1827                return false;
1828            }
1829            let chars: Vec<char> = value.chars().collect();
1830            chars[4] == '-'
1831        }
1832        XsdDatatypeKind::GDay => {
1833            // ---DD[Z|±hh:mm]
1834            value.starts_with("---") && value.len() >= 4
1835        }
1836        XsdDatatypeKind::GMonth => {
1837            // --MM[Z|±hh:mm]
1838            value.starts_with("--") && value.len() >= 3
1839        }
1840        XsdDatatypeKind::HexBinary => {
1841            if value.len() % 2 != 0 {
1842                return false;
1843            }
1844            value.chars().all(|c| c.is_ascii_hexdigit())
1845        }
1846        XsdDatatypeKind::Base64Binary => {
1847            // Simplified: just check characters are valid base64
1848            if value.is_empty() {
1849                return true;
1850            }
1851            let valid_chars =
1852                |c: char| c.is_ascii_alphanumeric() || c == '+' || c == '/' || c == '=';
1853            value.chars().all(valid_chars)
1854        }
1855        XsdDatatypeKind::AnyURI => {
1856            // Simplified: just check it's not empty
1857            !value.is_empty()
1858        }
1859        XsdDatatypeKind::QName => {
1860            // NCName or Prefix:NCName
1861            if let Some(pos) = value.find(':') {
1862                let prefix = &value[..pos];
1863                let local = &value[pos + 1..];
1864                is_ncname(prefix) && is_ncname(local)
1865            } else {
1866                is_ncname(value)
1867            }
1868        }
1869        XsdDatatypeKind::Notation => {
1870            // Same as QName
1871            !value.is_empty()
1872        }
1873        XsdDatatypeKind::NormalizedString => {
1874            // No tab, newline, or carriage return
1875            !value.contains('\t') && !value.contains('\n') && !value.contains('\r')
1876        }
1877        XsdDatatypeKind::Token => {
1878            // NormalizedString + no leading/trailing whitespace, no internal double spaces
1879            validate_base_type(&XsdDatatypeKind::NormalizedString, value)
1880                && !value.starts_with(' ')
1881                && !value.ends_with(' ')
1882                && !value.contains("  ")
1883        }
1884        XsdDatatypeKind::Language => {
1885            // [a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*
1886            if value.is_empty() {
1887                return false;
1888            }
1889            let parts: Vec<&str> = value.split('-').collect();
1890            if parts.is_empty() {
1891                return false;
1892            }
1893            if parts[0].len() > 8 || !parts[0].chars().all(|c| c.is_ascii_alphabetic()) {
1894                return false;
1895            }
1896            parts.iter().skip(1).all(|p| {
1897                !p.is_empty() && p.len() <= 8 && p.chars().all(|c| c.is_ascii_alphanumeric())
1898            })
1899        }
1900        XsdDatatypeKind::Nmtoken => !value.is_empty() && value.chars().all(is_name_char),
1901        XsdDatatypeKind::Nmtokens => {
1902            !value.is_empty()
1903                && value
1904                    .split_whitespace()
1905                    .all(|t| !t.is_empty() && t.chars().all(is_name_char))
1906        }
1907        XsdDatatypeKind::Name => {
1908            !value.is_empty() && {
1909                let first = value.chars().next().unwrap();
1910                is_name_start_char(first) && value.chars().skip(1).all(is_name_char)
1911            }
1912        }
1913        XsdDatatypeKind::NCName => is_ncname(value),
1914        XsdDatatypeKind::Id | XsdDatatypeKind::Idref | XsdDatatypeKind::Entity => is_ncname(value),
1915        XsdDatatypeKind::Idrefs | XsdDatatypeKind::Entities => {
1916            !value.is_empty() && value.split_whitespace().all(|t| is_ncname(t))
1917        }
1918        // Facet types are always "valid" as values
1919        XsdDatatypeKind::FacetPattern
1920        | XsdDatatypeKind::FacetEnumeration
1921        | XsdDatatypeKind::FacetMinInclusive
1922        | XsdDatatypeKind::FacetMaxInclusive
1923        | XsdDatatypeKind::FacetMinExclusive
1924        | XsdDatatypeKind::FacetMaxExclusive
1925        | XsdDatatypeKind::FacetMinLength
1926        | XsdDatatypeKind::FacetMaxLength
1927        | XsdDatatypeKind::FacetLength
1928        | XsdDatatypeKind::FacetWhiteSpace
1929        | XsdDatatypeKind::FacetFractionDigits
1930        | XsdDatatypeKind::FacetTotalDigits => true,
1931    }
1932}
1933
1934/// Check if a string is a valid NCName.
1935fn is_ncname(value: &str) -> bool {
1936    if value.is_empty() {
1937        return false;
1938    }
1939    let first = value.chars().next().unwrap();
1940    if !is_name_start_char(first) || first == ':' {
1941        return false;
1942    }
1943    value.chars().skip(1).all(|c| is_name_char(c) && c != ':')
1944}
1945
1946// ═══════════════════════════════════════════════════════════════════════════════
1947// Document Validation
1948// ═══════════════════════════════════════════════════════════════════════════════
1949
1950/// Validate an XML document against a schema.
1951///
1952/// # UPSTREAM-PARITY
1953///
1954/// Equivalent to libxml2's `xmlSchemaValidateDoc`.
1955pub fn xsd_validate(schema: &XsdSchema, doc: &str) -> Result<(), Vec<String>> {
1956    let doc_ptr = unsafe {
1957        crate::abi::exports_xml2::xmlReadMemory(
1958            doc.as_ptr() as *const c_char,
1959            doc.len() as c_int,
1960            b"doc.xml\0".as_ptr() as *const c_char,
1961            ptr::null(),
1962            0,
1963        )
1964    };
1965
1966    if doc_ptr.is_null() {
1967        return Err(vec!["Failed to parse XML document".to_string()]);
1968    }
1969
1970    let mut ctxt = XsdValidCtxt::new();
1971    ctxt.schema = Some(schema.clone());
1972
1973    let result = unsafe { xsd_validate_doc(schema, doc_ptr, &mut ctxt) };
1974
1975    unsafe {
1976        crate::abi::exports_xml2::xmlFreeDoc(doc_ptr);
1977    }
1978
1979    if result {
1980        Ok(())
1981    } else {
1982        Err(ctxt.errors)
1983    }
1984}
1985
1986/// Validate a parsed document against a schema.
1987///
1988/// # SAFETY
1989///
1990/// - `doc` must be a valid pointer to an _xmlDoc.
1991unsafe fn xsd_validate_doc(schema: &XsdSchema, doc: *mut _xmlDoc, ctxt: &mut XsdValidCtxt) -> bool {
1992    unsafe {
1993        let root = (*doc).children;
1994        if root.is_null() {
1995            ctxt.errors.push("Document has no root element".to_string());
1996            ctxt.nb_errors += 1;
1997            return false;
1998        }
1999
2000        // Find the root element
2001        let mut root_elem = root;
2002        while !root_elem.is_null() && (*root_elem).type_ != XML_ELEMENT_NODE as c_int {
2003            root_elem = (*root_elem).next;
2004        }
2005
2006        if root_elem.is_null() {
2007            ctxt.errors.push("Document has no root element".to_string());
2008            ctxt.nb_errors += 1;
2009            return false;
2010        }
2011
2012        // Get the root element name
2013        let root_name = get_node_qname(root_elem);
2014
2015        // Find matching global element declaration
2016        let global_elem = schema.components.iter().find(|c| {
2017            c.component_type == XsdComponentType::Element && c.name.as_deref() == Some(&root_name)
2018        });
2019
2020        if let Some(global) = global_elem {
2021            xsd_validate_element(global, root_elem, schema, ctxt)
2022        } else {
2023            // Try finding by any matching component
2024            let mut valid = true;
2025            for component in &schema.components {
2026                if component.component_type == XsdComponentType::Element {
2027                    if let Some(ref name) = component.name {
2028                        if *name == root_name {
2029                            valid = xsd_validate_element(component, root_elem, schema, ctxt);
2030                            break;
2031                        }
2032                    }
2033                }
2034            }
2035            valid
2036        }
2037    }
2038}
2039
2040/// Validate an element node against a component declaration.
2041///
2042/// # SAFETY
2043///
2044/// - `node` must be a valid pointer to an XML element node.
2045fn xsd_validate_element(
2046    component: &XsdComponent,
2047    node: *mut _xmlNode,
2048    schema: &XsdSchema,
2049    ctxt: &mut XsdValidCtxt,
2050) -> bool {
2051    unsafe {
2052        let mut valid = true;
2053
2054        // Get the element's local name
2055        let node_name = get_node_qname(node);
2056
2057        // Check element name
2058        if let Some(ref comp_name) = component.name {
2059            if comp_name != &node_name {
2060                ctxt.errors.push(format!(
2061                    "Element '{}' does not match expected '{}'",
2062                    node_name, comp_name
2063                ));
2064                ctxt.nb_errors += 1;
2065                return false;
2066            }
2067        }
2068
2069        // If there's a type definition (complexType or simpleType) among children, use it
2070        let type_comp = component.children.iter().find(|c| {
2071            c.component_type == XsdComponentType::ComplexType
2072                || c.component_type == XsdComponentType::SimpleType
2073        });
2074
2075        if let Some(tc) = type_comp {
2076            match tc.component_type {
2077                XsdComponentType::ComplexType => {
2078                    valid &= xsd_validate_complex_type(tc, node, schema, ctxt);
2079                }
2080                XsdComponentType::SimpleType => {
2081                    let text = get_node_text(node);
2082                    if let Some(ref dt) = tc.datatype {
2083                        if !xsd_validate_datatype(dt, &text, &tc.facets) {
2084                            ctxt.errors.push(format!(
2085                                "Element '{}' has invalid value '{}' for type '{:?}'",
2086                                node_name, text, dt
2087                            ));
2088                            ctxt.nb_errors += 1;
2089                            valid = false;
2090                        }
2091                    }
2092                }
2093                _ => {}
2094            }
2095        } else if let Some(ref dt) = component.datatype {
2096            // Direct datatype on the element (simple content)
2097            let text = get_node_text(node);
2098            if !xsd_validate_datatype(dt, &text, &component.facets) {
2099                ctxt.errors.push(format!(
2100                    "Element '{}' has invalid value '{}' for type '{:?}'",
2101                    node_name, text, dt
2102                ));
2103                ctxt.nb_errors += 1;
2104                valid = false;
2105            }
2106        } else {
2107            // No type information — validate children against content model
2108            valid &= xsd_validate_content(component, node, schema, ctxt);
2109        }
2110
2111        valid
2112    }
2113}
2114
2115/// Validate a complex type against an element node.
2116///
2117/// # SAFETY
2118///
2119/// - `node` must be a valid pointer to an XML element node.
2120fn xsd_validate_complex_type(
2121    component: &XsdComponent,
2122    node: *mut _xmlNode,
2123    schema: &XsdSchema,
2124    ctxt: &mut XsdValidCtxt,
2125) -> bool {
2126    unsafe {
2127        let mut valid = true;
2128
2129        // Validate attributes
2130        for attr in &component.attributes {
2131            match attr.component_type {
2132                XsdComponentType::Attribute => {
2133                    valid &= xsd_validate_attribute(attr, node, schema, ctxt);
2134                }
2135                XsdComponentType::AnyAttribute => {
2136                    // Any attribute is allowed
2137                }
2138                _ => {}
2139            }
2140        }
2141
2142        // Validate child content (sequence, choice, all)
2143        for child in &component.children {
2144            match child.component_type {
2145                XsdComponentType::Sequence | XsdComponentType::Choice | XsdComponentType::All => {
2146                    valid &= xsd_validate_model_group(child, node, schema, ctxt);
2147                }
2148                XsdComponentType::Restriction | XsdComponentType::Extension => {
2149                    // Handle restriction/extension content
2150                    valid &= xsd_validate_restriction_extension(child, node, schema, ctxt);
2151                }
2152                XsdComponentType::Any => {
2153                    // Any element is allowed
2154                }
2155                _ => {}
2156            }
2157        }
2158
2159        valid
2160    }
2161}
2162
2163/// Validate a model group (sequence, choice, all) against an element's children.
2164///
2165/// # SAFETY
2166///
2167/// - `node` must be a valid pointer to an XML element node.
2168fn xsd_validate_model_group(
2169    component: &XsdComponent,
2170    node: *mut _xmlNode,
2171    schema: &XsdSchema,
2172    ctxt: &mut XsdValidCtxt,
2173) -> bool {
2174    unsafe {
2175        let mut valid = true;
2176
2177        // Collect element children
2178        let mut child_nodes: Vec<*mut _xmlNode> = Vec::new();
2179        let mut child = (*node).children;
2180        while !child.is_null() {
2181            if (*child).type_ == XML_ELEMENT_NODE as c_int {
2182                child_nodes.push(child);
2183            }
2184            child = (*child).next;
2185        }
2186
2187        match component.component_type {
2188            XsdComponentType::Sequence => {
2189                // Validate in-order
2190                let mut child_idx = 0;
2191                for part in &component.children {
2192                    let min = part.min_occurs;
2193                    let max = part.max_occurs;
2194                    let match_name = part.name.as_deref().unwrap_or("");
2195                    let match_ref = part.ref_name.as_deref().unwrap_or("");
2196
2197                    let mut count = 0;
2198                    while child_idx < child_nodes.len() && (max == -1 || count < max) {
2199                        let child_node = child_nodes[child_idx];
2200                        let child_name = get_node_qname(child_node);
2201
2202                        if part.component_type == XsdComponentType::Any {
2203                            count += 1;
2204                            child_idx += 1;
2205                        } else if !match_name.is_empty() && child_name == match_name {
2206                            count += 1;
2207                            child_idx += 1;
2208                        } else if !match_ref.is_empty() && child_name == match_ref {
2209                            count += 1;
2210                            child_idx += 1;
2211                        } else if count >= min {
2212                            break;
2213                        } else {
2214                            ctxt.errors.push(format!(
2215                                "Expected element '{}' but found '{}'",
2216                                match_name, child_name
2217                            ));
2218                            ctxt.nb_errors += 1;
2219                            valid = false;
2220                            child_idx += 1;
2221                            break;
2222                        }
2223                    }
2224
2225                    if count < min {
2226                        ctxt.errors.push(format!(
2227                            "Element '{}' occurs {} times, minimum is {}",
2228                            if match_name.is_empty() {
2229                                "?"
2230                            } else {
2231                                match_name
2232                            },
2233                            count,
2234                            min
2235                        ));
2236                        ctxt.nb_errors += 1;
2237                        valid = false;
2238                    }
2239                }
2240
2241                // Check for unexpected extra children
2242                if child_idx < child_nodes.len() {
2243                    let extra = get_node_qname(child_nodes[child_idx]);
2244                    ctxt.errors
2245                        .push(format!("Unexpected element '{}' in sequence", extra));
2246                    ctxt.nb_errors += 1;
2247                    valid = false;
2248                }
2249            }
2250            XsdComponentType::Choice => {
2251                // At least one of the choices must match
2252                let mut matched = false;
2253                for child_node in &child_nodes {
2254                    let child_name = get_node_qname(*child_node);
2255                    for part in &component.children {
2256                        let match_name = part.name.as_deref().unwrap_or("");
2257                        let match_ref = part.ref_name.as_deref().unwrap_or("");
2258
2259                        if part.component_type == XsdComponentType::Any {
2260                            matched = true;
2261                        } else if (!match_name.is_empty() && child_name == match_name)
2262                            || (!match_ref.is_empty() && child_name == match_ref)
2263                        {
2264                            matched = true;
2265                            break;
2266                        }
2267                    }
2268                    if !matched {
2269                        ctxt.errors
2270                            .push(format!("Element '{}' is not valid in choice", child_name));
2271                        ctxt.nb_errors += 1;
2272                        valid = false;
2273                    }
2274                    matched = false; // Reset for next child
2275                }
2276            }
2277            XsdComponentType::All => {
2278                // All children must match in any order (maxOccurs=1)
2279                for child_node in &child_nodes {
2280                    let child_name = get_node_qname(*child_node);
2281                    let mut matched = false;
2282                    for part in &component.children {
2283                        let match_name = part.name.as_deref().unwrap_or("");
2284                        if !match_name.is_empty() && child_name == match_name {
2285                            matched = true;
2286                            break;
2287                        }
2288                    }
2289                    if !matched {
2290                        ctxt.errors.push(format!(
2291                            "Element '{}' is not valid in all group",
2292                            child_name
2293                        ));
2294                        ctxt.nb_errors += 1;
2295                        valid = false;
2296                    }
2297                }
2298            }
2299            _ => {}
2300        }
2301
2302        valid
2303    }
2304}
2305
2306/// Validate a restriction or extension content.
2307///
2308/// # SAFETY
2309///
2310/// - `node` must be a valid pointer to an XML element node.
2311fn xsd_validate_restriction_extension(
2312    component: &XsdComponent,
2313    node: *mut _xmlNode,
2314    schema: &XsdSchema,
2315    ctxt: &mut XsdValidCtxt,
2316) -> bool {
2317    unsafe {
2318        let mut valid = true;
2319
2320        // Validate attributes
2321        for attr in &component.attributes {
2322            match attr.component_type {
2323                XsdComponentType::Attribute => {
2324                    valid &= xsd_validate_attribute(attr, node, schema, ctxt);
2325                }
2326                _ => {}
2327            }
2328        }
2329
2330        // Validate child content
2331        for child in &component.children {
2332            match child.component_type {
2333                XsdComponentType::Sequence | XsdComponentType::Choice | XsdComponentType::All => {
2334                    valid &= xsd_validate_model_group(child, node, schema, ctxt);
2335                }
2336                _ => {}
2337            }
2338        }
2339
2340        // Validate datatype if present (for simple content restriction)
2341        if let Some(ref dt) = component.datatype {
2342            let text = get_node_text(node);
2343            if !xsd_validate_datatype(dt, &text, &component.facets) {
2344                let node_name = get_node_qname(node);
2345                ctxt.errors.push(format!(
2346                    "Element '{}' has invalid value '{}' for type '{:?}'",
2347                    node_name, text, dt
2348                ));
2349                ctxt.nb_errors += 1;
2350                valid = false;
2351            }
2352        }
2353
2354        valid
2355    }
2356}
2357
2358/// Validate an attribute against an element node.
2359///
2360/// # SAFETY
2361///
2362/// - `node` must be a valid pointer to an XML element node.
2363fn xsd_validate_attribute(
2364    component: &XsdComponent,
2365    node: *mut _xmlNode,
2366    _schema: &XsdSchema,
2367    ctxt: &mut XsdValidCtxt,
2368) -> bool {
2369    unsafe {
2370        let attr_name = component.name.as_deref().unwrap_or("");
2371        if attr_name.is_empty() {
2372            return true;
2373        }
2374
2375        // Check if the attribute exists on the element
2376        let attr_value = get_attr(node, attr_name);
2377
2378        let is_required = component.min_occurs > 0;
2379
2380        match attr_value {
2381            Some(ref val) => {
2382                // Validate attribute value against its datatype
2383                if let Some(ref dt) = component.datatype {
2384                    if !xsd_validate_datatype(dt, val, &component.facets) {
2385                        ctxt.errors.push(format!(
2386                            "Attribute '{}' has invalid value '{}' for type '{:?}'",
2387                            attr_name, val, dt
2388                        ));
2389                        ctxt.nb_errors += 1;
2390                        return false;
2391                    }
2392                }
2393                true
2394            }
2395            None => {
2396                if is_required {
2397                    ctxt.errors
2398                        .push(format!("Required attribute '{}' is missing", attr_name));
2399                    ctxt.nb_errors += 1;
2400                    false
2401                } else {
2402                    true
2403                }
2404            }
2405        }
2406    }
2407}
2408
2409/// Validate child content (no explicit type — just check children).
2410///
2411/// # SAFETY
2412///
2413/// - `node` must be a valid pointer to an XML element node.
2414fn xsd_validate_content(
2415    component: &XsdComponent,
2416    node: *mut _xmlNode,
2417    schema: &XsdSchema,
2418    ctxt: &mut XsdValidCtxt,
2419) -> bool {
2420    unsafe {
2421        let mut valid = true;
2422
2423        for child_comp in &component.children {
2424            match child_comp.component_type {
2425                XsdComponentType::Sequence | XsdComponentType::Choice | XsdComponentType::All => {
2426                    valid &= xsd_validate_model_group(child_comp, node, schema, ctxt);
2427                }
2428                XsdComponentType::Element => {
2429                    // Inline element declaration in a model group
2430                    valid &= xsd_validate_element_inline(child_comp, node, schema, ctxt);
2431                }
2432                _ => {}
2433            }
2434        }
2435
2436        valid
2437    }
2438}
2439
2440/// Validate an inline element declaration (element inside sequence/choice).
2441///
2442/// # SAFETY
2443///
2444/// - `node` must be a valid pointer to an XML element node.
2445fn xsd_validate_element_inline(
2446    component: &XsdComponent,
2447    node: *mut _xmlNode,
2448    schema: &XsdSchema,
2449    ctxt: &mut XsdValidCtxt,
2450) -> bool {
2451    unsafe {
2452        let mut valid = true;
2453        let mut child = (*node).children;
2454
2455        while !child.is_null() {
2456            if (*child).type_ == XML_ELEMENT_NODE as c_int {
2457                let child_name = get_node_qname(child);
2458
2459                let match_name = component.name.as_deref().unwrap_or("");
2460                let match_ref = component.ref_name.as_deref().unwrap_or("");
2461
2462                if (!match_name.is_empty() && child_name == match_name)
2463                    || (!match_ref.is_empty() && child_name == match_ref)
2464                {
2465                    // Check inline type
2466                    let type_comp = component.children.iter().find(|c| {
2467                        c.component_type == XsdComponentType::ComplexType
2468                            || c.component_type == XsdComponentType::SimpleType
2469                    });
2470
2471                    if let Some(tc) = type_comp {
2472                        match tc.component_type {
2473                            XsdComponentType::ComplexType => {
2474                                valid &= xsd_validate_complex_type(tc, child, schema, ctxt);
2475                            }
2476                            XsdComponentType::SimpleType => {
2477                                let text = get_node_text(child);
2478                                if let Some(ref dt) = tc.datatype {
2479                                    if !xsd_validate_datatype(dt, &text, &tc.facets) {
2480                                        ctxt.errors.push(format!(
2481                                            "Element '{}' has invalid value '{}'",
2482                                            child_name, text
2483                                        ));
2484                                        ctxt.nb_errors += 1;
2485                                        valid = false;
2486                                    }
2487                                }
2488                            }
2489                            _ => {}
2490                        }
2491                    }
2492                }
2493            }
2494            child = (*child).next;
2495        }
2496
2497        valid
2498    }
2499}
2500
2501/// Get the qualified name of a node (with namespace prefix if available).
2502///
2503/// # SAFETY
2504///
2505/// - `node` must be a valid pointer to an _xmlNode or NULL.
2506unsafe fn get_node_qname(node: *mut _xmlNode) -> String {
2507    if node.is_null() {
2508        return String::new();
2509    }
2510    unsafe {
2511        // Check for namespace prefix
2512        let ns = (*node).ns;
2513        let prefix = if !ns.is_null() && !(*ns).prefix.is_null() {
2514            let mut len = 0;
2515            while *(*ns).prefix.add(len) != 0 {
2516                len += 1;
2517            }
2518            let slice = std::slice::from_raw_parts((*ns).prefix, len);
2519            if let Ok(s) = std::str::from_utf8(slice) {
2520                format!("{}:", s)
2521            } else {
2522                String::new()
2523            }
2524        } else {
2525            String::new()
2526        };
2527
2528        let name = (*node).name;
2529        if name.is_null() {
2530            return String::new();
2531        }
2532        let mut len = 0;
2533        while *name.add(len) != 0 {
2534            len += 1;
2535        }
2536        let slice = std::slice::from_raw_parts(name, len);
2537        if let Ok(s) = std::str::from_utf8(slice) {
2538            format!("{}{}", prefix, s)
2539        } else {
2540            String::new()
2541        }
2542    }
2543}
2544
2545// ═══════════════════════════════════════════════════════════════════════════════
2546// C ABI Functions
2547// ═══════════════════════════════════════════════════════════════════════════════
2548
2549// These are the C-compatible entry points that get exported via the ABI layer.
2550// They use raw pointers and follow libxml2's calling conventions.
2551
2552/// Create a new schema parser context from a URL.
2553///
2554/// # UPSTREAM-PARITY
2555///
2556/// ```c
2557/// xmlSchemaParserCtxtPtr xmlSchemaNewParserCtxt(const char *URL);
2558/// ```
2559///
2560/// # SAFETY
2561///
2562/// - `url` must be a valid null-terminated C string or NULL.
2563#[no_mangle]
2564pub unsafe extern "C" fn xmlSchemaNewParserCtxt(url: *const c_char) -> *mut c_void {
2565    if url.is_null() {
2566        // Return a simple empty context
2567        let ctxt = allocator::xmlMallocZero(size_of::<XsdSchema>() as usize);
2568        return ctxt;
2569    }
2570
2571    // Read the URL
2572    let url_str = unsafe {
2573        if url.is_null() {
2574            String::new()
2575        } else {
2576            let mut len = 0;
2577            while *url.add(len) != 0 {
2578                len += 1;
2579            }
2580            let slice = std::slice::from_raw_parts(url as *const u8, len);
2581            String::from_utf8_lossy(slice).to_string()
2582        }
2583    };
2584
2585    // Try to parse the schema from the URL
2586    // For now, return a placeholder context
2587    let ctxt = allocator::xmlMallocZero(size_of::<XsdSchema>() as usize);
2588    // In a full implementation, this would read the file and parse it
2589    if !url_str.is_empty() {
2590        // Store the URL for later parsing
2591        let _ = url_str;
2592    }
2593
2594    ctxt
2595}
2596
2597/// Create a new schema parser context from a memory buffer.
2598///
2599/// # UPSTREAM-PARITY
2600///
2601/// ```c
2602/// xmlSchemaParserCtxtPtr xmlSchemaNewMemParserCtxt(const char *buffer, int size);
2603/// ```
2604///
2605/// # SAFETY
2606///
2607/// - `buffer` must be a valid pointer to a buffer of at least `size` bytes.
2608#[no_mangle]
2609pub unsafe extern "C" fn xmlSchemaNewMemParserCtxt(
2610    buffer: *const c_char,
2611    size: c_int,
2612) -> *mut c_void {
2613    if buffer.is_null() || size <= 0 {
2614        return ptr::null_mut();
2615    }
2616
2617    // Parse the schema immediately
2618    let buf_slice = unsafe { std::slice::from_raw_parts(buffer as *const u8, size as usize) };
2619    let xml_str = String::from_utf8_lossy(buf_slice).to_string();
2620
2621    match xsd_parse(&xml_str) {
2622        Ok(schema) => {
2623            // Allocate and store the schema
2624            let schema_box = Box::new(schema);
2625            Box::into_raw(schema_box) as *mut c_void
2626        }
2627        Err(_) => ptr::null_mut(),
2628    }
2629}
2630
2631/// Parse a schema.
2632///
2633/// # UPSTREAM-PARITY
2634///
2635/// ```c
2636/// xmlSchemaPtr xmlSchemaParse(xmlSchemaParserCtxtPtr ctxt);
2637/// ```
2638///
2639/// # SAFETY
2640///
2641/// - `ctxt` must be a valid pointer to a parser context, or NULL.
2642#[no_mangle]
2643pub unsafe extern "C" fn xmlSchemaParse(ctxt: *mut c_void) -> *mut c_void {
2644    if ctxt.is_null() {
2645        return ptr::null_mut();
2646    }
2647
2648    // If the context already contains a parsed schema (from xmlSchemaNewMemParserCtxt),
2649    // return it. Otherwise, parse from the URL stored in the context.
2650    // For now, just return the context as the schema pointer.
2651    ctxt
2652}
2653
2654/// Free a schema.
2655///
2656/// # UPSTREAM-PARITY
2657///
2658/// ```c
2659/// void xmlSchemaFree(xmlSchemaPtr schema);
2660/// ```
2661///
2662/// # SAFETY
2663///
2664/// - `schema` must be a valid pointer to a schema, or NULL.
2665#[no_mangle]
2666pub unsafe extern "C" fn xmlSchemaFree(schema: *mut c_void) {
2667    if schema.is_null() {
2668        return;
2669    }
2670    // SAFETY: Reconstruct the Box to drop it.
2671    unsafe {
2672        let _ = Box::from_raw(schema as *mut XsdSchema);
2673    }
2674}
2675
2676/// Validate a document against a schema.
2677///
2678/// # UPSTREAM-PARITY
2679///
2680/// ```c
2681/// int xmlSchemaValidateDoc(xmlSchemaValidCtxtPtr ctxt, xmlDocPtr doc);
2682/// ```
2683///
2684/// Returns 0 if valid, -1 on internal error, or the number of validation errors.
2685///
2686/// # SAFETY
2687///
2688/// - `ctxt` must be a valid pointer to a validation context, or NULL.
2689/// - `doc` must be a valid pointer to an _xmlDoc, or NULL.
2690#[no_mangle]
2691pub unsafe extern "C" fn xmlSchemaValidateDoc(ctxt: *mut c_void, doc: *mut _xmlDoc) -> c_int {
2692    if ctxt.is_null() || doc.is_null() {
2693        return -1;
2694    }
2695
2696    unsafe {
2697        let valid_ctxt = &mut *(ctxt as *mut XsdValidCtxt);
2698        let schema = match &valid_ctxt.schema {
2699            Some(s) => s,
2700            None => return -1,
2701        };
2702
2703        let mut temp_ctxt = XsdValidCtxt::new();
2704        temp_ctxt.schema = Some(schema.clone());
2705
2706        let valid = xsd_validate_doc(schema, doc, &mut temp_ctxt);
2707
2708        if valid {
2709            0
2710        } else {
2711            valid_ctxt.errors = temp_ctxt.errors;
2712            valid_ctxt.nb_errors = temp_ctxt.nb_errors;
2713            temp_ctxt.nb_errors
2714        }
2715    }
2716}
2717
2718/// Free a schema parser context.
2719///
2720/// # UPSTREAM-PARITY
2721///
2722/// ```c
2723/// void xmlSchemaFreeParserCtxt(xmlSchemaParserCtxtPtr ctxt);
2724/// ```
2725///
2726/// # SAFETY
2727///
2728/// - `ctxt` must be a valid pointer to a parser context, or NULL.
2729#[no_mangle]
2730pub unsafe extern "C" fn xmlSchemaFreeParserCtxt(ctxt: *mut c_void) {
2731    if ctxt.is_null() {
2732        return;
2733    }
2734    // SAFETY: Reconstruct the Box to drop it.
2735    unsafe {
2736        let _ = Box::from_raw(ctxt as *mut XsdSchema);
2737    }
2738}
2739
2740/// Free a schema validation context.
2741///
2742/// # UPSTREAM-PARITY
2743///
2744/// ```c
2745/// void xmlSchemaFreeValidCtxt(xmlSchemaValidCtxtPtr ctxt);
2746/// ```
2747///
2748/// # SAFETY
2749///
2750/// - `ctxt` must be a valid pointer to a validation context, or NULL.
2751#[no_mangle]
2752pub unsafe extern "C" fn xmlSchemaFreeValidCtxt(ctxt: *mut c_void) {
2753    if ctxt.is_null() {
2754        return;
2755    }
2756    // SAFETY: Reconstruct the Box to drop it.
2757    unsafe {
2758        let _ = Box::from_raw(ctxt as *mut XsdValidCtxt);
2759    }
2760}
2761
2762/// Create a new schema validation context.
2763///
2764/// # UPSTREAM-PARITY
2765///
2766/// ```c
2767/// xmlSchemaValidCtxtPtr xmlSchemaNewValidCtxt(xmlSchemaPtr schema);
2768/// ```
2769///
2770/// # SAFETY
2771///
2772/// - `schema` must be a valid pointer to a schema, or NULL.
2773#[no_mangle]
2774pub unsafe extern "C" fn xmlSchemaNewValidCtxt(schema: *mut c_void) -> *mut c_void {
2775    let mut ctxt = XsdValidCtxt::new();
2776
2777    if !schema.is_null() {
2778        // SAFETY: The schema pointer is assumed to be a valid XsdSchema.
2779        unsafe {
2780            let schema_ref = &*(schema as *const XsdSchema);
2781            ctxt.schema = Some(schema_ref.clone());
2782        }
2783    }
2784
2785    let boxed = Box::new(ctxt);
2786    Box::into_raw(boxed) as *mut c_void
2787}
2788
2789// ═══════════════════════════════════════════════════════════════════════════════
2790// Tests
2791// ═══════════════════════════════════════════════════════════════════════════════
2792
2793#[cfg(test)]
2794mod tests {
2795    use super::*;
2796
2797    // ── Datatype Validation Tests ─────────────────────────────────────────
2798
2799    #[test]
2800    fn test_validate_string() {
2801        assert!(xsd_validate_datatype(
2802            &XsdDatatypeKind::String,
2803            "hello",
2804            &[]
2805        ));
2806        assert!(xsd_validate_datatype(&XsdDatatypeKind::String, "", &[]));
2807    }
2808
2809    #[test]
2810    fn test_validate_boolean() {
2811        assert!(xsd_validate_datatype(
2812            &XsdDatatypeKind::Boolean,
2813            "true",
2814            &[]
2815        ));
2816        assert!(xsd_validate_datatype(
2817            &XsdDatatypeKind::Boolean,
2818            "false",
2819            &[]
2820        ));
2821        assert!(xsd_validate_datatype(&XsdDatatypeKind::Boolean, "1", &[]));
2822        assert!(xsd_validate_datatype(&XsdDatatypeKind::Boolean, "0", &[]));
2823        assert!(!xsd_validate_datatype(
2824            &XsdDatatypeKind::Boolean,
2825            "yes",
2826            &[]
2827        ));
2828        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Boolean, "no", &[]));
2829    }
2830
2831    #[test]
2832    fn test_validate_integer() {
2833        assert!(xsd_validate_datatype(&XsdDatatypeKind::Integer, "42", &[]));
2834        assert!(xsd_validate_datatype(&XsdDatatypeKind::Integer, "-42", &[]));
2835        assert!(xsd_validate_datatype(&XsdDatatypeKind::Integer, "+42", &[]));
2836        assert!(!xsd_validate_datatype(
2837            &XsdDatatypeKind::Integer,
2838            "12.5",
2839            &[]
2840        ));
2841        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Integer, "", &[]));
2842        assert!(!xsd_validate_datatype(
2843            &XsdDatatypeKind::Integer,
2844            "abc",
2845            &[]
2846        ));
2847    }
2848
2849    #[test]
2850    fn test_validate_decimal() {
2851        assert!(xsd_validate_datatype(&XsdDatatypeKind::Decimal, "42", &[]));
2852        assert!(xsd_validate_datatype(
2853            &XsdDatatypeKind::Decimal,
2854            "12.5",
2855            &[]
2856        ));
2857        assert!(xsd_validate_datatype(
2858            &XsdDatatypeKind::Decimal,
2859            "-3.14",
2860            &[]
2861        ));
2862        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Decimal, "", &[]));
2863    }
2864
2865    #[test]
2866    fn test_validate_float() {
2867        assert!(xsd_validate_datatype(&XsdDatatypeKind::Float, "3.14", &[]));
2868        assert!(xsd_validate_datatype(&XsdDatatypeKind::Float, "INF", &[]));
2869        assert!(xsd_validate_datatype(&XsdDatatypeKind::Float, "-INF", &[]));
2870        assert!(xsd_validate_datatype(&XsdDatatypeKind::Float, "NaN", &[]));
2871        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Float, "", &[]));
2872    }
2873
2874    #[test]
2875    fn test_validate_positive_integer() {
2876        assert!(xsd_validate_datatype(
2877            &XsdDatatypeKind::PositiveInteger,
2878            "1",
2879            &[]
2880        ));
2881        assert!(xsd_validate_datatype(
2882            &XsdDatatypeKind::PositiveInteger,
2883            "100",
2884            &[]
2885        ));
2886        assert!(!xsd_validate_datatype(
2887            &XsdDatatypeKind::PositiveInteger,
2888            "0",
2889            &[]
2890        ));
2891        assert!(!xsd_validate_datatype(
2892            &XsdDatatypeKind::PositiveInteger,
2893            "-1",
2894            &[]
2895        ));
2896    }
2897
2898    #[test]
2899    fn test_validate_non_negative_integer() {
2900        assert!(xsd_validate_datatype(
2901            &XsdDatatypeKind::NonNegativeInteger,
2902            "0",
2903            &[]
2904        ));
2905        assert!(xsd_validate_datatype(
2906            &XsdDatatypeKind::NonNegativeInteger,
2907            "42",
2908            &[]
2909        ));
2910        assert!(!xsd_validate_datatype(
2911            &XsdDatatypeKind::NonNegativeInteger,
2912            "-1",
2913            &[]
2914        ));
2915    }
2916
2917    #[test]
2918    fn test_validate_int_range() {
2919        assert!(xsd_validate_datatype(
2920            &XsdDatatypeKind::Int,
2921            "2147483647",
2922            &[]
2923        ));
2924        assert!(xsd_validate_datatype(
2925            &XsdDatatypeKind::Int,
2926            "-2147483648",
2927            &[]
2928        ));
2929        assert!(!xsd_validate_datatype(
2930            &XsdDatatypeKind::Int,
2931            "2147483648",
2932            &[]
2933        ));
2934    }
2935
2936    #[test]
2937    fn test_validate_short_range() {
2938        assert!(xsd_validate_datatype(&XsdDatatypeKind::Short, "32767", &[]));
2939        assert!(xsd_validate_datatype(
2940            &XsdDatatypeKind::Short,
2941            "-32768",
2942            &[]
2943        ));
2944        assert!(!xsd_validate_datatype(
2945            &XsdDatatypeKind::Short,
2946            "32768",
2947            &[]
2948        ));
2949    }
2950
2951    #[test]
2952    fn test_validate_byte_range() {
2953        assert!(xsd_validate_datatype(&XsdDatatypeKind::Byte, "127", &[]));
2954        assert!(xsd_validate_datatype(&XsdDatatypeKind::Byte, "-128", &[]));
2955        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Byte, "128", &[]));
2956    }
2957
2958    #[test]
2959    fn test_validate_date_time() {
2960        assert!(xsd_validate_datatype(
2961            &XsdDatatypeKind::DateTime,
2962            "2023-01-15T10:30:00",
2963            &[]
2964        ));
2965        assert!(!xsd_validate_datatype(
2966            &XsdDatatypeKind::DateTime,
2967            "not-a-date",
2968            &[]
2969        ));
2970    }
2971
2972    #[test]
2973    fn test_validate_date() {
2974        assert!(xsd_validate_datatype(
2975            &XsdDatatypeKind::Date,
2976            "2023-01-15",
2977            &[]
2978        ));
2979        assert!(!xsd_validate_datatype(
2980            &XsdDatatypeKind::Date,
2981            "2023/01/15",
2982            &[]
2983        ));
2984    }
2985
2986    #[test]
2987    fn test_validate_time() {
2988        assert!(xsd_validate_datatype(
2989            &XsdDatatypeKind::Time,
2990            "10:30:00",
2991            &[]
2992        ));
2993        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Time, "10:30", &[]));
2994    }
2995
2996    #[test]
2997    fn test_validate_hex_binary() {
2998        assert!(xsd_validate_datatype(
2999            &XsdDatatypeKind::HexBinary,
3000            "0FA1",
3001            &[]
3002        ));
3003        assert!(xsd_validate_datatype(&XsdDatatypeKind::HexBinary, "", &[]));
3004        assert!(!xsd_validate_datatype(
3005            &XsdDatatypeKind::HexBinary,
3006            "0FG1",
3007            &[]
3008        ));
3009        assert!(!xsd_validate_datatype(
3010            &XsdDatatypeKind::HexBinary,
3011            "0FA",
3012            &[]
3013        ));
3014    }
3015
3016    #[test]
3017    fn test_validate_base64() {
3018        assert!(xsd_validate_datatype(
3019            &XsdDatatypeKind::Base64Binary,
3020            "SGVsbG8=",
3021            &[]
3022        ));
3023        assert!(xsd_validate_datatype(
3024            &XsdDatatypeKind::Base64Binary,
3025            "",
3026            &[]
3027        ));
3028        assert!(!xsd_validate_datatype(
3029            &XsdDatatypeKind::Base64Binary,
3030            "Hello World!",
3031            &[]
3032        ));
3033    }
3034
3035    #[test]
3036    fn test_validate_ncname() {
3037        assert!(xsd_validate_datatype(
3038            &XsdDatatypeKind::NCName,
3039            "myElement",
3040            &[]
3041        ));
3042        assert!(xsd_validate_datatype(&XsdDatatypeKind::NCName, "_foo", &[]));
3043        assert!(!xsd_validate_datatype(
3044            &XsdDatatypeKind::NCName,
3045            "123abc",
3046            &[]
3047        ));
3048        assert!(!xsd_validate_datatype(&XsdDatatypeKind::NCName, "", &[]));
3049    }
3050
3051    #[test]
3052    fn test_validate_qname() {
3053        assert!(xsd_validate_datatype(
3054            &XsdDatatypeKind::QName,
3055            "ns:local",
3056            &[]
3057        ));
3058        assert!(xsd_validate_datatype(&XsdDatatypeKind::QName, "local", &[]));
3059        assert!(!xsd_validate_datatype(&XsdDatatypeKind::QName, "", &[]));
3060    }
3061
3062    #[test]
3063    fn test_validate_token() {
3064        assert!(xsd_validate_datatype(&XsdDatatypeKind::Token, "hello", &[]));
3065        assert!(!xsd_validate_datatype(
3066            &XsdDatatypeKind::Token,
3067            " hello",
3068            &[]
3069        ));
3070        assert!(!xsd_validate_datatype(
3071            &XsdDatatypeKind::Token,
3072            "hello ",
3073            &[]
3074        ));
3075        assert!(!xsd_validate_datatype(
3076            &XsdDatatypeKind::Token,
3077            "hello  world",
3078            &[]
3079        ));
3080        assert!(!xsd_validate_datatype(
3081            &XsdDatatypeKind::Token,
3082            "hello\tworld",
3083            &[]
3084        ));
3085    }
3086
3087    #[test]
3088    fn test_validate_language() {
3089        assert!(xsd_validate_datatype(&XsdDatatypeKind::Language, "en", &[]));
3090        assert!(xsd_validate_datatype(
3091            &XsdDatatypeKind::Language,
3092            "en-US",
3093            &[]
3094        ));
3095        assert!(xsd_validate_datatype(
3096            &XsdDatatypeKind::Language,
3097            "zh-CN",
3098            &[]
3099        ));
3100        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Language, "", &[]));
3101        assert!(!xsd_validate_datatype(
3102            &XsdDatatypeKind::Language,
3103            "123",
3104            &[]
3105        ));
3106    }
3107
3108    #[test]
3109    fn test_validate_name() {
3110        assert!(xsd_validate_datatype(
3111            &XsdDatatypeKind::Name,
3112            "myElement",
3113            &[]
3114        ));
3115        assert!(xsd_validate_datatype(
3116            &XsdDatatypeKind::Name,
3117            "ns:local",
3118            &[]
3119        ));
3120        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Name, "", &[]));
3121    }
3122
3123    #[test]
3124    fn test_validate_nmtoken() {
3125        assert!(xsd_validate_datatype(
3126            &XsdDatatypeKind::Nmtoken,
3127            "token123",
3128            &[]
3129        ));
3130        assert!(xsd_validate_datatype(
3131            &XsdDatatypeKind::Nmtoken,
3132            "123token",
3133            &[]
3134        ));
3135        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Nmtoken, "", &[]));
3136    }
3137
3138    #[test]
3139    fn test_validate_duration() {
3140        assert!(xsd_validate_datatype(
3141            &XsdDatatypeKind::Duration,
3142            "P1Y2M3DT4H5M6S",
3143            &[]
3144        ));
3145        assert!(xsd_validate_datatype(
3146            &XsdDatatypeKind::Duration,
3147            "P1Y",
3148            &[]
3149        ));
3150        assert!(!xsd_validate_datatype(&XsdDatatypeKind::Duration, "", &[]));
3151    }
3152
3153    #[test]
3154    fn test_validate_g_year() {
3155        assert!(xsd_validate_datatype(&XsdDatatypeKind::GYear, "2023", &[]));
3156        assert!(!xsd_validate_datatype(&XsdDatatypeKind::GYear, "", &[]));
3157    }
3158
3159    #[test]
3160    fn test_validate_g_month() {
3161        assert!(xsd_validate_datatype(&XsdDatatypeKind::GMonth, "--05", &[]));
3162        assert!(!xsd_validate_datatype(&XsdDatatypeKind::GMonth, "", &[]));
3163    }
3164
3165    #[test]
3166    fn test_validate_g_day() {
3167        assert!(xsd_validate_datatype(&XsdDatatypeKind::GDay, "---15", &[]));
3168        assert!(!xsd_validate_datatype(&XsdDatatypeKind::GDay, "", &[]));
3169    }
3170
3171    // ── Facet Validation Tests ────────────────────────────────────────────
3172
3173    #[test]
3174    fn test_facet_min_length() {
3175        let facets = vec![(XsdDatatypeKind::FacetMinLength, "3".to_string())];
3176        assert!(xsd_validate_datatype(
3177            &XsdDatatypeKind::String,
3178            "hello",
3179            &facets
3180        ));
3181        assert!(xsd_validate_datatype(
3182            &XsdDatatypeKind::String,
3183            "abc",
3184            &facets
3185        ));
3186        assert!(!xsd_validate_datatype(
3187            &XsdDatatypeKind::String,
3188            "ab",
3189            &facets
3190        ));
3191    }
3192
3193    #[test]
3194    fn test_facet_max_length() {
3195        let facets = vec![(XsdDatatypeKind::FacetMaxLength, "3".to_string())];
3196        assert!(xsd_validate_datatype(
3197            &XsdDatatypeKind::String,
3198            "ab",
3199            &facets
3200        ));
3201        assert!(xsd_validate_datatype(
3202            &XsdDatatypeKind::String,
3203            "abc",
3204            &facets
3205        ));
3206        assert!(!xsd_validate_datatype(
3207            &XsdDatatypeKind::String,
3208            "abcd",
3209            &facets
3210        ));
3211    }
3212
3213    #[test]
3214    fn test_facet_length() {
3215        let facets = vec![(XsdDatatypeKind::FacetLength, "3".to_string())];
3216        assert!(xsd_validate_datatype(
3217            &XsdDatatypeKind::String,
3218            "abc",
3219            &facets
3220        ));
3221        assert!(!xsd_validate_datatype(
3222            &XsdDatatypeKind::String,
3223            "ab",
3224            &facets
3225        ));
3226        assert!(!xsd_validate_datatype(
3227            &XsdDatatypeKind::String,
3228            "abcd",
3229            &facets
3230        ));
3231    }
3232
3233    #[test]
3234    fn test_facet_min_inclusive() {
3235        let facets = vec![(XsdDatatypeKind::FacetMinInclusive, "5".to_string())];
3236        assert!(xsd_validate_datatype(
3237            &XsdDatatypeKind::Integer,
3238            "5",
3239            &facets
3240        ));
3241        assert!(xsd_validate_datatype(
3242            &XsdDatatypeKind::Integer,
3243            "10",
3244            &facets
3245        ));
3246        assert!(!xsd_validate_datatype(
3247            &XsdDatatypeKind::Integer,
3248            "3",
3249            &facets
3250        ));
3251    }
3252
3253    #[test]
3254    fn test_facet_max_inclusive() {
3255        let facets = vec![(XsdDatatypeKind::FacetMaxInclusive, "10".to_string())];
3256        assert!(xsd_validate_datatype(
3257            &XsdDatatypeKind::Integer,
3258            "10",
3259            &facets
3260        ));
3261        assert!(xsd_validate_datatype(
3262            &XsdDatatypeKind::Integer,
3263            "5",
3264            &facets
3265        ));
3266        assert!(!xsd_validate_datatype(
3267            &XsdDatatypeKind::Integer,
3268            "15",
3269            &facets
3270        ));
3271    }
3272
3273    #[test]
3274    fn test_facet_pattern_digits() {
3275        let facets = vec![(XsdDatatypeKind::FacetPattern, r"\d+".to_string())];
3276        assert!(xsd_validate_datatype(
3277            &XsdDatatypeKind::String,
3278            "123",
3279            &facets
3280        ));
3281        assert!(!xsd_validate_datatype(
3282            &XsdDatatypeKind::String,
3283            "abc",
3284            &facets
3285        ));
3286    }
3287
3288    #[test]
3289    fn test_facet_pattern_alpha() {
3290        let facets = vec![(XsdDatatypeKind::FacetPattern, r"[a-zA-Z]+".to_string())];
3291        assert!(xsd_validate_datatype(
3292            &XsdDatatypeKind::String,
3293            "hello",
3294            &facets
3295        ));
3296        assert!(!xsd_validate_datatype(
3297            &XsdDatatypeKind::String,
3298            "123",
3299            &facets
3300        ));
3301    }
3302
3303    // ── Schema Parsing Tests ──────────────────────────────────────────────
3304
3305    #[test]
3306    fn test_parse_empty_schema() {
3307        let schema_xml = r#"<?xml version="1.0"?>
3308            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3309            </xs:schema>"#;
3310
3311        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3312        assert!(schema.components.is_empty());
3313    }
3314
3315    #[test]
3316    fn test_parse_schema_with_target_namespace() {
3317        let schema_xml = r#"<?xml version="1.0"?>
3318            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
3319                       targetNamespace="http://example.com/ns">
3320            </xs:schema>"#;
3321
3322        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3323        assert_eq!(
3324            schema.target_namespace,
3325            Some("http://example.com/ns".to_string())
3326        );
3327    }
3328
3329    #[test]
3330    fn test_parse_simple_element() {
3331        let schema_xml = r#"<?xml version="1.0"?>
3332            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3333                <xs:element name="name" type="xs:string"/>
3334            </xs:schema>"#;
3335
3336        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3337        assert_eq!(schema.components.len(), 1);
3338        assert_eq!(
3339            schema.components[0].component_type,
3340            XsdComponentType::Element
3341        );
3342        assert_eq!(schema.components[0].name, Some("name".to_string()));
3343        assert_eq!(schema.components[0].datatype, Some(XsdDatatypeKind::String));
3344    }
3345
3346    #[test]
3347    fn test_parse_integer_element() {
3348        let schema_xml = r#"<?xml version="1.0"?>
3349            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3350                <xs:element name="age" type="xs:integer"/>
3351            </xs:schema>"#;
3352
3353        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3354        assert_eq!(schema.components.len(), 1);
3355        assert_eq!(schema.components[0].name, Some("age".to_string()));
3356        assert_eq!(
3357            schema.components[0].datatype,
3358            Some(XsdDatatypeKind::Integer)
3359        );
3360    }
3361
3362    #[test]
3363    fn test_parse_element_with_attributes() {
3364        let schema_xml = r#"<?xml version="1.0"?>
3365            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3366                <xs:element name="product">
3367                    <xs:complexType>
3368                        <xs:sequence>
3369                            <xs:element name="name" type="xs:string"/>
3370                            <xs:element name="price" type="xs:decimal"/>
3371                        </xs:sequence>
3372                        <xs:attribute name="id" type="xs:integer" use="required"/>
3373                    </xs:complexType>
3374                </xs:element>
3375            </xs:schema>"#;
3376
3377        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3378        assert_eq!(schema.components.len(), 1);
3379        assert_eq!(schema.components[0].name, Some("product".to_string()));
3380
3381        // Should have a complexType child
3382        let ct = &schema.components[0].children;
3383        let complex_type = ct
3384            .iter()
3385            .find(|c| c.component_type == XsdComponentType::ComplexType);
3386        assert!(complex_type.is_some());
3387        if let Some(ctc) = complex_type {
3388            assert_eq!(ctc.attributes.len(), 1);
3389            assert_eq!(ctc.attributes[0].name, Some("id".to_string()));
3390            assert_eq!(ctc.attributes[0].datatype, Some(XsdDatatypeKind::Integer));
3391        }
3392    }
3393
3394    #[test]
3395    fn test_parse_complex_type_with_sequence() {
3396        let schema_xml = r#"<?xml version="1.0"?>
3397            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3398                <xs:complexType name="AddressType">
3399                    <xs:sequence>
3400                        <xs:element name="street" type="xs:string"/>
3401                        <xs:element name="city" type="xs:string"/>
3402                        <xs:element name="zip" type="xs:string"/>
3403                    </xs:sequence>
3404                </xs:complexType>
3405            </xs:schema>"#;
3406
3407        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3408        assert_eq!(schema.components.len(), 1);
3409        assert_eq!(
3410            schema.components[0].component_type,
3411            XsdComponentType::ComplexType
3412        );
3413        assert_eq!(schema.components[0].name, Some("AddressType".to_string()));
3414    }
3415
3416    #[test]
3417    fn test_parse_restriction() {
3418        let schema_xml = r#"<?xml version="1.0"?>
3419            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3420                <xs:simpleType name="AgeType">
3421                    <xs:restriction base="xs:integer">
3422                        <xs:minInclusive value="0"/>
3423                        <xs:maxInclusive value="150"/>
3424                    </xs:restriction>
3425                </xs:simpleType>
3426            </xs:schema>"#;
3427
3428        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3429        assert_eq!(schema.components.len(), 1);
3430        assert_eq!(
3431            schema.components[0].component_type,
3432            XsdComponentType::SimpleType
3433        );
3434
3435        // Should have facets from the restriction
3436        let st = &schema.components[0];
3437        assert_eq!(st.datatype, Some(XsdDatatypeKind::Integer));
3438        assert!(!st.facets.is_empty());
3439    }
3440
3441    #[test]
3442    fn test_parse_enumeration() {
3443        let schema_xml = r#"<?xml version="1.0"?>
3444            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3445                <xs:simpleType name="ColorType">
3446                    <xs:restriction base="xs:string">
3447                        <xs:enumeration value="red"/>
3448                        <xs:enumeration value="green"/>
3449                        <xs:enumeration value="blue"/>
3450                    </xs:restriction>
3451                </xs:simpleType>
3452            </xs:schema>"#;
3453
3454        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3455        assert_eq!(schema.components.len(), 1);
3456        assert_eq!(schema.components[0].name, Some("ColorType".to_string()));
3457    }
3458
3459    #[test]
3460    fn test_parse_min_max_occurs() {
3461        let schema_xml = r#"<?xml version="1.0"?>
3462            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3463                <xs:element name="items">
3464                    <xs:complexType>
3465                        <xs:sequence>
3466                            <xs:element name="item" type="xs:string"
3467                                        minOccurs="0" maxOccurs="unbounded"/>
3468                        </xs:sequence>
3469                    </xs:complexType>
3470                </xs:element>
3471            </xs:schema>"#;
3472
3473        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3474        assert_eq!(schema.components.len(), 1);
3475
3476        // Check the item element inside the sequence
3477        let elem = &schema.components[0];
3478        let ct = elem
3479            .children
3480            .iter()
3481            .find(|c| c.component_type == XsdComponentType::ComplexType);
3482        assert!(ct.is_some());
3483        if let Some(ctc) = ct {
3484            let seq = ctc
3485                .children
3486                .iter()
3487                .find(|c| c.component_type == XsdComponentType::Sequence);
3488            assert!(seq.is_some());
3489            if let Some(seqc) = seq {
3490                assert!(!seqc.children.is_empty());
3491                let item = &seqc.children[0];
3492                assert_eq!(item.min_occurs, 0);
3493                assert_eq!(item.max_occurs, -1);
3494            }
3495        }
3496    }
3497
3498    #[test]
3499    fn test_parse_attribute_default() {
3500        let schema_xml = r#"<?xml version="1.0"?>
3501            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3502                <xs:element name="book">
3503                    <xs:complexType>
3504                        <xs:sequence>
3505                            <xs:element name="title" type="xs:string"/>
3506                        </xs:sequence>
3507                        <xs:attribute name="lang" type="xs:string" default="en"/>
3508                        <xs:attribute name="id" type="xs:integer" use="required"/>
3509                    </xs:complexType>
3510                </xs:element>
3511            </xs:schema>"#;
3512
3513        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3514        let elem = &schema.components[0];
3515        let ct = elem
3516            .children
3517            .iter()
3518            .find(|c| c.component_type == XsdComponentType::ComplexType);
3519        assert!(ct.is_some());
3520        if let Some(ctc) = ct {
3521            let lang_attr = ctc
3522                .attributes
3523                .iter()
3524                .find(|a| a.name.as_deref() == Some("lang"));
3525            assert!(lang_attr.is_some());
3526            if let Some(la) = lang_attr {
3527                assert_eq!(la.min_occurs, 0); // optional
3528            }
3529
3530            let id_attr = ctc
3531                .attributes
3532                .iter()
3533                .find(|a| a.name.as_deref() == Some("id"));
3534            assert!(id_attr.is_some());
3535        }
3536    }
3537
3538    #[test]
3539    fn test_parse_element_with_ref() {
3540        let schema_xml = r#"<?xml version="1.0"?>
3541            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3542                <xs:element name="root">
3543                    <xs:complexType>
3544                        <xs:sequence>
3545                            <xs:element ref="child" minOccurs="0"/>
3546                        </xs:sequence>
3547                    </xs:complexType>
3548                </xs:element>
3549                <xs:element name="child" type="xs:string"/>
3550            </xs:schema>"#;
3551
3552        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3553        assert_eq!(schema.components.len(), 2);
3554        // The ref element inside the sequence
3555        let root = &schema.components[0];
3556        let ct = root
3557            .children
3558            .iter()
3559            .find(|c| c.component_type == XsdComponentType::ComplexType);
3560        assert!(ct.is_some());
3561        if let Some(ctc) = ct {
3562            let seq = ctc
3563                .children
3564                .iter()
3565                .find(|c| c.component_type == XsdComponentType::Sequence);
3566            assert!(seq.is_some());
3567            if let Some(seqc) = seq {
3568                assert!(!seqc.children.is_empty());
3569                let ref_elem = &seqc.children[0];
3570                assert_eq!(ref_elem.ref_name, Some("child".to_string()));
3571            }
3572        }
3573    }
3574
3575    // ── Document Validation Tests ─────────────────────────────────────────
3576
3577    #[test]
3578    fn test_validate_simple_element() {
3579        let schema_xml = r#"<?xml version="1.0"?>
3580            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3581                <xs:element name="name" type="xs:string"/>
3582            </xs:schema>"#;
3583
3584        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3585
3586        let doc = r#"<?xml version="1.0"?>
3587            <name>John Doe</name>"#;
3588
3589        assert!(xsd_validate(&schema, doc).is_ok());
3590    }
3591
3592    #[test]
3593    fn test_validate_integer_element() {
3594        let schema_xml = r#"<?xml version="1.0"?>
3595            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3596                <xs:element name="age" type="xs:integer"/>
3597            </xs:schema>"#;
3598
3599        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3600
3601        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><age>25</age>"#).is_ok());
3602        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><age>not-a-number</age>"#).is_err());
3603    }
3604
3605    #[test]
3606    fn test_validate_complex_element() {
3607        let schema_xml = r#"<?xml version="1.0"?>
3608            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3609                <xs:element name="product">
3610                    <xs:complexType>
3611                        <xs:sequence>
3612                            <xs:element name="name" type="xs:string"/>
3613                            <xs:element name="price" type="xs:decimal"/>
3614                        </xs:sequence>
3615                        <xs:attribute name="id" type="xs:integer" use="required"/>
3616                    </xs:complexType>
3617                </xs:element>
3618            </xs:schema>"#;
3619
3620        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3621
3622        let valid_doc = r#"<?xml version="1.0"?>
3623            <product id="123">
3624                <name>Widget</name>
3625                <price>9.99</price>
3626            </product>"#;
3627
3628        assert!(xsd_validate(&schema, valid_doc).is_ok());
3629    }
3630
3631    #[test]
3632    fn test_validate_missing_required_attribute() {
3633        let schema_xml = r#"<?xml version="1.0"?>
3634            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3635                <xs:element name="product">
3636                    <xs:complexType>
3637                        <xs:sequence>
3638                            <xs:element name="name" type="xs:string"/>
3639                        </xs:sequence>
3640                        <xs:attribute name="id" type="xs:integer" use="required"/>
3641                    </xs:complexType>
3642                </xs:element>
3643            </xs:schema>"#;
3644
3645        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3646
3647        let invalid_doc = r#"<?xml version="1.0"?>
3648            <product>
3649                <name>Widget</name>
3650            </product>"#;
3651
3652        assert!(xsd_validate(&schema, invalid_doc).is_err());
3653    }
3654
3655    #[test]
3656    fn test_validate_enumeration_facet() {
3657        let schema_xml = r#"<?xml version="1.0"?>
3658            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3659                <xs:element name="color">
3660                    <xs:simpleType>
3661                        <xs:restriction base="xs:string">
3662                            <xs:enumeration value="red"/>
3663                            <xs:enumeration value="green"/>
3664                            <xs:enumeration value="blue"/>
3665                        </xs:restriction>
3666                    </xs:simpleType>
3667                </xs:element>
3668            </xs:schema>"#;
3669
3670        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3671
3672        // Note: enumeration validation is currently simplified - the facet
3673        // matches each individual value
3674        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><color>red</color>"#).is_ok());
3675    }
3676
3677    #[test]
3678    fn test_validate_boolean_element() {
3679        let schema_xml = r#"<?xml version="1.0"?>
3680            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3681                <xs:element name="active" type="xs:boolean"/>
3682            </xs:schema>"#;
3683
3684        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3685
3686        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><active>true</active>"#).is_ok());
3687        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><active>false</active>"#).is_ok());
3688        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><active>1</active>"#).is_ok());
3689        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><active>yes</active>"#).is_err());
3690    }
3691
3692    #[test]
3693    fn test_validate_element_with_range_constraint() {
3694        let schema_xml = r#"<?xml version="1.0"?>
3695            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3696                <xs:element name="age">
3697                    <xs:simpleType>
3698                        <xs:restriction base="xs:integer">
3699                            <xs:minInclusive value="0"/>
3700                            <xs:maxInclusive value="150"/>
3701                        </xs:restriction>
3702                    </xs:simpleType>
3703                </xs:element>
3704            </xs:schema>"#;
3705
3706        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3707
3708        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><age>25</age>"#).is_ok());
3709        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><age>0</age>"#).is_ok());
3710        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><age>150</age>"#).is_ok());
3711        // Note: minInclusive/maxInclusive validation currently works for facets
3712    }
3713
3714    #[test]
3715    fn test_validate_optional_element() {
3716        let schema_xml = r#"<?xml version="1.0"?>
3717            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3718                <xs:element name="person">
3719                    <xs:complexType>
3720                        <xs:sequence>
3721                            <xs:element name="name" type="xs:string"/>
3722                            <xs:element name="nickname" type="xs:string" minOccurs="0"/>
3723                        </xs:sequence>
3724                    </xs:complexType>
3725                </xs:element>
3726            </xs:schema>"#;
3727
3728        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3729
3730        let doc_with_nick = r#"<?xml version="1.0"?>
3731            <person>
3732                <name>John</name>
3733                <nickname>Johnny</nickname>
3734            </person>"#;
3735
3736        let doc_without_nick = r#"<?xml version="1.0"?>
3737            <person>
3738                <name>John</name>
3739            </person>"#;
3740
3741        assert!(xsd_validate(&schema, doc_with_nick).is_ok());
3742        assert!(xsd_validate(&schema, doc_without_nick).is_ok());
3743    }
3744
3745    #[test]
3746    fn test_validate_unbounded_element() {
3747        let schema_xml = r#"<?xml version="1.0"?>
3748            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3749                <xs:element name="items">
3750                    <xs:complexType>
3751                        <xs:sequence>
3752                            <xs:element name="item" type="xs:string"
3753                                        minOccurs="0" maxOccurs="unbounded"/>
3754                        </xs:sequence>
3755                    </xs:complexType>
3756                </xs:element>
3757            </xs:schema>"#;
3758
3759        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3760
3761        let doc = r#"<?xml version="1.0"?>
3762            <items>
3763                <item>one</item>
3764                <item>two</item>
3765                <item>three</item>
3766            </items>"#;
3767
3768        assert!(xsd_validate(&schema, doc).is_ok());
3769    }
3770
3771    #[test]
3772    fn test_validate_date_element() {
3773        let schema_xml = r#"<?xml version="1.0"?>
3774            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3775                <xs:element name="birthDate" type="xs:date"/>
3776            </xs:schema>"#;
3777
3778        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3779
3780        assert!(xsd_validate(
3781            &schema,
3782            r#"<?xml version="1.0"?><birthDate>1990-01-15</birthDate>"#
3783        )
3784        .is_ok());
3785        assert!(xsd_validate(
3786            &schema,
3787            r#"<?xml version="1.0"?><birthDate>not-a-date</birthDate>"#
3788        )
3789        .is_err());
3790    }
3791
3792    #[test]
3793    fn test_validate_choice() {
3794        let schema_xml = r#"<?xml version="1.0"?>
3795            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3796                <xs:element name="contact">
3797                    <xs:complexType>
3798                        <xs:choice>
3799                            <xs:element name="email" type="xs:string"/>
3800                            <xs:element name="phone" type="xs:string"/>
3801                        </xs:choice>
3802                    </xs:complexType>
3803                </xs:element>
3804            </xs:schema>"#;
3805
3806        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3807
3808        assert!(xsd_validate(
3809            &schema,
3810            r#"<?xml version="1.0"?><contact><email>a@b.com</email></contact>"#
3811        )
3812        .is_ok());
3813        assert!(xsd_validate(
3814            &schema,
3815            r#"<?xml version="1.0"?><contact><phone>555-1234</phone></contact>"#
3816        )
3817        .is_ok());
3818    }
3819
3820    #[test]
3821    fn test_validate_positive_integer_constraint() {
3822        let schema_xml = r#"<?xml version="1.0"?>
3823            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3824                <xs:element name="quantity" type="xs:positiveInteger"/>
3825            </xs:schema>"#;
3826
3827        let schema = xsd_parse(schema_xml).expect("Failed to parse schema");
3828
3829        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><quantity>1</quantity>"#).is_ok());
3830        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><quantity>0</quantity>"#).is_err());
3831        assert!(xsd_validate(&schema, r#"<?xml version="1.0"?><quantity>-1</quantity>"#).is_err());
3832    }
3833
3834    // ── C ABI Tests ───────────────────────────────────────────────────────
3835
3836    #[test]
3837    fn test_xml_schema_new_mem_parser_ctxt() {
3838        let schema_xml = r#"<?xml version="1.0"?>
3839            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3840                <xs:element name="name" type="xs:string"/>
3841            </xs:schema>"#;
3842
3843        let ctxt = unsafe {
3844            xmlSchemaNewMemParserCtxt(
3845                schema_xml.as_ptr() as *const c_char,
3846                schema_xml.len() as c_int,
3847            )
3848        };
3849        assert!(!ctxt.is_null());
3850
3851        let schema = unsafe { xmlSchemaParse(ctxt) };
3852        assert!(!schema.is_null());
3853
3854        unsafe {
3855            xmlSchemaFree(schema);
3856        }
3857    }
3858
3859    #[test]
3860    fn test_xml_schema_validate_doc() {
3861        let schema_xml = r#"<?xml version="1.0"?>
3862            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3863                <xs:element name="name" type="xs:string"/>
3864            </xs:schema>"#;
3865
3866        let doc_xml = r#"<?xml version="1.0"?>
3867            <name>John Doe</name>"#;
3868
3869        let ctxt = unsafe {
3870            xmlSchemaNewMemParserCtxt(
3871                schema_xml.as_ptr() as *const c_char,
3872                schema_xml.len() as c_int,
3873            )
3874        };
3875        let schema = unsafe { xmlSchemaParse(ctxt) };
3876        let valid_ctxt = unsafe { xmlSchemaNewValidCtxt(schema) };
3877
3878        let doc = unsafe {
3879            crate::abi::exports_xml2::xmlReadMemory(
3880                doc_xml.as_ptr() as *const c_char,
3881                doc_xml.len() as c_int,
3882                b"test.xml\0".as_ptr() as *const c_char,
3883                ptr::null(),
3884                0,
3885            )
3886        };
3887
3888        let result = unsafe { xmlSchemaValidateDoc(valid_ctxt, doc) };
3889        assert_eq!(result, 0);
3890
3891        unsafe {
3892            xmlSchemaFreeValidCtxt(valid_ctxt);
3893            xmlSchemaFree(schema);
3894            crate::abi::exports_xml2::xmlFreeDoc(doc);
3895        }
3896    }
3897
3898    #[test]
3899    fn test_xml_schema_validate_invalid_doc() {
3900        let schema_xml = r#"<?xml version="1.0"?>
3901            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3902                <xs:element name="age" type="xs:integer"/>
3903            </xs:schema>"#;
3904
3905        let doc_xml = r#"<?xml version="1.0"?>
3906            <age>not-a-number</age>"#;
3907
3908        let ctxt = unsafe {
3909            xmlSchemaNewMemParserCtxt(
3910                schema_xml.as_ptr() as *const c_char,
3911                schema_xml.len() as c_int,
3912            )
3913        };
3914        let schema = unsafe { xmlSchemaParse(ctxt) };
3915        let valid_ctxt = unsafe { xmlSchemaNewValidCtxt(schema) };
3916
3917        let doc = unsafe {
3918            crate::abi::exports_xml2::xmlReadMemory(
3919                doc_xml.as_ptr() as *const c_char,
3920                doc_xml.len() as c_int,
3921                b"test.xml\0".as_ptr() as *const c_char,
3922                ptr::null(),
3923                0,
3924            )
3925        };
3926
3927        let result = unsafe { xmlSchemaValidateDoc(valid_ctxt, doc) };
3928        assert_ne!(result, 0); // Should have errors
3929
3930        unsafe {
3931            xmlSchemaFreeValidCtxt(valid_ctxt);
3932            xmlSchemaFree(schema);
3933            crate::abi::exports_xml2::xmlFreeDoc(doc);
3934        }
3935    }
3936
3937    #[test]
3938    fn test_xml_schema_new_valid_ctxt_null() {
3939        let ctxt = unsafe { xmlSchemaNewValidCtxt(ptr::null_mut()) };
3940        assert!(!ctxt.is_null());
3941        unsafe { xmlSchemaFreeValidCtxt(ctxt) };
3942    }
3943
3944    #[test]
3945    fn test_xml_schema_free_null() {
3946        unsafe {
3947            xmlSchemaFree(ptr::null_mut());
3948            xmlSchemaFreeParserCtxt(ptr::null_mut());
3949            xmlSchemaFreeValidCtxt(ptr::null_mut());
3950        }
3951    }
3952
3953    #[test]
3954    fn test_xml_schema_new_parser_ctxt_null() {
3955        let ctxt = unsafe { xmlSchemaNewParserCtxt(ptr::null()) };
3956        assert!(!ctxt.is_null());
3957        // Clean up
3958        unsafe {
3959            allocator::xmlFree(ctxt);
3960        }
3961    }
3962
3963    #[test]
3964    fn test_datatype_parse_kind() {
3965        assert_eq!(
3966            parse_datatype_kind("xs:string"),
3967            Some(XsdDatatypeKind::String)
3968        );
3969        assert_eq!(parse_datatype_kind("string"), Some(XsdDatatypeKind::String));
3970        assert_eq!(
3971            parse_datatype_kind("xs:integer"),
3972            Some(XsdDatatypeKind::Integer)
3973        );
3974        assert_eq!(
3975            parse_datatype_kind("xs:boolean"),
3976            Some(XsdDatatypeKind::Boolean)
3977        );
3978        assert_eq!(
3979            parse_datatype_kind("xs:decimal"),
3980            Some(XsdDatatypeKind::Decimal)
3981        );
3982        assert_eq!(
3983            parse_datatype_kind("xs:float"),
3984            Some(XsdDatatypeKind::Float)
3985        );
3986        assert_eq!(
3987            parse_datatype_kind("xs:double"),
3988            Some(XsdDatatypeKind::Double)
3989        );
3990        assert_eq!(parse_datatype_kind("xs:date"), Some(XsdDatatypeKind::Date));
3991        assert_eq!(
3992            parse_datatype_kind("xs:dateTime"),
3993            Some(XsdDatatypeKind::DateTime)
3994        );
3995        assert_eq!(parse_datatype_kind("xs:time"), Some(XsdDatatypeKind::Time));
3996        assert_eq!(
3997            parse_datatype_kind("xs:hexBinary"),
3998            Some(XsdDatatypeKind::HexBinary)
3999        );
4000        assert_eq!(
4001            parse_datatype_kind("xs:base64Binary"),
4002            Some(XsdDatatypeKind::Base64Binary)
4003        );
4004        assert_eq!(
4005            parse_datatype_kind("xs:anyURI"),
4006            Some(XsdDatatypeKind::AnyURI)
4007        );
4008        assert_eq!(
4009            parse_datatype_kind("xs:QName"),
4010            Some(XsdDatatypeKind::QName)
4011        );
4012        assert_eq!(
4013            parse_datatype_kind("xs:normalizedString"),
4014            Some(XsdDatatypeKind::NormalizedString)
4015        );
4016        assert_eq!(
4017            parse_datatype_kind("xs:token"),
4018            Some(XsdDatatypeKind::Token)
4019        );
4020        assert_eq!(
4021            parse_datatype_kind("xs:language"),
4022            Some(XsdDatatypeKind::Language)
4023        );
4024        assert_eq!(parse_datatype_kind("xs:Name"), Some(XsdDatatypeKind::Name));
4025        assert_eq!(
4026            parse_datatype_kind("xs:NCName"),
4027            Some(XsdDatatypeKind::NCName)
4028        );
4029        assert_eq!(parse_datatype_kind("xs:ID"), Some(XsdDatatypeKind::Id));
4030        assert_eq!(
4031            parse_datatype_kind("xs:IDREF"),
4032            Some(XsdDatatypeKind::Idref)
4033        );
4034        assert_eq!(
4035            parse_datatype_kind("xs:integer"),
4036            Some(XsdDatatypeKind::Integer)
4037        );
4038        assert_eq!(parse_datatype_kind("xs:long"), Some(XsdDatatypeKind::Long));
4039        assert_eq!(parse_datatype_kind("xs:int"), Some(XsdDatatypeKind::Int));
4040        assert_eq!(
4041            parse_datatype_kind("xs:short"),
4042            Some(XsdDatatypeKind::Short)
4043        );
4044        assert_eq!(parse_datatype_kind("xs:byte"), Some(XsdDatatypeKind::Byte));
4045        assert_eq!(
4046            parse_datatype_kind("xs:positiveInteger"),
4047            Some(XsdDatatypeKind::PositiveInteger)
4048        );
4049        assert_eq!(
4050            parse_datatype_kind("xs:negativeInteger"),
4051            Some(XsdDatatypeKind::NegativeInteger)
4052        );
4053        assert_eq!(parse_datatype_kind("unknown"), None);
4054    }
4055}