1use crate::ids::{SimpleTypeKey, TypeKey};
8use crate::parser::frames::{ComplexContentResult, DerivationMethod, SimpleTypeVariety};
9use crate::schema::SchemaSet;
10use crate::types::facets::{normalize_whitespace, FacetSet, WhitespaceMode};
11use crate::types::validators::VALIDATOR_REGISTRY;
12use crate::types::value::{XmlValue, XmlValueKind};
13use crate::types::XmlTypeCode;
14
15use super::errors::{self, ValidationError};
16
17#[derive(Debug)]
19pub struct SimpleTypeResult {
20 pub typed_value: XmlValue,
22 pub member_type: Option<TypeKey>,
24 pub normalized_value: Option<String>,
26}
27
28pub fn validate_simple_type(
34 value: &str,
35 type_key: TypeKey,
36 schema_set: &SchemaSet,
37) -> Result<SimpleTypeResult, ValidationError> {
38 validate_simple_type_inner(value, type_key, schema_set)
39}
40
41pub fn fixed_values_equal(
47 a: &str,
48 b: &str,
49 type_key: Option<TypeKey>,
50 schema_set: &SchemaSet,
51) -> bool {
52 if a == b {
53 return true;
54 }
55 let Some(tk) = type_key else {
56 return false;
57 };
58 let Ok(a_result) = validate_simple_type(a, tk, schema_set) else {
59 return false;
60 };
61 let Ok(b_result) = validate_simple_type(b, tk, schema_set) else {
62 return false;
63 };
64 a_result.typed_value == b_result.typed_value
65}
66
67pub fn fixed_matches_typed(
72 instance_raw: &str,
73 instance_typed: &XmlValue,
74 fixed: &str,
75 type_key: Option<TypeKey>,
76 schema_set: &SchemaSet,
77) -> bool {
78 if instance_raw == fixed {
79 return true;
80 }
81 let Some(tk) = type_key else {
82 return false;
83 };
84 let Ok(fixed_result) = validate_simple_type(fixed, tk, schema_set) else {
85 return false;
86 };
87 *instance_typed == fixed_result.typed_value
88}
89
90fn validate_simple_type_inner(
91 value: &str,
92 type_key: TypeKey,
93 schema_set: &SchemaSet,
94) -> Result<SimpleTypeResult, ValidationError> {
95 match type_key {
96 TypeKey::Simple(sk) => {
97 let st_data = match schema_set.arenas.simple_types.get(sk) {
98 Some(d) => d,
99 None => {
100 return Err(errors::error(
101 "cvc-simple-type",
102 "Simple type definition not found",
103 None,
104 ));
105 }
106 };
107 match st_data.variety {
108 SimpleTypeVariety::Atomic => validate_atomic_type(value, sk, schema_set),
109 SimpleTypeVariety::List => validate_list_type(value, sk, schema_set),
110 SimpleTypeVariety::Union => validate_union_type(value, sk, schema_set),
111 }
112 }
113 TypeKey::Complex(ck) => {
114 let ct_data = match schema_set.arenas.complex_types.get(ck) {
118 Some(d) => d,
119 None => {
120 return Err(errors::error(
121 "cvc-simple-type",
122 "Complex type definition not found",
123 None,
124 ));
125 }
126 };
127 let result = if let Some(inline_st) = ct_data.resolved_simple_content_type {
128 validate_simple_type_inner(value, inline_st, schema_set)?
129 } else if let Some(base_key) = ct_data.resolved_base_type {
130 validate_simple_type_inner(value, base_key, schema_set)?
131 } else {
132 SimpleTypeResult {
134 typed_value: XmlValue::untyped(value),
135 member_type: None,
136 normalized_value: None,
137 }
138 };
139 if let ComplexContentResult::Simple(sc) = &ct_data.content {
146 if sc.derivation == DerivationMethod::Restriction && !sc.facets.is_empty() {
147 let lex = result.normalized_value.as_deref().unwrap_or(value);
148 sc.facets.validate_string(lex).map_err(|e| {
149 let code = errors::facet_constraint_code(&e);
150 errors::from_facet_error(code, e, None)
151 })?;
152 }
153 }
154 Ok(result)
155 }
156 }
157}
158
159fn resolve_type_code(sk: SimpleTypeKey, schema_set: &SchemaSet) -> Option<XmlTypeCode> {
166 let mut current = sk;
167 for _ in 0..100 {
168 if let Some(code) = schema_set.get_type_code(current) {
169 return Some(code);
170 }
171 let st_data = schema_set.arenas.simple_types.get(current)?;
172 match st_data.resolved_base_type {
173 Some(TypeKey::Simple(base)) => current = base,
174 _ => return None,
175 }
176 }
177 None
178}
179
180fn has_enumeration_in_chain(sk: SimpleTypeKey, schema_set: &SchemaSet) -> bool {
185 let mut current = sk;
186 for _ in 0..100 {
187 let Some(st_data) = schema_set.arenas.simple_types.get(current) else {
188 return false;
189 };
190 if st_data.facets.enumeration.is_some() {
191 return true;
192 }
193 match st_data.resolved_base_type {
194 Some(TypeKey::Simple(base)) => current = base,
195 _ => return false,
196 }
197 }
198 false
199}
200
201fn collect_facets(sk: SimpleTypeKey, schema_set: &SchemaSet) -> FacetSet {
206 let st_data = match schema_set.arenas.simple_types.get(sk) {
207 Some(d) => d,
208 None => return FacetSet::new(),
209 };
210 let mut facets = st_data.facets.clone();
211
212 let mut current_base = st_data.resolved_base_type;
214 for _ in 0..100 {
215 match current_base {
216 Some(TypeKey::Simple(base_sk)) => {
217 if let Some(base_data) = schema_set.arenas.simple_types.get(base_sk) {
218 facets.inherit_from(&base_data.facets);
219 current_base = base_data.resolved_base_type;
220 } else {
221 break;
222 }
223 }
224 _ => break,
225 }
226 }
227 facets
228}
229
230fn validate_atomic_type(
235 value: &str,
236 sk: SimpleTypeKey,
237 schema_set: &SchemaSet,
238) -> Result<SimpleTypeResult, ValidationError> {
239 let type_code = resolve_type_code(sk, schema_set);
240
241 match type_code {
243 Some(XmlTypeCode::AnySimpleType) | Some(XmlTypeCode::AnyAtomicType) | None => {
244 return Ok(SimpleTypeResult {
245 typed_value: XmlValue::untyped(value),
246 member_type: None,
247 normalized_value: None,
248 });
249 }
250 _ => {}
251 }
252 let type_code = type_code.unwrap();
253
254 let validator = match VALIDATOR_REGISTRY.get_by_code(type_code) {
255 Some(v) => v,
256 None => {
257 return Err(errors::error(
258 "cvc-datatype-valid",
259 format!("No validator for type code {:?}", type_code),
260 None,
261 ));
262 }
263 };
264
265 let facets = collect_facets(sk, schema_set);
266
267 if type_code == XmlTypeCode::Notation && !has_enumeration_in_chain(sk, schema_set) {
273 return Err(errors::error(
274 "cvc-datatype-valid",
275 "xs:NOTATION cannot be used directly for instance validation; \
276 only datatypes derived from NOTATION by restriction with an \
277 enumeration facet are permitted (§3.2)",
278 None,
279 ));
280 }
281
282 let effective_ws = facets
284 .whitespace
285 .as_ref()
286 .map(|w| w.value)
287 .unwrap_or_else(|| validator.whitespace());
288 let normalized = normalize_whitespace(value, effective_ws);
289
290 let typed_value = if facets.is_empty() {
291 validator.validate(value)
292 } else {
293 validator.validate_with_facets(value, &facets)
294 };
295
296 match typed_value {
297 Ok(mut val) => {
298 if val.schema_type.is_none() {
300 val.schema_type = Some(sk);
301 }
302 if schema_set.is_xsd10() {
308 check_xsd10_year_zero(&val, value)?;
309 check_xsd10_plus_inf(&val, value)?;
310 }
311 #[cfg(feature = "xsd11")]
313 evaluate_assertion_facets(&val, &facets, schema_set, Some(sk))?;
314 Ok(SimpleTypeResult {
315 typed_value: val,
316 member_type: None,
317 normalized_value: Some(normalized),
318 })
319 }
320 Err(type_err) => {
321 let code = errors::value_error_constraint_code(&type_err);
322 Err(errors::from_value_error(code, type_err, None))
323 }
324 }
325}
326
327fn check_xsd10_plus_inf(val: &XmlValue, raw: &str) -> Result<(), ValidationError> {
332 use crate::types::value::XmlAtomicValue;
333 let is_float_or_double = matches!(
334 val.value,
335 XmlValueKind::Atomic(XmlAtomicValue::Float(_)) | XmlValueKind::Atomic(XmlAtomicValue::Double(_))
336 );
337 if !is_float_or_double {
338 return Ok(());
339 }
340 let collapsed =
341 crate::types::facets::normalize_whitespace(raw, crate::types::facets::WhitespaceMode::Collapse);
342 if collapsed == "+INF" {
343 Err(errors::error(
344 "cvc-datatype-valid",
345 format!("'+INF' is not a valid XSD 1.0 float/double lexical form (value '{}')", raw),
346 None,
347 ))
348 } else {
349 Ok(())
350 }
351}
352
353fn check_xsd10_year_zero(val: &XmlValue, raw: &str) -> Result<(), ValidationError> {
355 use crate::types::value::XmlAtomicValue;
356 let year_zero = match &val.value {
357 XmlValueKind::Atomic(XmlAtomicValue::DateTime(v)) => v.year == 0,
358 XmlValueKind::Atomic(XmlAtomicValue::Date(v)) => v.year == 0,
359 XmlValueKind::Atomic(XmlAtomicValue::GYear(v)) => v.year == 0,
360 XmlValueKind::Atomic(XmlAtomicValue::GYearMonth(v)) => v.year == 0,
361 _ => false,
362 };
363 if year_zero {
364 Err(errors::error(
365 "cvc-datatype-valid",
366 format!(
367 "Year 0000 is not a valid XSD 1.0 lexical form (value '{}'); use '-0001' for 1 BCE",
368 raw
369 ),
370 None,
371 ))
372 } else {
373 Ok(())
374 }
375}
376
377fn validate_list_type(
382 value: &str,
383 sk: SimpleTypeKey,
384 schema_set: &SchemaSet,
385) -> Result<SimpleTypeResult, ValidationError> {
386 let st_data = match schema_set.arenas.simple_types.get(sk) {
387 Some(d) => d,
388 None => {
389 return Err(errors::error(
390 "cvc-simple-type",
391 "List type definition not found",
392 None,
393 ));
394 }
395 };
396
397 let item_type_key = match st_data.resolved_item_type {
398 Some(tk) => tk,
399 None => {
400 if let Some(deferred) = &st_data.deferred_item_type_error {
401 return Err(errors::error(
402 "src-resolve",
403 deferred.message.clone(),
404 None,
405 ));
406 }
407 return Ok(SimpleTypeResult {
409 typed_value: XmlValue::untyped(value),
410 member_type: None,
411 normalized_value: None,
412 });
413 }
414 };
415
416 let normalized = normalize_whitespace(value, WhitespaceMode::Collapse);
418 let items_str: Vec<&str> = if normalized.is_empty() {
419 Vec::new()
420 } else {
421 normalized.split(' ').collect()
422 };
423
424 let mut items = Vec::with_capacity(items_str.len());
426 let mut item_type_code = XmlTypeCode::UntypedAtomic;
427 for item_str in &items_str {
428 let result = validate_simple_type_inner(item_str, item_type_key, schema_set)?;
429 item_type_code = result.typed_value.type_code;
430 match result.typed_value.value {
431 XmlValueKind::Atomic(atom) => items.push(atom),
432 XmlValueKind::UntypedAtomic(s) => {
433 items.push(crate::types::value::XmlAtomicValue::String(s));
434 }
435 _ => {
436 items.push(crate::types::value::XmlAtomicValue::String(
437 result.typed_value.to_string_value(),
438 ));
439 }
440 }
441 }
442
443 let facets = collect_facets(sk, schema_set);
445 if !facets.is_empty() {
446 facets
448 .validate_list_length(items_str.len() as u64)
449 .map_err(|e| {
450 let code = errors::facet_constraint_code(&e);
451 errors::from_facet_error(code, e, None)
452 })?;
453
454 facets
456 .validate_string_patterns_enums(&normalized)
457 .map_err(|e| {
458 let code = errors::facet_constraint_code(&e);
459 errors::from_facet_error(code, e, None)
460 })?;
461 }
462
463 let list_type_code = resolve_type_code(sk, schema_set)
467 .filter(|code| code.is_list())
468 .unwrap_or(item_type_code);
469
470 let typed_value = XmlValue::with_schema_type(
471 list_type_code,
472 sk,
473 XmlValueKind::List {
474 item_type: item_type_code,
475 items,
476 },
477 );
478
479 #[cfg(feature = "xsd11")]
481 {
482 let item_sk = item_type_key.as_simple();
483 evaluate_assertion_facets(&typed_value, &facets, schema_set, item_sk)?;
484 }
485
486 Ok(SimpleTypeResult {
487 typed_value,
488 member_type: None,
489 normalized_value: Some(normalized),
490 })
491}
492
493fn validate_union_type(
498 value: &str,
499 sk: SimpleTypeKey,
500 schema_set: &SchemaSet,
501) -> Result<SimpleTypeResult, ValidationError> {
502 let st_data = match schema_set.arenas.simple_types.get(sk) {
503 Some(d) => d,
504 None => {
505 return Err(errors::error(
506 "cvc-simple-type",
507 "Union type definition not found",
508 None,
509 ));
510 }
511 };
512
513 for &member_key in &st_data.resolved_member_types {
515 if let Ok(result) = validate_simple_type_inner(value, member_key, schema_set) {
516 let facets = collect_facets(sk, schema_set);
518 if !facets.is_empty() {
519 let check_value = result.typed_value.to_string_value();
520 let lex_value = normalize_whitespace(value, WhitespaceMode::Collapse);
523 facets.validate_patterns_only(&lex_value).map_err(|e| {
524 let code = errors::facet_constraint_code(&e);
525 errors::from_facet_error(code, e, None)
526 })?;
527 let type_code = result.typed_value.type_code;
531 facets
532 .validate_enum_value_space(
533 |s| {
534 VALIDATOR_REGISTRY
535 .validate(type_code, s)
536 .map(|v| v.to_string_value() == check_value)
537 .unwrap_or(false)
538 },
539 &check_value,
540 )
541 .map_err(|e| {
542 let code = errors::facet_constraint_code(&e);
543 errors::from_facet_error(code, e, None)
544 })?;
545 }
546
547 let mut inner = result.typed_value;
549 if inner.schema_type.is_none() {
550 inner.schema_type = member_key.as_simple();
551 }
552
553 #[cfg(feature = "xsd11")]
555 {
556 let item_sk = member_key.as_simple().and_then(|sk| {
557 crate::types::sequence::resolve_list_item_schema_type(sk, schema_set)
558 });
559 evaluate_assertion_facets(&inner, &facets, schema_set, item_sk)?;
560 }
561
562 return Ok(SimpleTypeResult {
563 normalized_value: result.normalized_value,
564 typed_value: XmlValue::with_schema_type(
565 inner.type_code,
566 sk,
567 XmlValueKind::Union(Box::new(inner)),
568 ),
569 member_type: Some(member_key),
570 });
571 }
572 }
573
574 Err(errors::error(
576 "cvc-simple-type",
577 format!(
578 "Value '{}' does not match any member type of the union",
579 value
580 ),
581 None,
582 ))
583}
584
585#[cfg(feature = "xsd11")]
590fn resolve_assertion_default_ns(
591 raw: Option<&str>,
592 source: Option<&crate::parser::location::SourceRef>,
593 ns_snapshot: &crate::namespace::context::NamespaceContextSnapshot,
594 schema_set: &SchemaSet,
595) -> Option<crate::ids::NameId> {
596 let doc = source.and_then(|s| schema_set.documents.get(s.doc_id as usize));
598
599 let effective = if let Some(raw) = raw {
601 Some(raw.to_string())
602 } else {
603 doc.and_then(|d| d.xpath_default_namespace)
604 .map(|id| schema_set.name_table.resolve(id))
605 };
606
607 match effective.as_deref() {
608 Some("##defaultNamespace") => ns_snapshot.default_ns,
609 Some("##targetNamespace") => doc.and_then(|d| d.target_namespace),
610 Some("##local") | None => None,
611 Some(uri) => Some(schema_set.name_table.add(uri)),
612 }
613}
614
615#[cfg(feature = "xsd11")]
621fn evaluate_assertion_facets(
622 typed_value: &XmlValue,
623 facets: &FacetSet,
624 schema_set: &SchemaSet,
625 item_schema_type: Option<SimpleTypeKey>,
626) -> Result<(), ValidationError> {
627 use crate::xpath::api::XPathExpr;
628 use crate::xpath::functions::effective_boolean_value;
629 use crate::xpath::{RoXmlNavigator, XPathContext};
630
631 if facets.assertions.is_empty() {
632 return Ok(());
633 }
634
635 for assertion in &facets.assertions {
636 if assertion.test.is_empty() {
637 continue;
638 }
639
640 let location = assertion
642 .source
643 .as_ref()
644 .and_then(|s| schema_set.source_maps.locate(s));
645
646 let ctx = XPathContext::new(&schema_set.name_table)
648 .with_namespaces(assertion.ns_snapshot.clone())
649 .with_schema_set(schema_set);
650
651 let ctx = if let Some(default_ns) = resolve_assertion_default_ns(
653 assertion.xpath_default_namespace.as_deref(),
654 assertion.source.as_ref(),
655 &assertion.ns_snapshot,
656 schema_set,
657 ) {
658 ctx.with_default_element_ns(default_ns)
659 } else {
660 ctx
661 };
662
663 let expr =
665 XPathExpr::compile_with_vars(&assertion.test, &ctx, &["value"]).map_err(|e| {
666 errors::error(
667 "cvc-assertion",
668 format!(
669 "Failed to compile assertion test '{}': {}",
670 assertion.test, e
671 ),
672 location.clone(),
673 )
674 })?;
675
676 let xpath_value = typed_value.to_xpath_value::<RoXmlNavigator<'static>>(item_schema_type);
678
679 let result = expr
681 .evaluator(&ctx)
682 .run_with::<RoXmlNavigator<'static>, _>(|eval| {
683 eval.set_variable_by_name("value", xpath_value).unwrap();
684 })
685 .map_err(|e| {
686 errors::error(
687 "cvc-assertion",
688 format!(
689 "Failed to evaluate assertion test '{}': {}",
690 assertion.test, e
691 ),
692 location.clone(),
693 )
694 })?;
695
696 let ebv = effective_boolean_value(&result).map_err(|e| {
698 errors::error(
699 "cvc-assertion",
700 format!(
701 "Failed to compute boolean value for assertion '{}': {}",
702 assertion.test, e
703 ),
704 location.clone(),
705 )
706 })?;
707
708 if !ebv {
709 return Err(errors::error(
710 "cvc-assertion",
711 format!(
712 "Assertion '{}' failed for value '{}'",
713 assertion.test,
714 typed_value.to_string_value()
715 ),
716 location,
717 ));
718 }
719 }
720
721 Ok(())
722}
723
724#[cfg(test)]
729mod tests {
730 use super::*;
731 use crate::pipeline::load_and_process_schema;
732
733 fn load_schema(xsd: &str) -> SchemaSet {
734 let mut schema_set = SchemaSet::new();
735 load_and_process_schema(xsd.as_bytes(), "test.xsd", &mut schema_set, None)
736 .expect("failed to load schema");
737 schema_set
738 }
739
740 fn element_type(schema_set: &SchemaSet, local_name: &str) -> TypeKey {
742 let name_id = schema_set.name_table.add(local_name);
743 let elem_key = schema_set
744 .lookup_element(None, name_id)
745 .expect("element not found");
746 schema_set.arenas.elements[elem_key]
747 .resolved_type
748 .expect("element has no resolved type")
749 }
750
751 #[test]
752 fn test_builtin_string_accepts_anything() {
753 let schema = load_schema(
754 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
755 <xs:element name="e" type="xs:string"/>
756 </xs:schema>"#,
757 );
758 let tk = element_type(&schema, "e");
759 let result = validate_simple_type("hello world", tk, &schema).unwrap();
760 assert!(result.member_type.is_none());
761 assert_eq!(result.typed_value.type_code, XmlTypeCode::String);
762 }
763
764 #[test]
765 fn test_builtin_integer_valid() {
766 let schema = load_schema(
767 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
768 <xs:element name="e" type="xs:integer"/>
769 </xs:schema>"#,
770 );
771 let tk = element_type(&schema, "e");
772 let result = validate_simple_type("42", tk, &schema).unwrap();
773 assert_eq!(result.typed_value.type_code, XmlTypeCode::Integer);
774 }
775
776 #[test]
777 fn test_builtin_integer_invalid() {
778 let schema = load_schema(
779 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
780 <xs:element name="e" type="xs:integer"/>
781 </xs:schema>"#,
782 );
783 let tk = element_type(&schema, "e");
784 let err = validate_simple_type("abc", tk, &schema).unwrap_err();
785 assert_eq!(err.constraint, "cvc-datatype-valid");
786 }
787
788 #[test]
789 fn test_builtin_boolean_valid() {
790 let schema = load_schema(
791 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
792 <xs:element name="e" type="xs:boolean"/>
793 </xs:schema>"#,
794 );
795 let tk = element_type(&schema, "e");
796 for v in &["true", "false", "1", "0"] {
797 assert!(
798 validate_simple_type(v, tk, &schema).is_ok(),
799 "failed for '{}'",
800 v
801 );
802 }
803 }
804
805 #[test]
806 fn test_builtin_boolean_invalid() {
807 let schema = load_schema(
808 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
809 <xs:element name="e" type="xs:boolean"/>
810 </xs:schema>"#,
811 );
812 let tk = element_type(&schema, "e");
813 assert!(validate_simple_type("yes", tk, &schema).is_err());
814 }
815
816 #[test]
817 fn test_user_defined_restriction_min_max() {
818 let schema = load_schema(
819 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
820 <xs:simpleType name="score">
821 <xs:restriction base="xs:integer">
822 <xs:minInclusive value="0"/>
823 <xs:maxInclusive value="100"/>
824 </xs:restriction>
825 </xs:simpleType>
826 <xs:element name="e" type="score"/>
827 </xs:schema>"#,
828 );
829 let tk = element_type(&schema, "e");
830
831 assert!(validate_simple_type("50", tk, &schema).is_ok());
832 assert!(validate_simple_type("0", tk, &schema).is_ok());
833 assert!(validate_simple_type("100", tk, &schema).is_ok());
834
835 let err = validate_simple_type("101", tk, &schema).unwrap_err();
836 assert_eq!(err.constraint, "cvc-maxInclusive-valid");
837
838 let err = validate_simple_type("-1", tk, &schema).unwrap_err();
839 assert_eq!(err.constraint, "cvc-minInclusive-valid");
840 }
841
842 #[test]
843 fn test_enumeration_facet() {
844 let schema = load_schema(
845 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
846 <xs:simpleType name="color">
847 <xs:restriction base="xs:string">
848 <xs:enumeration value="red"/>
849 <xs:enumeration value="green"/>
850 <xs:enumeration value="blue"/>
851 </xs:restriction>
852 </xs:simpleType>
853 <xs:element name="e" type="color"/>
854 </xs:schema>"#,
855 );
856 let tk = element_type(&schema, "e");
857
858 assert!(validate_simple_type("red", tk, &schema).is_ok());
859 assert!(validate_simple_type("green", tk, &schema).is_ok());
860
861 let err = validate_simple_type("yellow", tk, &schema).unwrap_err();
862 assert_eq!(err.constraint, "cvc-enumeration-valid");
863 }
864
865 #[test]
866 fn test_pattern_facet() {
867 let schema = load_schema(
868 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
869 <xs:simpleType name="zipCode">
870 <xs:restriction base="xs:string">
871 <xs:pattern value="[0-9]{5}"/>
872 </xs:restriction>
873 </xs:simpleType>
874 <xs:element name="e" type="zipCode"/>
875 </xs:schema>"#,
876 );
877 let tk = element_type(&schema, "e");
878
879 assert!(validate_simple_type("12345", tk, &schema).is_ok());
880
881 let err = validate_simple_type("1234", tk, &schema).unwrap_err();
882 assert_eq!(err.constraint, "cvc-pattern-valid");
883 }
884
885 #[test]
886 fn test_empty_string_against_integer() {
887 let schema = load_schema(
888 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
889 <xs:element name="e" type="xs:integer"/>
890 </xs:schema>"#,
891 );
892 let tk = element_type(&schema, "e");
893 assert!(validate_simple_type("", tk, &schema).is_err());
894 }
895
896 #[test]
897 fn test_empty_string_against_string() {
898 let schema = load_schema(
899 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
900 <xs:element name="e" type="xs:string"/>
901 </xs:schema>"#,
902 );
903 let tk = element_type(&schema, "e");
904 assert!(validate_simple_type("", tk, &schema).is_ok());
905 }
906
907 #[test]
908 fn test_list_type() {
909 let schema = load_schema(
910 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
911 <xs:simpleType name="intList">
912 <xs:list itemType="xs:integer"/>
913 </xs:simpleType>
914 <xs:element name="e" type="intList"/>
915 </xs:schema>"#,
916 );
917 let tk = element_type(&schema, "e");
918
919 let result = validate_simple_type("1 2 3", tk, &schema).unwrap();
920 assert!(result.typed_value.is_list());
921
922 assert!(validate_simple_type("1 abc 3", tk, &schema).is_err());
924 }
925
926 #[test]
927 fn test_union_type() {
928 let schema = load_schema(
929 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
930 <xs:simpleType name="intOrString">
931 <xs:union memberTypes="xs:integer xs:string"/>
932 </xs:simpleType>
933 <xs:element name="e" type="intOrString"/>
934 </xs:schema>"#,
935 );
936 let tk = element_type(&schema, "e");
937
938 let result = validate_simple_type("42", tk, &schema).unwrap();
940 assert!(result.member_type.is_some());
941 assert_eq!(result.typed_value.type_code, XmlTypeCode::Integer);
942
943 let result = validate_simple_type("hello", tk, &schema).unwrap();
945 assert!(result.member_type.is_some());
946 assert_eq!(result.typed_value.type_code, XmlTypeCode::String);
947 }
948}
949
950#[cfg(all(test, feature = "xsd11"))]
955mod xsd11_tests {
956 use super::*;
957 use crate::navigator::RoXmlNavigator;
958 use crate::pipeline::load_and_process_schema;
959 use crate::types::sequence::resolve_list_item_schema_type;
960 use crate::xpath::XPathValue;
961
962 fn load_schema(xsd: &str) -> SchemaSet {
963 let mut schema_set = SchemaSet::xsd11();
964 load_and_process_schema(xsd.as_bytes(), "test.xsd", &mut schema_set, None)
965 .expect("failed to load schema");
966 schema_set
967 }
968
969 fn element_type(schema_set: &SchemaSet, local_name: &str) -> TypeKey {
970 let name_id = schema_set.name_table.add(local_name);
971 let elem_key = schema_set
972 .lookup_element(None, name_id)
973 .expect("element not found");
974 schema_set.arenas.elements[elem_key]
975 .resolved_type
976 .expect("element has no resolved type")
977 }
978
979 #[test]
980 fn test_atomic_schema_type_set() {
981 let schema = load_schema(
982 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
983 <xs:simpleType name="score">
984 <xs:restriction base="xs:integer">
985 <xs:minInclusive value="0"/>
986 <xs:maxInclusive value="100"/>
987 </xs:restriction>
988 </xs:simpleType>
989 <xs:element name="e" type="score"/>
990 </xs:schema>"#,
991 );
992 let tk = element_type(&schema, "e");
993 let result = validate_simple_type("50", tk, &schema).unwrap();
994 assert!(
995 result.typed_value.schema_type.is_some(),
996 "atomic value should have schema_type set"
997 );
998 }
999
1000 #[test]
1001 fn test_list_schema_type_set() {
1002 let schema = load_schema(
1003 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1004 <xs:simpleType name="intList">
1005 <xs:list itemType="xs:integer"/>
1006 </xs:simpleType>
1007 <xs:element name="e" type="intList"/>
1008 </xs:schema>"#,
1009 );
1010 let tk = element_type(&schema, "e");
1011 let result = validate_simple_type("1 2 3", tk, &schema).unwrap();
1012 assert!(
1013 result.typed_value.schema_type.is_some(),
1014 "list value should have schema_type set"
1015 );
1016 }
1017
1018 #[test]
1019 fn test_union_schema_type_set() {
1020 let schema = load_schema(
1021 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1022 <xs:simpleType name="intOrString">
1023 <xs:union memberTypes="xs:integer xs:string"/>
1024 </xs:simpleType>
1025 <xs:element name="e" type="intOrString"/>
1026 </xs:schema>"#,
1027 );
1028 let tk = element_type(&schema, "e");
1029 let result = validate_simple_type("42", tk, &schema).unwrap();
1030
1031 assert!(
1033 result.typed_value.schema_type.is_some(),
1034 "union value should have schema_type set"
1035 );
1036
1037 if let XmlValueKind::Union(ref inner) = result.typed_value.value {
1039 assert!(
1040 inner.schema_type.is_some(),
1041 "inner union value should have schema_type set from matched member"
1042 );
1043 } else {
1044 panic!("expected Union variant");
1045 }
1046 }
1047
1048 #[test]
1049 fn test_list_to_xpath_value() {
1050 let schema = load_schema(
1051 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1052 <xs:simpleType name="intList">
1053 <xs:list itemType="xs:integer"/>
1054 </xs:simpleType>
1055 <xs:element name="e" type="intList"/>
1056 </xs:schema>"#,
1057 );
1058 let tk = element_type(&schema, "e");
1059 let result = validate_simple_type("1 2 3", tk, &schema).unwrap();
1060
1061 let list_sk = tk.as_simple().expect("should be simple type");
1063 let item_sk = resolve_list_item_schema_type(list_sk, &schema);
1064
1065 let xpath_val: XPathValue<RoXmlNavigator<'static>> =
1066 result.typed_value.to_xpath_value(item_sk);
1067
1068 let items = xpath_val.into_vec();
1070 assert_eq!(items.len(), 3, "list should produce 3 XPath items");
1071
1072 for item in &items {
1074 let val = item.as_atomic().expect("should be atomic");
1075 assert!(
1076 val.schema_type.is_some(),
1077 "each list item should have schema_type"
1078 );
1079 }
1080 }
1081
1082 #[test]
1083 fn test_union_to_xpath_value() {
1084 let schema = load_schema(
1085 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1086 <xs:simpleType name="intOrString">
1087 <xs:union memberTypes="xs:integer xs:string"/>
1088 </xs:simpleType>
1089 <xs:element name="e" type="intOrString"/>
1090 </xs:schema>"#,
1091 );
1092 let tk = element_type(&schema, "e");
1093 let result = validate_simple_type("42", tk, &schema).unwrap();
1094
1095 let xpath_val: XPathValue<RoXmlNavigator<'static>> =
1096 result.typed_value.to_xpath_value(None);
1097
1098 let items = xpath_val.into_vec();
1100 assert_eq!(items.len(), 1, "union should unwrap to single item");
1101 assert!(items[0].is_atomic());
1102 }
1103
1104 #[test]
1105 fn test_atomic_to_xpath_value() {
1106 let schema = load_schema(
1107 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1108 <xs:element name="e" type="xs:integer"/>
1109 </xs:schema>"#,
1110 );
1111 let tk = element_type(&schema, "e");
1112 let result = validate_simple_type("42", tk, &schema).unwrap();
1113
1114 let xpath_val: XPathValue<RoXmlNavigator<'static>> =
1115 result.typed_value.to_xpath_value(None);
1116
1117 let items = xpath_val.into_vec();
1118 assert_eq!(items.len(), 1, "atomic should produce single item");
1119 assert!(items[0].is_atomic());
1120 assert_eq!(
1121 items[0].as_atomic().unwrap().type_code,
1122 XmlTypeCode::Integer
1123 );
1124 }
1125
1126 #[test]
1131 fn test_assertion_even_integer() {
1132 let schema = load_schema(
1133 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1134 <xs:simpleType name="evenInt">
1135 <xs:restriction base="xs:integer">
1136 <xs:assertion test="$value mod 2 = 0"/>
1137 </xs:restriction>
1138 </xs:simpleType>
1139 <xs:element name="e" type="evenInt"/>
1140 </xs:schema>"#,
1141 );
1142 let tk = element_type(&schema, "e");
1143 assert!(validate_simple_type("4", tk, &schema).is_ok());
1144 assert!(validate_simple_type("0", tk, &schema).is_ok());
1145 assert!(validate_simple_type("-2", tk, &schema).is_ok());
1146
1147 let err = validate_simple_type("3", tk, &schema).unwrap_err();
1148 assert_eq!(err.constraint, "cvc-assertion");
1149
1150 let err = validate_simple_type("7", tk, &schema).unwrap_err();
1151 assert_eq!(err.constraint, "cvc-assertion");
1152 }
1153
1154 #[test]
1155 fn test_assertion_positive_value() {
1156 let schema = load_schema(
1157 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1158 <xs:simpleType name="posInt">
1159 <xs:restriction base="xs:integer">
1160 <xs:assertion test="$value gt 0"/>
1161 </xs:restriction>
1162 </xs:simpleType>
1163 <xs:element name="e" type="posInt"/>
1164 </xs:schema>"#,
1165 );
1166 let tk = element_type(&schema, "e");
1167 assert!(validate_simple_type("1", tk, &schema).is_ok());
1168 assert!(validate_simple_type("100", tk, &schema).is_ok());
1169
1170 let err = validate_simple_type("0", tk, &schema).unwrap_err();
1171 assert_eq!(err.constraint, "cvc-assertion");
1172
1173 let err = validate_simple_type("-5", tk, &schema).unwrap_err();
1174 assert_eq!(err.constraint, "cvc-assertion");
1175 }
1176
1177 #[test]
1178 fn test_assertion_string_length() {
1179 let schema = load_schema(
1180 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1181 <xs:simpleType name="shortStr">
1182 <xs:restriction base="xs:string">
1183 <xs:assertion test="string-length($value) le 5"/>
1184 </xs:restriction>
1185 </xs:simpleType>
1186 <xs:element name="e" type="shortStr"/>
1187 </xs:schema>"#,
1188 );
1189 let tk = element_type(&schema, "e");
1190 assert!(validate_simple_type("hello", tk, &schema).is_ok());
1191 assert!(validate_simple_type("", tk, &schema).is_ok());
1192 assert!(validate_simple_type("abcde", tk, &schema).is_ok());
1193
1194 let err = validate_simple_type("toolong", tk, &schema).unwrap_err();
1195 assert_eq!(err.constraint, "cvc-assertion");
1196 }
1197
1198 #[test]
1199 fn test_assertion_inherited() {
1200 let schema = load_schema(
1202 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1203 <xs:simpleType name="positiveInt">
1204 <xs:restriction base="xs:integer">
1205 <xs:assertion test="$value gt 0"/>
1206 </xs:restriction>
1207 </xs:simpleType>
1208 <xs:simpleType name="smallPositiveInt">
1209 <xs:restriction base="positiveInt">
1210 <xs:maxInclusive value="10"/>
1211 </xs:restriction>
1212 </xs:simpleType>
1213 <xs:element name="e" type="smallPositiveInt"/>
1214 </xs:schema>"#,
1215 );
1216 let tk = element_type(&schema, "e");
1217 assert!(validate_simple_type("5", tk, &schema).is_ok());
1218 assert!(validate_simple_type("10", tk, &schema).is_ok());
1219
1220 assert!(validate_simple_type("11", tk, &schema).is_err());
1222 let err = validate_simple_type("0", tk, &schema).unwrap_err();
1224 assert_eq!(err.constraint, "cvc-assertion");
1225 }
1226
1227 #[test]
1228 fn test_assertion_compile_error() {
1229 let schema = load_schema(
1231 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1232 <xs:simpleType name="badAssert">
1233 <xs:restriction base="xs:integer">
1234 <xs:assertion test="$value @@@ invalid"/>
1235 </xs:restriction>
1236 </xs:simpleType>
1237 <xs:element name="e" type="badAssert"/>
1238 </xs:schema>"#,
1239 );
1240 let tk = element_type(&schema, "e");
1241 let err = validate_simple_type("42", tk, &schema).unwrap_err();
1242 assert_eq!(err.constraint, "cvc-assertion");
1243 assert!(err.message.contains("compile"));
1244 }
1245
1246 #[test]
1247 fn test_assertion_with_other_facets() {
1248 let schema = load_schema(
1250 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1251 <xs:simpleType name="evenScore">
1252 <xs:restriction base="xs:integer">
1253 <xs:minInclusive value="0"/>
1254 <xs:maxInclusive value="100"/>
1255 <xs:assertion test="$value mod 2 = 0"/>
1256 </xs:restriction>
1257 </xs:simpleType>
1258 <xs:element name="e" type="evenScore"/>
1259 </xs:schema>"#,
1260 );
1261 let tk = element_type(&schema, "e");
1262 assert!(validate_simple_type("50", tk, &schema).is_ok());
1263 assert!(validate_simple_type("0", tk, &schema).is_ok());
1264 assert!(validate_simple_type("100", tk, &schema).is_ok());
1265
1266 let err = validate_simple_type("51", tk, &schema).unwrap_err();
1268 assert_eq!(err.constraint, "cvc-assertion");
1269
1270 assert!(validate_simple_type("102", tk, &schema).is_err());
1272 assert!(validate_simple_type("-2", tk, &schema).is_err());
1273 }
1274
1275 #[test]
1276 fn test_assertion_multiple() {
1277 let schema = load_schema(
1279 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1280 <xs:simpleType name="constrained">
1281 <xs:restriction base="xs:integer">
1282 <xs:assertion test="$value gt 0"/>
1283 <xs:assertion test="$value mod 2 = 0"/>
1284 </xs:restriction>
1285 </xs:simpleType>
1286 <xs:element name="e" type="constrained"/>
1287 </xs:schema>"#,
1288 );
1289 let tk = element_type(&schema, "e");
1290 assert!(validate_simple_type("2", tk, &schema).is_ok());
1292 assert!(validate_simple_type("4", tk, &schema).is_ok());
1293
1294 let err = validate_simple_type("3", tk, &schema).unwrap_err();
1296 assert_eq!(err.constraint, "cvc-assertion");
1297
1298 let err = validate_simple_type("0", tk, &schema).unwrap_err();
1300 assert_eq!(err.constraint, "cvc-assertion");
1301 }
1302
1303 #[test]
1304 fn test_assertion_boolean_value() {
1305 let schema = load_schema(
1307 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1308 <xs:simpleType name="mustBeTrue">
1309 <xs:restriction base="xs:boolean">
1310 <xs:assertion test="$value"/>
1311 </xs:restriction>
1312 </xs:simpleType>
1313 <xs:element name="e" type="mustBeTrue"/>
1314 </xs:schema>"#,
1315 );
1316 let tk = element_type(&schema, "e");
1317 assert!(validate_simple_type("true", tk, &schema).is_ok());
1318 assert!(validate_simple_type("1", tk, &schema).is_ok());
1319
1320 let err = validate_simple_type("false", tk, &schema).unwrap_err();
1321 assert_eq!(err.constraint, "cvc-assertion");
1322 }
1323
1324 #[test]
1325 fn test_assertion_union_with_list_member_item_typing() {
1326 let schema = load_schema(
1330 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1331 <xs:simpleType name="intList">
1332 <xs:list itemType="xs:integer"/>
1333 </xs:simpleType>
1334 <xs:simpleType name="unionWithList">
1335 <xs:union memberTypes="intList">
1336 <xs:simpleType>
1337 <xs:restriction base="xs:string"/>
1338 </xs:simpleType>
1339 </xs:union>
1340 </xs:simpleType>
1341 <xs:simpleType name="checkedUnion">
1342 <xs:restriction base="unionWithList">
1343 <xs:assertion test="every $i in $value satisfies $i instance of xs:integer"/>
1344 </xs:restriction>
1345 </xs:simpleType>
1346 <xs:element name="e" type="checkedUnion"/>
1347 </xs:schema>"#,
1348 );
1349 let tk = element_type(&schema, "e");
1350 assert!(validate_simple_type("1 2 3", tk, &schema).is_ok());
1352 }
1353
1354 #[test]
1359 fn test_assertion_xpath_default_ns_schema_level_fallback() {
1360 let schema = load_schema(
1364 r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
1365 xpathDefaultNamespace="http://www.w3.org/2001/XMLSchema">
1366 <xs:simpleType name="checkedInt">
1367 <xs:restriction base="xs:integer">
1368 <xs:assertion test="$value instance of integer"/>
1369 </xs:restriction>
1370 </xs:simpleType>
1371 <xs:element name="e" type="checkedInt"/>
1372 </xs:schema>"#,
1373 );
1374 let tk = element_type(&schema, "e");
1375 assert!(validate_simple_type("42", tk, &schema).is_ok());
1377 }
1378
1379 #[test]
1380 fn test_assertion_xpath_default_ns_assertion_level_overrides_schema() {
1381 let schema = load_schema(
1386 r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
1387 xpathDefaultNamespace="http://www.w3.org/2001/XMLSchema">
1388 <xs:simpleType name="checkedInt">
1389 <xs:restriction base="xs:integer">
1390 <xs:assertion test="$value instance of integer"
1391 xpathDefaultNamespace="##local"/>
1392 </xs:restriction>
1393 </xs:simpleType>
1394 <xs:element name="e" type="checkedInt"/>
1395 </xs:schema>"###,
1396 );
1397 let tk = element_type(&schema, "e");
1398 let err = validate_simple_type("42", tk, &schema).unwrap_err();
1400 assert_eq!(err.constraint, "cvc-assertion");
1401 }
1402
1403 #[test]
1404 fn test_assertion_xpath_default_ns_target_namespace_token() {
1405 let schema = load_schema(
1410 r###"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
1411 targetNamespace="http://example.com/ns"
1412 xmlns:tns="http://example.com/ns"
1413 xpathDefaultNamespace="##targetNamespace">
1414 <xs:simpleType name="checkedInt">
1415 <xs:restriction base="xs:integer">
1416 <xs:assertion test="$value instance of integer"/>
1417 </xs:restriction>
1418 </xs:simpleType>
1419 <xs:element name="e" type="tns:checkedInt"/>
1420 </xs:schema>"###,
1421 );
1422 let ns_id = schema.name_table.add("http://example.com/ns");
1423 let name_id = schema.name_table.add("e");
1424 let elem_key = schema
1425 .lookup_element(Some(ns_id), name_id)
1426 .expect("element not found");
1427 let tk = schema.arenas.elements[elem_key]
1428 .resolved_type
1429 .expect("element has no resolved type");
1430 let err = validate_simple_type("42", tk, &schema).unwrap_err();
1432 assert_eq!(err.constraint, "cvc-assertion");
1433 }
1434}