1use crate::ids::NameId;
13use crate::namespace::table::NameTable;
14use crate::parser::frames::{IdentityKind, QNameRef};
15use crate::parser::location::SourceLocation;
16use crate::schema::model::XsdVersion;
17use crate::types::value::XmlValue;
18use crate::types::PrimitiveTypeCode;
19
20use super::active_axis::ActiveAxis;
21use super::asttree::{Asttree, IdentityXPathError};
22use super::errors::{error, error_with_path, ValidationError};
23
24use crate::arenas::IdentityConstraintData;
25use crate::ids::IdentityConstraintKey;
26
27fn extract_single_atomic(value: &XmlValue) -> Option<&crate::types::value::XmlAtomicValue> {
33 match &value.value {
34 crate::types::value::XmlValueKind::Atomic(a) => Some(a),
35 crate::types::value::XmlValueKind::List { items, .. } if items.len() == 1 => {
36 Some(&items[0])
37 }
38 crate::types::value::XmlValueKind::Union(inner) => extract_single_atomic(inner),
39 _ => None,
40 }
41}
42
43fn xml_value_ic_eq(a: &XmlValue, b: &XmlValue) -> bool {
56 if a.type_code == b.type_code && a.value == b.value {
57 return true;
58 }
59 if a.type_code == b.type_code && xml_value_datetime_eq(a, b) {
60 return true;
61 }
62 match (extract_single_atomic(a), extract_single_atomic(b)) {
64 (Some(va), Some(vb)) => va == vb || xml_atomic_datetime_eq(va, vb),
65 _ => false,
66 }
67}
68
69fn xml_atomic_datetime_eq(
77 a: &crate::types::value::XmlAtomicValue,
78 b: &crate::types::value::XmlAtomicValue,
79) -> bool {
80 use crate::types::value::XmlAtomicValue as V;
81 match (a, b) {
82 (V::DateTime(x), V::DateTime(y)) => {
83 x.timezone.is_some() == y.timezone.is_some()
84 && x.partial_cmp(y) == Some(std::cmp::Ordering::Equal)
85 }
86 (V::Date(x), V::Date(y)) => {
87 x.timezone.is_some() == y.timezone.is_some()
88 && x.partial_cmp(y) == Some(std::cmp::Ordering::Equal)
89 }
90 (V::Time(x), V::Time(y)) => {
91 x.timezone.is_some() == y.timezone.is_some()
92 && x.partial_cmp(y) == Some(std::cmp::Ordering::Equal)
93 }
94 (V::Duration(x), V::Duration(y)) => x.partial_cmp(y) == Some(std::cmp::Ordering::Equal),
95 (V::YearMonthDuration(x), V::YearMonthDuration(y)) => {
96 x.partial_cmp(y) == Some(std::cmp::Ordering::Equal)
97 }
98 (V::DayTimeDuration(x), V::DayTimeDuration(y)) => {
99 x.partial_cmp(y) == Some(std::cmp::Ordering::Equal)
100 }
101 _ => false,
102 }
103}
104
105fn xml_value_datetime_eq(a: &XmlValue, b: &XmlValue) -> bool {
106 use crate::types::value::XmlValueKind;
107 match (&a.value, &b.value) {
108 (XmlValueKind::Atomic(va), XmlValueKind::Atomic(vb)) => xml_atomic_datetime_eq(va, vb),
109 _ => false,
110 }
111}
112
113pub(crate) struct CompiledIdentityConstraint {
122 pub key: IdentityConstraintKey,
124 pub name: NameId,
126 pub kind: IdentityKind,
128 pub selector: Asttree,
130 pub fields: Vec<Asttree>,
132 pub refer: Option<QNameRef>,
134 pub refer_key: Option<IdentityConstraintKey>,
137 pub field_count: usize,
139 pub target_namespace: Option<NameId>,
141}
142
143impl CompiledIdentityConstraint {
144 pub fn compile(
149 data: &IdentityConstraintData,
150 key: IdentityConstraintKey,
151 name_table: &NameTable,
152 schema_xpath_default_ns: Option<NameId>,
153 target_namespace: Option<NameId>,
154 xsd_version: XsdVersion,
155 ) -> Result<Self, IdentityXPathError> {
156 let selector = Asttree::compile_selector(
158 &data.selector.xpath,
159 &data.selector.ns_snapshot,
160 name_table,
161 data.selector.xpath_default_namespace.as_deref(),
162 schema_xpath_default_ns,
163 target_namespace,
164 xsd_version,
165 )?;
166
167 let selector_level_ns = match &data.selector.xpath_default_namespace {
171 Some(val) => match val.as_str() {
172 "##targetNamespace" => target_namespace,
173 "##local" => None,
174 uri => Some(name_table.add(uri)),
175 },
176 None => schema_xpath_default_ns,
177 };
178
179 let fields: Vec<Asttree> = data
181 .fields
182 .iter()
183 .map(|f| {
184 Asttree::compile_field(
185 &f.xpath,
186 &f.ns_snapshot,
187 name_table,
188 f.xpath_default_namespace.as_deref(),
189 selector_level_ns,
190 target_namespace,
191 xsd_version,
192 )
193 })
194 .collect::<Result<Vec<_>, _>>()?;
195
196 let field_count = fields.len();
197
198 Ok(CompiledIdentityConstraint {
199 key,
200 name: data.name,
201 kind: data.kind,
202 selector,
203 fields,
204 refer: data.refer.clone(),
205 refer_key: None,
206 field_count,
207 target_namespace,
208 })
209 }
210}
211
212#[derive(Debug, Clone)]
228pub struct KeyFieldValue {
229 pub string_value: String,
230 pub typed_value: Option<XmlValue>,
231}
232
233impl PartialEq for KeyFieldValue {
234 fn eq(&self, other: &Self) -> bool {
235 match (&self.typed_value, &other.typed_value) {
236 (Some(a), Some(b)) => {
237 let prim_a = a.primitive_type();
238 let prim_b = b.primitive_type();
239 match (prim_a, prim_b) {
240 (Some(pa), Some(pb)) if pa == pb => {
241 if pa == PrimitiveTypeCode::Decimal {
246 match (a.as_decimal(), b.as_decimal()) {
247 (Some(da), Some(db)) => da == db,
248 _ => xml_value_ic_eq(a, b),
249 }
250 } else {
251 xml_value_ic_eq(a, b)
252 }
253 }
254 (Some(_), Some(_)) => false, _ => self.string_value == other.string_value,
256 }
257 }
258 _ => self.string_value == other.string_value,
259 }
260 }
261}
262
263impl Eq for KeyFieldValue {}
264
265#[derive(Debug, Clone)]
274pub struct KeySequence {
275 pub fields: Vec<Option<KeyFieldValue>>,
276}
277
278impl KeySequence {
279 pub fn is_complete(&self) -> bool {
281 self.fields.iter().all(|f| f.is_some())
282 }
283}
284
285impl PartialEq for KeySequence {
286 fn eq(&self, other: &Self) -> bool {
287 if self.fields.len() != other.fields.len() {
288 return false;
289 }
290 for (a, b) in self.fields.iter().zip(other.fields.iter()) {
291 match (a, b) {
292 (Some(va), Some(vb)) => {
293 if va != vb {
294 return false;
295 }
296 }
297 (None, None) => {
298 }
300 _ => return false,
301 }
302 }
303 true
304 }
305}
306
307impl Eq for KeySequence {}
308
309pub struct KeyTable {
315 pub ic_key: IdentityConstraintKey,
317 pub constraint_name: NameId,
318 pub kind: IdentityKind,
319 pub sequences: Vec<KeySequence>,
320}
321
322impl KeyTable {
323 pub fn new(ic_key: IdentityConstraintKey, constraint_name: NameId, kind: IdentityKind) -> Self {
325 KeyTable {
326 ic_key,
327 constraint_name,
328 kind,
329 sequences: Vec::new(),
330 }
331 }
332
333 pub fn add_sequence(
341 &mut self,
342 seq: KeySequence,
343 name_table: &NameTable,
344 element_path: &str,
345 location: Option<SourceLocation>,
346 ) -> Vec<ValidationError> {
347 let mut errors = Vec::new();
348 let name = name_table.resolve(self.constraint_name);
349
350 match self.kind {
351 IdentityKind::Key => {
352 if !seq.is_complete() {
353 errors.push(error_with_path(
354 "cvc-identity-constraint.4.2.1",
355 format!("Key constraint '{}': not all fields have values", name),
356 location.clone(),
357 element_path,
358 ));
359 }
360 if self.find_duplicate(&seq).is_some() {
363 errors.push(error_with_path(
364 "cvc-identity-constraint.4.2.2",
365 format!("Key constraint '{}': duplicate key value detected", name),
366 location,
367 element_path,
368 ));
369 }
370 }
371 IdentityKind::Unique => {
372 if seq.is_complete() && self.find_duplicate(&seq).is_some() {
374 errors.push(error_with_path(
375 "cvc-identity-constraint.4.2.2",
376 format!("Unique constraint '{}': duplicate key value detected", name),
377 location,
378 element_path,
379 ));
380 }
381 }
382 IdentityKind::Keyref => {
383 }
385 }
386
387 self.sequences.push(seq);
388 errors
389 }
390
391 pub fn check_keyref_against(
395 &self,
396 target: &KeyTable,
397 name_table: &NameTable,
398 ) -> Vec<ValidationError> {
399 let mut errors = Vec::new();
400 let name = name_table.resolve(self.constraint_name);
401
402 for seq in &self.sequences {
403 if !seq.is_complete() {
404 continue;
406 }
407 let found = target.sequences.iter().any(|ts| ts == seq);
408 if !found {
409 errors.push(error(
410 "cvc-identity-constraint.4.3",
411 format!(
412 "Keyref constraint '{}': no matching key value found in referenced constraint '{}'",
413 name,
414 name_table.resolve(target.constraint_name)
415 ),
416 None,
417 ));
418 }
419 }
420
421 errors
422 }
423
424 fn find_duplicate(&self, seq: &KeySequence) -> Option<usize> {
426 self.sequences.iter().position(|existing| existing == seq)
427 }
428}
429
430struct FieldCollectionFrame {
440 fields: Vec<ActiveAxis>,
442 current_key_sequence: Vec<Option<KeyFieldValue>>,
444 field_match_count: Vec<u8>,
447}
448
449pub(crate) struct ConstraintStruct {
464 pub ic_key: IdentityConstraintKey,
466 pub selector: ActiveAxis,
468 field_asttrees: Vec<Asttree>,
470 field_count: usize,
472 collection_stack: Vec<FieldCollectionFrame>,
475 pub key_table: KeyTable,
477}
478
479impl ConstraintStruct {
480 pub fn new(compiled: &CompiledIdentityConstraint) -> Self {
485 let selector = ActiveAxis::new(compiled.selector.clone());
486
487 ConstraintStruct {
488 ic_key: compiled.key,
489 selector,
490 field_asttrees: compiled.fields.clone(),
491 field_count: compiled.field_count,
492 collection_stack: Vec::new(),
493 key_table: KeyTable::new(compiled.key, compiled.name, compiled.kind),
494 }
495 }
496
497 #[cfg(test)]
499 pub fn collecting_fields(&self) -> bool {
500 !self.collection_stack.is_empty()
501 }
502
503 pub fn activate(&mut self) -> bool {
507 let is_scope_match = self.selector.activate();
508 if is_scope_match {
509 self.push_field_collection();
511 }
512 is_scope_match
513 }
514
515 pub fn start_element(&mut self, local_name: NameId, ns: NameId) {
521 self.selector.move_to_start_element(local_name, ns);
522
523 for frame in &mut self.collection_stack {
526 for field in &mut frame.fields {
527 field.move_to_start_element(local_name, ns);
528 }
529 }
530
531 if self.selector.entered_match() {
532 self.push_field_collection();
535 }
536 }
537
538 pub fn start_element_skipped(&mut self, _local_name: NameId, _ns: NameId) {
545 self.selector.move_to_skipped_element();
546 for frame in &mut self.collection_stack {
547 for field in &mut frame.fields {
548 field.move_to_skipped_element();
549 }
550 }
551 }
552
553 pub fn matching_fields(&self, local_name: NameId, ns: NameId) -> Vec<usize> {
558 let frame = match self.collection_stack.last() {
559 Some(f) => f,
560 None => return Vec::new(),
561 };
562 let mut indices = Vec::new();
563 for (i, field) in frame.fields.iter().enumerate() {
564 if field.matches_attribute(local_name, ns) {
565 indices.push(i);
566 }
567 }
568 indices
569 }
570
571 pub fn set_field_value(
577 &mut self,
578 field_idx: usize,
579 string_value: String,
580 typed_value: Option<XmlValue>,
581 ) -> bool {
582 if let Some(frame) = self.collection_stack.last_mut() {
583 if field_idx < frame.current_key_sequence.len() {
584 let already_matched = frame.field_match_count[field_idx] > 0;
585 frame.field_match_count[field_idx] =
586 frame.field_match_count[field_idx].saturating_add(1);
587 frame.current_key_sequence[field_idx] = Some(KeyFieldValue {
588 string_value,
589 typed_value,
590 });
591 return already_matched;
592 }
593 }
594 false
595 }
596
597 pub fn increment_field_match_count(&mut self, field_idx: usize) -> bool {
605 if let Some(frame) = self.collection_stack.last_mut() {
606 if field_idx < frame.field_match_count.len() {
607 let already_matched = frame.field_match_count[field_idx] > 0;
608 frame.field_match_count[field_idx] =
609 frame.field_match_count[field_idx].saturating_add(1);
610 return already_matched;
611 }
612 }
613 false
614 }
615
616 #[allow(clippy::too_many_arguments)]
633 pub fn end_element_with_text(
634 &mut self,
635 text_content: &str,
636 typed_value: Option<&XmlValue>,
637 is_nil: bool,
638 is_complex_content: bool,
639 name_table: &NameTable,
640 element_path: &str,
641 location: Option<SourceLocation>,
642 ) -> Vec<ValidationError> {
643 let mut errors = Vec::new();
644
645 for frame in &mut self.collection_stack {
647 for (field_idx, field) in frame.fields.iter_mut().enumerate() {
648 field.end_element();
649 if field.exited_match() {
650 if frame.field_match_count[field_idx] > 0 {
651 let name = name_table.resolve(self.key_table.constraint_name);
654 errors.push(error_with_path(
655 "cvc-identity-constraint.4.2.1",
656 format!(
657 "Identity constraint '{}': field {} matches more than one node",
658 name,
659 field_idx + 1
660 ),
661 location.clone(),
662 element_path,
663 ));
664 } else if frame.current_key_sequence[field_idx].is_none() {
665 frame.field_match_count[field_idx] =
667 frame.field_match_count[field_idx].saturating_add(1);
668 if is_complex_content {
669 let constraint_name =
675 name_table.resolve(self.key_table.constraint_name);
676 errors.push(error_with_path(
677 "cvc-identity-constraint.4",
678 format!(
679 "Identity constraint '{}': field matched an element with \
680 complex content type (element-only or mixed); \
681 typed field values must be simple-typed",
682 constraint_name
683 ),
684 location.clone(),
685 element_path,
686 ));
687 } else if typed_value.is_some() {
688 frame.current_key_sequence[field_idx] = Some(KeyFieldValue {
690 string_value: text_content.to_string(),
691 typed_value: typed_value.cloned(),
692 });
693 } else if is_nil {
694 frame.current_key_sequence[field_idx] = Some(KeyFieldValue {
699 string_value: String::new(),
700 typed_value: None,
701 });
702 }
703 }
706 }
707 }
708 }
709
710 self.selector.end_element();
712
713 if self.selector.exited_match() {
715 if let Some(frame) = self.collection_stack.pop() {
716 let seq = KeySequence {
717 fields: frame.current_key_sequence,
718 };
719 errors.extend(
720 self.key_table
721 .add_sequence(seq, name_table, element_path, location),
722 );
723 }
724 }
725
726 errors
727 }
728
729 pub fn is_active(&self) -> bool {
731 self.selector.is_active()
732 }
733
734 fn push_field_collection(&mut self) {
737 let fields: Vec<ActiveAxis> = self
738 .field_asttrees
739 .iter()
740 .map(|ast| {
741 let mut axis = ActiveAxis::new(ast.clone());
742 axis.activate();
743 axis
744 })
745 .collect();
746 self.collection_stack.push(FieldCollectionFrame {
747 fields,
748 current_key_sequence: vec![None; self.field_count],
749 field_match_count: vec![0; self.field_count],
750 });
751 }
752}
753
754#[cfg(test)]
759mod tests {
760 use super::*;
761 use crate::namespace::context::NamespaceContextSnapshot;
762 use crate::namespace::table::NameTable;
763 use crate::schema::model::XsdVersion;
764 use crate::types::value::{XmlAtomicValue, XmlValueKind};
765 use crate::types::XmlTypeCode;
766
767 fn make_string_field(s: &str) -> KeyFieldValue {
770 KeyFieldValue {
771 string_value: s.to_string(),
772 typed_value: None,
773 }
774 }
775
776 fn make_typed_field(s: &str, type_code: XmlTypeCode, value: XmlValueKind) -> KeyFieldValue {
777 KeyFieldValue {
778 string_value: s.to_string(),
779 typed_value: Some(XmlValue::new(type_code, value)),
780 }
781 }
782
783 fn make_name_table() -> NameTable {
784 NameTable::new()
785 }
786
787 #[test]
792 fn key_field_value_untyped_same_string_equal() {
793 let a = make_string_field("hello");
794 let b = make_string_field("hello");
795 assert_eq!(a, b);
796 }
797
798 #[test]
799 fn key_field_value_untyped_different_string_not_equal() {
800 let a = make_string_field("hello");
801 let b = make_string_field("world");
802 assert_ne!(a, b);
803 }
804
805 #[test]
806 fn key_field_value_typed_same_primitive_same_value_equal() {
807 let a = make_typed_field(
808 "42",
809 XmlTypeCode::Integer,
810 XmlValueKind::Atomic(XmlAtomicValue::Integer(42.into())),
811 );
812 let b = make_typed_field(
813 "42",
814 XmlTypeCode::Integer,
815 XmlValueKind::Atomic(XmlAtomicValue::Integer(42.into())),
816 );
817 assert_eq!(a, b);
818 }
819
820 #[test]
821 fn key_field_value_typed_same_primitive_different_value_not_equal() {
822 let a = make_typed_field(
823 "42",
824 XmlTypeCode::Integer,
825 XmlValueKind::Atomic(XmlAtomicValue::Integer(42.into())),
826 );
827 let b = make_typed_field(
828 "99",
829 XmlTypeCode::Integer,
830 XmlValueKind::Atomic(XmlAtomicValue::Integer(99.into())),
831 );
832 assert_ne!(a, b);
833 }
834
835 #[test]
836 fn key_field_value_typed_different_primitive_not_equal() {
837 let a = make_typed_field(
839 "5",
840 XmlTypeCode::Integer,
841 XmlValueKind::Atomic(XmlAtomicValue::Integer(5.into())),
842 );
843 let b = make_typed_field(
844 "5",
845 XmlTypeCode::String,
846 XmlValueKind::Atomic(XmlAtomicValue::String("5".to_string())),
847 );
848 assert_ne!(a, b);
849 }
850
851 #[test]
852 fn key_field_value_one_typed_one_untyped_fallback_string() {
853 let a = make_typed_field(
855 "hello",
856 XmlTypeCode::String,
857 XmlValueKind::Atomic(XmlAtomicValue::String("hello".to_string())),
858 );
859 let b = make_string_field("hello");
860 assert_eq!(a, b);
861
862 let c = make_typed_field(
863 "42",
864 XmlTypeCode::Integer,
865 XmlValueKind::Atomic(XmlAtomicValue::Integer(42.into())),
866 );
867 let d = make_string_field("99");
868 assert_ne!(c, d);
869 }
870
871 #[test]
876 fn key_sequence_is_complete_all_present() {
877 let seq = KeySequence {
878 fields: vec![Some(make_string_field("a")), Some(make_string_field("b"))],
879 };
880 assert!(seq.is_complete());
881 }
882
883 #[test]
884 fn key_sequence_is_complete_missing_field() {
885 let seq = KeySequence {
886 fields: vec![Some(make_string_field("a")), None],
887 };
888 assert!(!seq.is_complete());
889 }
890
891 #[test]
892 fn key_sequence_equal() {
893 let a = KeySequence {
894 fields: vec![Some(make_string_field("x")), Some(make_string_field("y"))],
895 };
896 let b = KeySequence {
897 fields: vec![Some(make_string_field("x")), Some(make_string_field("y"))],
898 };
899 assert_eq!(a, b);
900 }
901
902 #[test]
903 fn key_sequence_not_equal() {
904 let a = KeySequence {
905 fields: vec![Some(make_string_field("x")), Some(make_string_field("y"))],
906 };
907 let b = KeySequence {
908 fields: vec![Some(make_string_field("x")), Some(make_string_field("z"))],
909 };
910 assert_ne!(a, b);
911 }
912
913 #[test]
914 fn key_sequence_both_none_equal() {
915 let a = KeySequence {
916 fields: vec![Some(make_string_field("x")), None],
917 };
918 let b = KeySequence {
919 fields: vec![Some(make_string_field("x")), None],
920 };
921 assert_eq!(a, b);
922 }
923
924 #[test]
929 fn key_table_key_duplicate_error() {
930 let nt = make_name_table();
931 let name = nt.add("pk");
932 let mut table = KeyTable::new(IdentityConstraintKey::default(), name, IdentityKind::Key);
933
934 let seq1 = KeySequence {
935 fields: vec![Some(make_string_field("1"))],
936 };
937 let errs = table.add_sequence(seq1, &nt, "/root/item[1]", None);
938 assert!(errs.is_empty());
939
940 let seq2 = KeySequence {
941 fields: vec![Some(make_string_field("1"))],
942 };
943 let errs = table.add_sequence(seq2, &nt, "/root/item[2]", None);
944 assert_eq!(errs.len(), 1);
945 assert_eq!(errs[0].constraint, "cvc-identity-constraint.4.2.2");
946 }
947
948 #[test]
949 fn key_table_key_incomplete_error() {
950 let nt = make_name_table();
951 let name = nt.add("pk");
952 let mut table = KeyTable::new(IdentityConstraintKey::default(), name, IdentityKind::Key);
953
954 let seq = KeySequence {
955 fields: vec![Some(make_string_field("a")), None],
956 };
957 let errs = table.add_sequence(seq, &nt, "/root/item[1]", None);
958 assert!(errs
959 .iter()
960 .any(|e| e.constraint == "cvc-identity-constraint.4.2.1"));
961 }
962
963 #[test]
964 fn key_table_unique_duplicate_error() {
965 let nt = make_name_table();
966 let name = nt.add("uq");
967 let mut table = KeyTable::new(IdentityConstraintKey::default(), name, IdentityKind::Unique);
968
969 let seq1 = KeySequence {
970 fields: vec![Some(make_string_field("val"))],
971 };
972 let errs = table.add_sequence(seq1, &nt, "/root/item[1]", None);
973 assert!(errs.is_empty());
974
975 let seq2 = KeySequence {
976 fields: vec![Some(make_string_field("val"))],
977 };
978 let errs = table.add_sequence(seq2, &nt, "/root/item[2]", None);
979 assert_eq!(errs.len(), 1);
980 assert_eq!(errs[0].constraint, "cvc-identity-constraint.4.2.2");
981 }
982
983 #[test]
984 fn key_table_unique_incomplete_no_error() {
985 let nt = make_name_table();
986 let name = nt.add("uq");
987 let mut table = KeyTable::new(IdentityConstraintKey::default(), name, IdentityKind::Unique);
988
989 let seq = KeySequence { fields: vec![None] };
990 let errs = table.add_sequence(seq, &nt, "/root/item[1]", None);
991 assert!(errs.is_empty());
992 }
993
994 #[test]
995 fn key_table_keyref_no_error() {
996 let nt = make_name_table();
997 let name = nt.add("fk");
998 let mut table = KeyTable::new(IdentityConstraintKey::default(), name, IdentityKind::Keyref);
999
1000 let seq = KeySequence {
1001 fields: vec![Some(make_string_field("anything"))],
1002 };
1003 let errs = table.add_sequence(seq, &nt, "/root/item[1]", None);
1004 assert!(errs.is_empty());
1005 }
1006
1007 #[test]
1008 fn check_keyref_against_matching() {
1009 let nt = make_name_table();
1010 let pk_name = nt.add("pk");
1011 let fk_name = nt.add("fk");
1012
1013 let mut key_table =
1014 KeyTable::new(IdentityConstraintKey::default(), pk_name, IdentityKind::Key);
1015 key_table.sequences.push(KeySequence {
1016 fields: vec![Some(make_string_field("1"))],
1017 });
1018
1019 let mut keyref_table = KeyTable::new(
1020 IdentityConstraintKey::default(),
1021 fk_name,
1022 IdentityKind::Keyref,
1023 );
1024 keyref_table.sequences.push(KeySequence {
1025 fields: vec![Some(make_string_field("1"))],
1026 });
1027
1028 let errs = keyref_table.check_keyref_against(&key_table, &nt);
1029 assert!(errs.is_empty());
1030 }
1031
1032 #[test]
1033 fn check_keyref_against_missing() {
1034 let nt = make_name_table();
1035 let pk_name = nt.add("pk");
1036 let fk_name = nt.add("fk");
1037
1038 let key_table = KeyTable::new(IdentityConstraintKey::default(), pk_name, IdentityKind::Key);
1039
1040 let mut keyref_table = KeyTable::new(
1041 IdentityConstraintKey::default(),
1042 fk_name,
1043 IdentityKind::Keyref,
1044 );
1045 keyref_table.sequences.push(KeySequence {
1046 fields: vec![Some(make_string_field("missing"))],
1047 });
1048
1049 let errs = keyref_table.check_keyref_against(&key_table, &nt);
1050 assert_eq!(errs.len(), 1);
1051 assert_eq!(errs[0].constraint, "cvc-identity-constraint.4.3");
1052 }
1053
1054 fn make_selector_result(xpath: &str) -> crate::parser::frames::SelectorResult {
1059 crate::parser::frames::SelectorResult {
1060 xpath: xpath.to_string(),
1061 xpath_default_namespace: None,
1062 ns_snapshot: NamespaceContextSnapshot::default(),
1063 id: None,
1064 annotation: None,
1065 source: None,
1066 }
1067 }
1068
1069 fn make_field_result(xpath: &str) -> crate::parser::frames::FieldResult {
1070 crate::parser::frames::FieldResult {
1071 xpath: xpath.to_string(),
1072 xpath_default_namespace: None,
1073 ns_snapshot: NamespaceContextSnapshot::default(),
1074 id: None,
1075 annotation: None,
1076 source: None,
1077 }
1078 }
1079
1080 fn make_identity_data(
1081 kind: IdentityKind,
1082 name: NameId,
1083 selector_xpath: &str,
1084 field_xpaths: &[&str],
1085 ) -> IdentityConstraintData {
1086 IdentityConstraintData {
1087 kind,
1088 name,
1089 ref_name: None,
1090 refer: None,
1091 selector: make_selector_result(selector_xpath),
1092 fields: field_xpaths.iter().map(|x| make_field_result(x)).collect(),
1093 id: None,
1094 annotation: None,
1095 source: None,
1096 }
1097 }
1098
1099 #[test]
1100 fn compile_simple_constraint() {
1101 let nt = make_name_table();
1102 let name = nt.add("testKey");
1103 let key = IdentityConstraintKey::default();
1104
1105 let data = make_identity_data(IdentityKind::Key, name, "./item", &["@id"]);
1106
1107 let compiled =
1108 CompiledIdentityConstraint::compile(&data, key, &nt, None, None, XsdVersion::V1_0);
1109 assert!(compiled.is_ok());
1110 let compiled = compiled.unwrap();
1111 assert_eq!(compiled.field_count, 1);
1112 assert_eq!(compiled.kind, IdentityKind::Key);
1113 assert_eq!(compiled.name, name);
1114 }
1115
1116 #[test]
1117 fn compile_invalid_xpath_propagates_error() {
1118 let nt = make_name_table();
1119 let name = nt.add("badKey");
1120 let key = IdentityConstraintKey::default();
1121
1122 let data = make_identity_data(IdentityKind::Key, name, "///invalid", &["@id"]);
1123
1124 let result =
1125 CompiledIdentityConstraint::compile(&data, key, &nt, None, None, XsdVersion::V1_0);
1126 assert!(result.is_err());
1127 }
1128
1129 #[test]
1134 fn constraint_struct_lifecycle() {
1135 let nt = make_name_table();
1136 let name = nt.add("testKey");
1137 let key = IdentityConstraintKey::default();
1138
1139 let data = make_identity_data(IdentityKind::Key, name, "./item", &["@id"]);
1140 let compiled =
1141 CompiledIdentityConstraint::compile(&data, key, &nt, None, None, XsdVersion::V1_0)
1142 .unwrap();
1143
1144 let mut cs = ConstraintStruct::new(&compiled);
1145
1146 let scope_match = cs.activate();
1148 assert!(!scope_match); assert!(cs.is_active());
1150
1151 let item_name = nt.add("item");
1153 let empty_ns = NameId(0);
1154 cs.start_element(item_name, empty_ns);
1155
1156 assert!(cs.collecting_fields());
1158
1159 let id_name = nt.add("id");
1161 let matches = cs.matching_fields(id_name, empty_ns);
1162 assert_eq!(matches, vec![0]);
1163
1164 cs.set_field_value(0, "val1".to_string(), None);
1166
1167 let errors = cs.end_element_with_text("", None, false, false, &nt, "/root/item[1]", None);
1169 assert!(errors.is_empty());
1170
1171 assert_eq!(cs.key_table.sequences.len(), 1);
1173 assert!(cs.key_table.sequences[0].is_complete());
1174 assert_eq!(
1175 cs.key_table.sequences[0].fields[0]
1176 .as_ref()
1177 .unwrap()
1178 .string_value,
1179 "val1"
1180 );
1181 }
1182
1183 #[test]
1186 fn constraint_struct_nested_selector() {
1187 let nt = make_name_table();
1188 let name = nt.add("uq");
1189 let key = IdentityConstraintKey::default();
1190
1191 let data = make_identity_data(IdentityKind::Unique, name, ".//item", &["@id"]);
1192 let compiled =
1193 CompiledIdentityConstraint::compile(&data, key, &nt, None, None, XsdVersion::V1_0)
1194 .unwrap();
1195
1196 let mut cs = ConstraintStruct::new(&compiled);
1197 cs.activate();
1198
1199 let item = nt.add("item");
1200 let id = nt.add("id");
1201 let ns = NameId(0);
1202
1203 cs.start_element(item, ns);
1205 assert!(cs.collecting_fields());
1206 let m = cs.matching_fields(id, ns);
1207 assert_eq!(m, vec![0]);
1208 cs.set_field_value(0, "outer".to_string(), None);
1209
1210 cs.start_element(item, ns);
1212 let m = cs.matching_fields(id, ns);
1214 assert_eq!(m, vec![0]); cs.set_field_value(0, "inner".to_string(), None);
1216
1217 let errors = cs.end_element_with_text("", None, false, false, &nt, "/root/item/item", None);
1219 assert!(errors.is_empty());
1220 assert_eq!(cs.key_table.sequences.len(), 1);
1221 assert_eq!(
1222 cs.key_table.sequences[0].fields[0]
1223 .as_ref()
1224 .unwrap()
1225 .string_value,
1226 "inner"
1227 );
1228
1229 assert!(cs.collecting_fields());
1231
1232 let errors = cs.end_element_with_text("", None, false, false, &nt, "/root/item", None);
1234 assert!(errors.is_empty());
1235 assert_eq!(cs.key_table.sequences.len(), 2);
1236 assert_eq!(
1237 cs.key_table.sequences[1].fields[0]
1238 .as_ref()
1239 .unwrap()
1240 .string_value,
1241 "outer"
1242 );
1243
1244 assert!(!cs.collecting_fields());
1246 }
1247
1248 #[test]
1250 fn constraint_struct_multi_field_same_attr() {
1251 let nt = make_name_table();
1252 let name = nt.add("uq2");
1253 let key = IdentityConstraintKey::default();
1254
1255 let data = make_identity_data(IdentityKind::Unique, name, "./item", &["@id", "@id"]);
1257 let compiled =
1258 CompiledIdentityConstraint::compile(&data, key, &nt, None, None, XsdVersion::V1_0)
1259 .unwrap();
1260
1261 let mut cs = ConstraintStruct::new(&compiled);
1262 cs.activate();
1263
1264 let item = nt.add("item");
1265 let id = nt.add("id");
1266 let ns = NameId(0);
1267
1268 cs.start_element(item, ns);
1269 let matches = cs.matching_fields(id, ns);
1270 assert_eq!(matches, vec![0, 1]); cs.set_field_value(0, "v".to_string(), None);
1273 cs.set_field_value(1, "v".to_string(), None);
1274
1275 let errors = cs.end_element_with_text("", None, false, false, &nt, "/root/item", None);
1276 assert!(errors.is_empty());
1277 assert!(cs.key_table.sequences[0].is_complete());
1278 }
1279}