1use crate::ion_extension::ElementExtensions;
2use crate::ion_path::{IonPath, IonPathElement};
3use crate::isl::isl_constraint::{
4 IslAnnotationsConstraint, IslConstraintValue, IslRegexConstraint,
5};
6use crate::isl::isl_type_reference::{
7 IslTypeRef, IslVariablyOccurringTypeRef, NullabilityModifier,
8};
9use crate::isl::ranges::{I64Range, Limit, TimestampPrecisionRange, U64Range, UsizeRange};
10use crate::isl::util::{
11 Annotation, Ieee754InterchangeFormat, TimestampOffset, TimestampPrecision, ValidValue,
12};
13use crate::isl::IslVersion;
14use crate::isl_require;
15use crate::ordered_elements_nfa::OrderedElementsNfa;
16use crate::result::{
17 invalid_schema_error, invalid_schema_error_raw, IonSchemaResult, ValidationResult,
18};
19use crate::system::{PendingTypes, TypeId, TypeStore};
20use crate::type_reference::{TypeReference, VariablyOccurringTypeRef};
21use crate::types::TypeValidator;
22use crate::violation::{Violation, ViolationCode};
23use crate::IonSchemaElement;
24use ion_rs::Element;
25use ion_rs::IonData;
26use ion_rs::IonType;
27use ion_rs::Value;
28use num_traits::ToPrimitive;
29use regex::{Regex, RegexBuilder};
30use std::collections::{HashMap, HashSet};
31use std::fmt::{Display, Formatter};
32use std::iter::Peekable;
33use std::ops::Neg;
34use std::str::Chars;
35
36pub trait ConstraintValidator {
38 fn validate(
43 &self,
44 value: &IonSchemaElement,
45 type_store: &TypeStore,
46 ion_path: &mut IonPath,
47 ) -> ValidationResult;
48}
49
50#[derive(Debug, Clone, PartialEq)]
52pub enum Constraint {
54 AllOf(AllOfConstraint),
55 Annotations(AnnotationsConstraint),
56 Annotations2_0(AnnotationsConstraint2_0),
57 AnyOf(AnyOfConstraint),
58 ByteLength(ByteLengthConstraint),
59 CodepointLength(CodepointLengthConstraint),
60 Contains(ContainsConstraint),
61 ContentClosed,
62 ContainerLength(ContainerLengthConstraint),
63 Element(ElementConstraint),
64 Exponent(ExponentConstraint),
65 FieldNames(FieldNamesConstraint),
66 Fields(FieldsConstraint),
67 Ieee754Float(Ieee754FloatConstraint),
68 Not(NotConstraint),
69 OneOf(OneOfConstraint),
70 OrderedElements(OrderedElementsConstraint),
71 Precision(PrecisionConstraint),
72 Regex(RegexConstraint),
73 Scale(ScaleConstraint),
74 TimestampOffset(TimestampOffsetConstraint),
75 TimestampPrecision(TimestampPrecisionConstraint),
76 Type(TypeConstraint),
77 Unknown(String, Element),
78 Utf8ByteLength(Utf8ByteLengthConstraint),
79 ValidValues(ValidValuesConstraint),
80}
81
82impl Constraint {
83 pub fn type_constraint(type_id: TypeId) -> Constraint {
85 Constraint::Type(TypeConstraint::new(TypeReference::new(
86 type_id,
87 NullabilityModifier::Nothing,
88 )))
89 }
90
91 pub fn all_of<A: Into<Vec<TypeId>>>(type_ids: A) -> Constraint {
93 let type_references = type_ids
94 .into()
95 .iter()
96 .map(|id| TypeReference::new(*id, NullabilityModifier::Nothing))
97 .collect();
98 Constraint::AllOf(AllOfConstraint::new(type_references))
99 }
100
101 pub fn any_of<A: Into<Vec<TypeId>>>(type_ids: A) -> Constraint {
103 let type_references = type_ids
104 .into()
105 .iter()
106 .map(|id| TypeReference::new(*id, NullabilityModifier::Nothing))
107 .collect();
108 Constraint::AnyOf(AnyOfConstraint::new(type_references))
109 }
110
111 pub fn one_of<A: Into<Vec<TypeId>>>(type_ids: A) -> Constraint {
113 let type_references = type_ids
114 .into()
115 .iter()
116 .map(|id| TypeReference::new(*id, NullabilityModifier::Nothing))
117 .collect();
118 Constraint::OneOf(OneOfConstraint::new(type_references))
119 }
120
121 pub fn not(type_id: TypeId) -> Constraint {
123 Constraint::Not(NotConstraint::new(TypeReference::new(
124 type_id,
125 NullabilityModifier::Nothing,
126 )))
127 }
128
129 pub fn ordered_elements<A: Into<Vec<TypeId>>>(type_ids: A) -> Constraint {
131 let type_references = type_ids
132 .into()
133 .iter()
134 .map(|id| {
135 VariablyOccurringTypeRef::new(
136 TypeReference::new(*id, NullabilityModifier::Nothing),
137 UsizeRange::new_single_value(1),
138 )
139 })
140 .collect();
141 Constraint::OrderedElements(OrderedElementsConstraint::new(type_references))
142 }
143
144 pub fn contains<A: Into<Vec<Element>>>(values: A) -> Constraint {
146 Constraint::Contains(ContainsConstraint::new(values.into()))
147 }
148
149 pub fn container_length(length: UsizeRange) -> Constraint {
151 Constraint::ContainerLength(ContainerLengthConstraint::new(length))
152 }
153
154 pub fn byte_length(length: UsizeRange) -> Constraint {
156 Constraint::ByteLength(ByteLengthConstraint::new(length))
157 }
158
159 pub fn codepoint_length(length: UsizeRange) -> Constraint {
161 Constraint::CodepointLength(CodepointLengthConstraint::new(length))
162 }
163
164 pub fn element(type_id: TypeId, requires_distinct: bool) -> Constraint {
166 Constraint::Element(ElementConstraint::new(
167 TypeReference::new(type_id, NullabilityModifier::Nothing),
168 requires_distinct,
169 ))
170 }
171
172 pub fn annotations<'a, A: IntoIterator<Item = &'a str>, B: IntoIterator<Item = Element>>(
174 annotations_modifiers: A,
175 annotations: B,
176 ) -> Constraint {
177 let annotations_modifiers: Vec<&str> = annotations_modifiers.into_iter().collect();
178 let annotations: Vec<Annotation> = annotations
179 .into_iter()
180 .map(|a| {
181 Annotation::new(
182 a.as_text().unwrap().to_owned(),
183 Annotation::is_annotation_required(
184 &a,
185 annotations_modifiers.contains(&"required"),
186 ),
187 IslVersion::V1_0,
188 )
189 })
190 .collect();
191 Constraint::Annotations(AnnotationsConstraint::new(
192 annotations_modifiers.contains(&"closed"),
193 annotations_modifiers.contains(&"ordered"),
194 annotations,
195 ))
196 }
197
198 pub fn annotations_v2_0(id: TypeId) -> Constraint {
200 Constraint::Annotations2_0(AnnotationsConstraint2_0::new(TypeReference::new(
201 id,
202 NullabilityModifier::Nothing,
203 )))
204 }
205
206 pub fn precision(precision: U64Range) -> Constraint {
208 Constraint::Precision(PrecisionConstraint::new(precision))
209 }
210
211 pub fn scale(scale: I64Range) -> Constraint {
213 Constraint::Scale(ScaleConstraint::new(scale))
214 }
215 pub fn exponent(exponent: I64Range) -> Constraint {
217 Constraint::Exponent(ExponentConstraint::new(exponent))
218 }
219
220 pub fn timestamp_precision(precision: TimestampPrecisionRange) -> Constraint {
222 Constraint::TimestampPrecision(TimestampPrecisionConstraint::new(precision))
223 }
224
225 pub fn timestamp_offset(offsets: Vec<TimestampOffset>) -> Constraint {
227 Constraint::TimestampOffset(TimestampOffsetConstraint::new(offsets))
228 }
229
230 pub fn utf8_byte_length(length: UsizeRange) -> Constraint {
232 Constraint::Utf8ByteLength(Utf8ByteLengthConstraint::new(length))
233 }
234
235 pub fn fields<I>(fields: I) -> Constraint
238 where
239 I: Iterator<Item = (String, TypeId)>,
240 {
241 let fields = fields
242 .map(|(field_name, type_id)| {
243 (
244 field_name,
245 VariablyOccurringTypeRef::new(
246 TypeReference::new(type_id, NullabilityModifier::Nothing),
247 UsizeRange::zero_or_one(),
248 ),
249 )
250 })
251 .collect();
252 Constraint::Fields(FieldsConstraint::new(fields, true))
253 }
254
255 pub fn field_names(type_id: TypeId, requires_distinct: bool) -> Constraint {
257 Constraint::FieldNames(FieldNamesConstraint::new(
258 TypeReference::new(type_id, NullabilityModifier::Nothing),
259 requires_distinct,
260 ))
261 }
262
263 pub fn valid_values(
266 valid_values: Vec<ValidValue>,
267 isl_version: IslVersion,
268 ) -> IonSchemaResult<Constraint> {
269 Ok(Constraint::ValidValues(ValidValuesConstraint::new(
270 valid_values,
271 isl_version,
272 )?))
273 }
274
275 pub fn regex(
277 case_insensitive: bool,
278 multi_line: bool,
279 expression: String,
280 isl_version: IslVersion,
281 ) -> IonSchemaResult<Constraint> {
282 let regex = IslRegexConstraint::new(case_insensitive, multi_line, expression);
283 Ok(Constraint::Regex(RegexConstraint::from_isl(
284 ®ex,
285 isl_version,
286 )?))
287 }
288
289 pub fn ieee754_float(interchange_format: Ieee754InterchangeFormat) -> Constraint {
291 Constraint::Ieee754Float(Ieee754FloatConstraint::new(interchange_format))
292 }
293
294 fn resolve_type_references(
296 isl_version: IslVersion,
297 type_references: &[IslTypeRef],
298 type_store: &mut TypeStore,
299 pending_types: &mut PendingTypes,
300 ) -> IonSchemaResult<Vec<TypeReference>> {
301 type_references
302 .iter()
303 .map(|t| IslTypeRef::resolve_type_reference(isl_version, t, type_store, pending_types))
304 .collect::<IonSchemaResult<Vec<TypeReference>>>()
305 }
306
307 pub(crate) fn resolve_from_isl_constraint(
309 isl_version: IslVersion,
310 isl_constraint: &IslConstraintValue,
311 type_store: &mut TypeStore,
312 pending_types: &mut PendingTypes,
313 open_content: bool, ) -> IonSchemaResult<Constraint> {
315 match isl_constraint {
317 IslConstraintValue::AllOf(isl_type_references) => {
318 let type_references = Constraint::resolve_type_references(
319 isl_version,
320 isl_type_references,
321 type_store,
322 pending_types,
323 )?;
324 Ok(Constraint::AllOf(AllOfConstraint::new(type_references)))
325 }
326 IslConstraintValue::Annotations(isl_annotations) => match isl_annotations {
327 IslAnnotationsConstraint::SimpleAnnotations(simple_annotations) => {
328 match isl_version {
329 IslVersion::V1_0 => {
330 Ok(Constraint::Annotations(AnnotationsConstraint::new(
331 simple_annotations.is_closed,
332 simple_annotations.is_ordered,
333 simple_annotations.annotations.to_owned(),
334 )))
335 }
336 IslVersion::V2_0 => {
337 let type_ref = IslTypeRef::resolve_type_reference(
338 IslVersion::V2_0,
339 &simple_annotations.convert_to_type_reference()?,
340 type_store,
341 pending_types,
342 )?;
343
344 Ok(Constraint::Annotations2_0(AnnotationsConstraint2_0::new(
345 type_ref,
346 )))
347 }
348 }
349 }
350 IslAnnotationsConstraint::StandardAnnotations(isl_type_ref) => {
351 let type_ref = IslTypeRef::resolve_type_reference(
352 isl_version,
353 isl_type_ref,
354 type_store,
355 pending_types,
356 )?;
357 Ok(Constraint::Annotations2_0(AnnotationsConstraint2_0::new(
358 type_ref,
359 )))
360 }
361 },
362 IslConstraintValue::AnyOf(isl_type_references) => {
363 let type_references = Constraint::resolve_type_references(
364 isl_version,
365 isl_type_references,
366 type_store,
367 pending_types,
368 )?;
369 Ok(Constraint::AnyOf(AnyOfConstraint::new(type_references)))
370 }
371 IslConstraintValue::ByteLength(byte_length) => Ok(Constraint::ByteLength(
372 ByteLengthConstraint::new(byte_length.to_owned()),
373 )),
374 IslConstraintValue::CodepointLength(codepoint_length) => {
375 Ok(Constraint::CodepointLength(CodepointLengthConstraint::new(
376 codepoint_length.to_owned(),
377 )))
378 }
379 IslConstraintValue::Contains(values) => {
380 let contains_constraint: ContainsConstraint =
381 ContainsConstraint::new(values.to_owned());
382 Ok(Constraint::Contains(contains_constraint))
383 }
384 IslConstraintValue::ContentClosed => Ok(Constraint::ContentClosed),
385 IslConstraintValue::ContainerLength(isl_length) => Ok(Constraint::ContainerLength(
386 ContainerLengthConstraint::new(isl_length.to_owned()),
387 )),
388 IslConstraintValue::Element(type_reference, require_distinct_elements) => {
389 let type_id = IslTypeRef::resolve_type_reference(
390 isl_version,
391 type_reference,
392 type_store,
393 pending_types,
394 )?;
395 Ok(Constraint::Element(ElementConstraint::new(
396 type_id,
397 *require_distinct_elements,
398 )))
399 }
400 IslConstraintValue::FieldNames(isl_type_reference, distinct) => {
401 let type_reference = IslTypeRef::resolve_type_reference(
402 isl_version,
403 isl_type_reference,
404 type_store,
405 pending_types,
406 )?;
407 Ok(Constraint::FieldNames(FieldNamesConstraint::new(
408 type_reference,
409 *distinct,
410 )))
411 }
412 IslConstraintValue::Fields(fields, content_closed) => {
413 let open_content = match isl_version {
414 IslVersion::V1_0 => open_content,
415 IslVersion::V2_0 => !content_closed, };
417 let fields_constraint: FieldsConstraint =
418 FieldsConstraint::resolve_from_isl_constraint(
419 isl_version,
420 fields,
421 type_store,
422 pending_types,
423 open_content,
424 )?;
425 Ok(Constraint::Fields(fields_constraint))
426 }
427 IslConstraintValue::Ieee754Float(iee754_interchange_format) => Ok(
428 Constraint::Ieee754Float(Ieee754FloatConstraint::new(*iee754_interchange_format)),
429 ),
430 IslConstraintValue::OneOf(isl_type_references) => {
431 let type_references = Constraint::resolve_type_references(
432 isl_version,
433 isl_type_references,
434 type_store,
435 pending_types,
436 )?;
437 Ok(Constraint::OneOf(OneOfConstraint::new(type_references)))
438 }
439 IslConstraintValue::Not(type_reference) => {
440 let type_id = IslTypeRef::resolve_type_reference(
441 isl_version,
442 type_reference,
443 type_store,
444 pending_types,
445 )?;
446 Ok(Constraint::Not(NotConstraint::new(type_id)))
447 }
448 IslConstraintValue::Type(type_reference) => {
449 let type_id = IslTypeRef::resolve_type_reference(
450 isl_version,
451 type_reference,
452 type_store,
453 pending_types,
454 )?;
455 Ok(Constraint::Type(TypeConstraint::new(type_id)))
456 }
457 IslConstraintValue::OrderedElements(isl_type_references) => {
458 Ok(Constraint::OrderedElements(
459 OrderedElementsConstraint::resolve_from_isl_constraint(
460 isl_version,
461 isl_type_references,
462 type_store,
463 pending_types,
464 )?,
465 ))
466 }
467 IslConstraintValue::Precision(precision_range) => {
468 isl_require!(precision_range.lower() != &Limit::Inclusive(0) => "precision range must have non-zero values")?;
469 Ok(Constraint::Precision(PrecisionConstraint::new(
470 precision_range.to_owned(),
471 )))
472 }
473 IslConstraintValue::Regex(regex) => Ok(Constraint::Regex(RegexConstraint::from_isl(
474 regex,
475 isl_version,
476 )?)),
477 IslConstraintValue::Scale(scale_range) => Ok(Constraint::Scale(ScaleConstraint::new(
478 scale_range.to_owned(),
479 ))),
480 IslConstraintValue::TimestampOffset(timestamp_offset) => {
481 Ok(Constraint::TimestampOffset(TimestampOffsetConstraint::new(
482 timestamp_offset.valid_offsets().to_vec(),
483 )))
484 }
485 IslConstraintValue::Exponent(exponent_range) => Ok(Constraint::Exponent(
486 ExponentConstraint::new(exponent_range.to_owned()),
487 )),
488 IslConstraintValue::TimestampPrecision(timestamp_precision_range) => {
489 Ok(Constraint::TimestampPrecision(
490 TimestampPrecisionConstraint::new(timestamp_precision_range.to_owned()),
491 ))
492 }
493 IslConstraintValue::Utf8ByteLength(utf8_byte_length) => Ok(Constraint::Utf8ByteLength(
494 Utf8ByteLengthConstraint::new(utf8_byte_length.to_owned()),
495 )),
496 IslConstraintValue::ValidValues(valid_values) => {
497 Ok(Constraint::ValidValues(ValidValuesConstraint {
498 valid_values: valid_values.values().to_owned(),
499 }))
500 }
501 IslConstraintValue::Unknown(constraint_name, element) => Ok(Constraint::Unknown(
502 constraint_name.to_owned(),
503 element.to_owned(),
504 )),
505 }
506 }
507
508 pub fn validate(
509 &self,
510 value: &IonSchemaElement,
511 type_store: &TypeStore,
512 ion_path: &mut IonPath,
513 ) -> ValidationResult {
514 match self {
515 Constraint::AllOf(all_of) => all_of.validate(value, type_store, ion_path),
516 Constraint::Annotations(annotations) => {
517 annotations.validate(value, type_store, ion_path)
518 }
519 Constraint::Annotations2_0(annotations) => {
520 annotations.validate(value, type_store, ion_path)
521 }
522 Constraint::AnyOf(any_of) => any_of.validate(value, type_store, ion_path),
523 Constraint::ByteLength(byte_length) => {
524 byte_length.validate(value, type_store, ion_path)
525 }
526 Constraint::CodepointLength(codepoint_length) => {
527 codepoint_length.validate(value, type_store, ion_path)
528 }
529 Constraint::Contains(contains) => contains.validate(value, type_store, ion_path),
530 Constraint::ContentClosed => {
531 Ok(())
536 }
537 Constraint::ContainerLength(container_length) => {
538 container_length.validate(value, type_store, ion_path)
539 }
540 Constraint::Element(element) => element.validate(value, type_store, ion_path),
541 Constraint::FieldNames(field_names) => {
542 field_names.validate(value, type_store, ion_path)
543 }
544 Constraint::Fields(fields) => fields.validate(value, type_store, ion_path),
545 Constraint::Ieee754Float(ieee754_float) => {
546 ieee754_float.validate(value, type_store, ion_path)
547 }
548 Constraint::Not(not) => not.validate(value, type_store, ion_path),
549 Constraint::OneOf(one_of) => one_of.validate(value, type_store, ion_path),
550 Constraint::Type(type_constraint) => {
551 type_constraint.validate(value, type_store, ion_path)
552 }
553 Constraint::OrderedElements(ordered_elements) => {
554 ordered_elements.validate(value, type_store, ion_path)
555 }
556 Constraint::Precision(precision) => precision.validate(value, type_store, ion_path),
557 Constraint::Regex(regex) => regex.validate(value, type_store, ion_path),
558 Constraint::Scale(scale) => scale.validate(value, type_store, ion_path),
559 Constraint::Exponent(exponent) => exponent.validate(value, type_store, ion_path),
560 Constraint::TimestampOffset(timestamp_offset) => {
561 timestamp_offset.validate(value, type_store, ion_path)
562 }
563 Constraint::TimestampPrecision(timestamp_precision) => {
564 timestamp_precision.validate(value, type_store, ion_path)
565 }
566 Constraint::Utf8ByteLength(utf8_byte_length) => {
567 utf8_byte_length.validate(value, type_store, ion_path)
568 }
569 Constraint::ValidValues(valid_values) => {
570 valid_values.validate(value, type_store, ion_path)
571 }
572 Constraint::Unknown(_, _) => {
573 Ok(())
576 }
577 }
578 }
579}
580
581#[derive(Debug, Clone, PartialEq, Eq)]
584pub struct AllOfConstraint {
585 type_references: Vec<TypeReference>,
586}
587
588impl AllOfConstraint {
589 pub fn new(type_references: Vec<TypeReference>) -> Self {
590 Self { type_references }
591 }
592}
593
594impl ConstraintValidator for AllOfConstraint {
595 fn validate(
596 &self,
597 value: &IonSchemaElement,
598 type_store: &TypeStore,
599 ion_path: &mut IonPath,
600 ) -> ValidationResult {
601 let mut violations: Vec<Violation> = vec![];
602 let mut valid_types = vec![];
603 for type_reference in &self.type_references {
604 match type_reference.validate(value, type_store, ion_path) {
605 Ok(_) => valid_types.push(type_reference.type_id()),
606 Err(violation) => violations.push(violation),
607 }
608 }
609 if !violations.is_empty() {
610 return Err(Violation::with_violations(
611 "all_of",
612 ViolationCode::AllTypesNotMatched,
613 format!(
614 "value matches {} types, expected {}",
615 valid_types.len(),
616 self.type_references.len()
617 ),
618 ion_path,
619 violations,
620 ));
621 }
622 Ok(())
623 }
624}
625
626#[derive(Debug, Clone, PartialEq, Eq)]
629pub struct AnyOfConstraint {
630 type_references: Vec<TypeReference>,
631}
632
633impl AnyOfConstraint {
634 pub fn new(type_references: Vec<TypeReference>) -> Self {
635 Self { type_references }
636 }
637}
638
639impl ConstraintValidator for AnyOfConstraint {
640 fn validate(
641 &self,
642 value: &IonSchemaElement,
643 type_store: &TypeStore,
644 ion_path: &mut IonPath,
645 ) -> ValidationResult {
646 let mut violations: Vec<Violation> = vec![];
647 let mut valid_types = vec![];
648 for type_reference in &self.type_references {
649 match type_reference.validate(value, type_store, ion_path) {
650 Ok(_) => valid_types.push(type_reference.type_id()),
651 Err(violation) => violations.push(violation),
652 }
653 }
654 let total_valid_types = valid_types.len();
655 if total_valid_types == 0 {
656 return Err(Violation::with_violations(
657 "any_of",
658 ViolationCode::NoTypesMatched,
659 "value matches none of the types",
660 ion_path,
661 violations,
662 ));
663 }
664 Ok(())
665 }
666}
667
668#[derive(Debug, Clone, PartialEq, Eq)]
671pub struct OneOfConstraint {
672 type_references: Vec<TypeReference>,
673}
674
675impl OneOfConstraint {
676 pub fn new(type_references: Vec<TypeReference>) -> Self {
677 Self { type_references }
678 }
679}
680
681impl ConstraintValidator for OneOfConstraint {
682 fn validate(
683 &self,
684 value: &IonSchemaElement,
685 type_store: &TypeStore,
686 ion_path: &mut IonPath,
687 ) -> ValidationResult {
688 let mut violations: Vec<Violation> = vec![];
689 let mut valid_types = vec![];
690 for type_reference in &self.type_references {
691 match type_reference.validate(value, type_store, ion_path) {
692 Ok(_) => valid_types.push(type_reference.type_id()),
693 Err(violation) => violations.push(violation),
694 }
695 }
696 let total_valid_types = valid_types.len();
697 match total_valid_types {
698 0 => Err(Violation::with_violations(
699 "one_of",
700 ViolationCode::NoTypesMatched,
701 "value matches none of the types",
702 ion_path,
703 violations,
704 )),
705 1 => Ok(()),
706 _ => Err(Violation::with_violations(
707 "one_of",
708 ViolationCode::MoreThanOneTypeMatched,
709 format!("value matches {total_valid_types} types, expected 1"),
710 ion_path,
711 violations,
712 )),
713 }
714 }
715}
716
717#[derive(Debug, Clone, PartialEq, Eq)]
720pub struct NotConstraint {
721 type_reference: TypeReference,
722}
723
724impl NotConstraint {
725 pub fn new(type_reference: TypeReference) -> Self {
726 Self { type_reference }
727 }
728}
729
730impl ConstraintValidator for NotConstraint {
731 fn validate(
732 &self,
733 value: &IonSchemaElement,
734 type_store: &TypeStore,
735 ion_path: &mut IonPath,
736 ) -> ValidationResult {
737 let violation = self.type_reference.validate(value, type_store, ion_path);
738 match violation {
739 Err(violation) => Ok(()),
740 Ok(_) => {
741 Err(Violation::new(
743 "not",
744 ViolationCode::TypeMatched,
745 "value unexpectedly matches type",
746 ion_path,
747 ))
748 }
749 }
750 }
751}
752
753#[derive(Debug, Clone, PartialEq, Eq)]
756pub struct TypeConstraint {
757 pub(crate) type_reference: TypeReference,
758}
759
760impl TypeConstraint {
761 pub fn new(type_reference: TypeReference) -> Self {
762 Self { type_reference }
763 }
764}
765
766impl ConstraintValidator for TypeConstraint {
767 fn validate(
768 &self,
769 value: &IonSchemaElement,
770 type_store: &TypeStore,
771 ion_path: &mut IonPath,
772 ) -> ValidationResult {
773 self.type_reference.validate(value, type_store, ion_path)
774 }
775}
776
777#[derive(Debug, Clone, PartialEq)]
780pub struct OrderedElementsConstraint {
781 nfa: OrderedElementsNfa,
782}
783
784impl OrderedElementsConstraint {
785 pub fn new(type_references: Vec<VariablyOccurringTypeRef>) -> Self {
786 let states: Vec<_> = type_references
787 .into_iter()
788 .map(|ty| (ty, None))
790 .collect();
791 OrderedElementsConstraint {
792 nfa: OrderedElementsNfa::new(states),
793 }
794 }
795
796 fn resolve_from_isl_constraint(
797 isl_version: IslVersion,
798 type_references: &[IslVariablyOccurringTypeRef],
799 type_store: &mut TypeStore,
800 pending_types: &mut PendingTypes,
801 ) -> IonSchemaResult<Self> {
802 let resolved_types = type_references
803 .iter()
804 .map(|t| {
805 let var_type_ref = t.resolve_type_reference(isl_version, type_store, pending_types);
807 var_type_ref.map(|it| (it, None))
809 })
810 .collect::<IonSchemaResult<Vec<_>>>()?;
811
812 Ok(OrderedElementsConstraint {
813 nfa: OrderedElementsNfa::new(resolved_types),
814 })
815 }
816}
817
818impl ConstraintValidator for OrderedElementsConstraint {
819 fn validate(
820 &self,
821 value: &IonSchemaElement,
822 type_store: &TypeStore,
823 ion_path: &mut IonPath,
824 ) -> ValidationResult {
825 let violations: Vec<Violation> = vec![];
826
827 let element_iter = match value.as_sequence_iter() {
828 Some(iter) => iter,
829 None => {
830 return Err(Violation::with_violations(
831 "ordered_elements",
832 ViolationCode::TypeMismatched,
833 format!(
834 "expected list, sexp, or document; found {}",
835 if value.is_null() {
836 format!("{value}")
837 } else {
838 format!("{}", value.ion_schema_type())
839 }
840 ),
841 ion_path,
842 violations,
843 ));
844 }
845 };
846
847 self.nfa.matches(element_iter, type_store, ion_path)
848 }
849}
850
851#[derive(Debug, Clone, PartialEq)]
854pub struct FieldsConstraint {
855 fields: HashMap<String, VariablyOccurringTypeRef>,
856 open_content: bool,
857}
858
859impl FieldsConstraint {
860 pub fn new(fields: HashMap<String, VariablyOccurringTypeRef>, open_content: bool) -> Self {
861 Self {
862 fields,
863 open_content,
864 }
865 }
866
867 pub fn open_content(&self) -> bool {
869 self.open_content
870 }
871
872 fn resolve_from_isl_constraint(
874 isl_version: IslVersion,
875 fields: &HashMap<String, IslVariablyOccurringTypeRef>,
876 type_store: &mut TypeStore,
877 pending_types: &mut PendingTypes,
878 open_content: bool, ) -> IonSchemaResult<Self> {
880 let resolved_fields: HashMap<String, VariablyOccurringTypeRef> = fields
881 .iter()
882 .map(|(f, t)| {
883 t.resolve_type_reference(isl_version, type_store, pending_types)
886 .map(|variably_occurring_type_ref| (f.to_owned(), variably_occurring_type_ref))
887 })
888 .collect::<IonSchemaResult<HashMap<String, VariablyOccurringTypeRef>>>()?;
889 Ok(FieldsConstraint::new(resolved_fields, open_content))
890 }
891}
892
893impl ConstraintValidator for FieldsConstraint {
894 fn validate(
895 &self,
896 value: &IonSchemaElement,
897 type_store: &TypeStore,
898 ion_path: &mut IonPath,
899 ) -> ValidationResult {
900 let mut violations: Vec<Violation> = vec![];
901
902 let ion_struct = value
904 .expect_element_of_type(&[IonType::Struct], "fields", ion_path)?
905 .as_struct()
906 .unwrap();
907
908 if !self.open_content() {
910 for (field_name, value) in ion_struct.iter() {
911 if !self.fields.contains_key(field_name.text().unwrap()) {
912 violations.push(Violation::new(
913 "fields",
914 ViolationCode::InvalidOpenContent,
915 format!("Found open content in the struct: {field_name}: {value}"),
916 ion_path,
917 ));
918 }
919 }
920 }
921
922 for (field_name, variably_occurring_type_ref) in &self.fields {
924 let type_reference = variably_occurring_type_ref.type_ref();
925 let values: Vec<&Element> = ion_struct.get_all(field_name).collect();
926
927 ion_path.push(IonPathElement::Field(field_name.to_owned()));
929
930 let occurs_range: &UsizeRange = variably_occurring_type_ref.occurs_range();
932
933 if !occurs_range.contains(&values.len()) {
935 violations.push(Violation::new(
936 "fields",
937 ViolationCode::TypeMismatched,
938 format!(
939 "Expected {} of field {}: found {}",
940 occurs_range,
941 field_name,
942 values.len()
943 ),
944 ion_path,
945 ));
946 }
947
948 for value in values {
950 let schema_element: IonSchemaElement = value.into();
951 if let Err(violation) =
952 type_reference.validate(&schema_element, type_store, ion_path)
953 {
954 violations.push(violation);
955 }
956 }
957
958 ion_path.pop();
960 }
961
962 if !violations.is_empty() {
964 return Err(Violation::with_violations(
965 "fields",
966 ViolationCode::FieldsNotMatched,
967 "value didn't satisfy fields constraint",
968 ion_path,
969 violations,
970 ));
971 }
972 Ok(())
973 }
974}
975
976#[derive(Debug, Clone, PartialEq)]
979pub struct FieldNamesConstraint {
980 type_reference: TypeReference,
981 requires_distinct: bool,
982}
983
984impl FieldNamesConstraint {
985 pub fn new(type_reference: TypeReference, requires_distinct: bool) -> Self {
986 Self {
987 type_reference,
988 requires_distinct,
989 }
990 }
991}
992
993impl ConstraintValidator for FieldNamesConstraint {
994 fn validate(
995 &self,
996 value: &IonSchemaElement,
997 type_store: &TypeStore,
998 ion_path: &mut IonPath,
999 ) -> ValidationResult {
1000 let mut violations: Vec<Violation> = vec![];
1001
1002 let mut field_name_set = HashSet::new();
1004
1005 let ion_struct = value
1006 .expect_element_of_type(&[IonType::Struct], "field_names", ion_path)?
1007 .as_struct()
1008 .unwrap();
1009
1010 for (field_name, _) in ion_struct.iter() {
1011 ion_path.push(IonPathElement::Field(field_name.text().unwrap().to_owned()));
1012 let field_name_symbol_as_element = Element::symbol(field_name);
1013 let schema_element: IonSchemaElement = (&field_name_symbol_as_element).into();
1014
1015 if let Err(violation) =
1016 self.type_reference
1017 .validate(&schema_element, type_store, ion_path)
1018 {
1019 violations.push(violation);
1020 }
1021 if self.requires_distinct && !field_name_set.insert(field_name.text().unwrap()) {
1022 violations.push(Violation::new(
1023 "field_names",
1024 ViolationCode::FieldNamesNotDistinct,
1025 format!(
1026 "expected distinct field names but found duplicate field name {field_name}",
1027 ),
1028 ion_path,
1029 ))
1030 }
1031 ion_path.pop();
1032 }
1033
1034 if !violations.is_empty() {
1035 return Err(Violation::with_violations(
1036 "field_names",
1037 ViolationCode::FieldNamesMismatched,
1038 "one or more field names don't satisfy field_names constraint",
1039 ion_path,
1040 violations,
1041 ));
1042 }
1043 Ok(())
1044 }
1045}
1046
1047#[derive(Debug, Clone, PartialEq, Eq)]
1050pub struct ContainsConstraint {
1051 values: Vec<Element>,
1054}
1055
1056impl ContainsConstraint {
1057 pub fn new(values: Vec<Element>) -> Self {
1058 Self { values }
1059 }
1060}
1061
1062impl ConstraintValidator for ContainsConstraint {
1063 fn validate(
1064 &self,
1065 value: &IonSchemaElement,
1066 type_store: &TypeStore,
1067 ion_path: &mut IonPath,
1068 ) -> ValidationResult {
1069 let values: Vec<IonData<&Element>> = if let Some(element_iter) = value.as_sequence_iter() {
1070 element_iter.map(IonData::from).collect()
1071 } else if let Some(strukt) = value.as_struct() {
1072 strukt.fields().map(|(k, v)| v).map(IonData::from).collect()
1073 } else {
1074 return Err(Violation::new(
1075 "contains",
1076 ViolationCode::TypeMismatched,
1077 format!(
1078 "expected list/sexp/struct/document found {}",
1079 if value.is_null() {
1080 format!("{value}")
1081 } else {
1082 format!("{}", value.ion_schema_type())
1083 }
1084 ),
1085 ion_path,
1086 ));
1087 };
1088
1089 let mut missing_values = vec![];
1091
1092 for expected_value in self.values.iter() {
1095 let expected = expected_value.into();
1096 if !values.contains(&expected) {
1097 missing_values.push(expected_value);
1098 }
1099 }
1100
1101 if !missing_values.is_empty() {
1103 return Err(Violation::new(
1104 "contains",
1105 ViolationCode::MissingValue,
1106 format!("{value} has missing value(s): {missing_values:?}"),
1107 ion_path,
1108 ));
1109 }
1110
1111 Ok(())
1112 }
1113}
1114
1115#[derive(Debug, Clone, PartialEq)]
1118pub struct ContainerLengthConstraint {
1119 length_range: UsizeRange,
1120}
1121
1122impl ContainerLengthConstraint {
1123 pub fn new(length_range: UsizeRange) -> Self {
1124 Self { length_range }
1125 }
1126
1127 pub fn length(&self) -> &UsizeRange {
1128 &self.length_range
1129 }
1130}
1131
1132impl ConstraintValidator for ContainerLengthConstraint {
1133 fn validate(
1134 &self,
1135 value: &IonSchemaElement,
1136 type_store: &TypeStore,
1137 ion_path: &mut IonPath,
1138 ) -> ValidationResult {
1139 let size = if let Some(element_iter) = value.as_sequence_iter() {
1141 element_iter.count()
1142 } else if let Some(strukt) = value.as_struct() {
1143 strukt.fields().map(|(k, v)| v).count()
1144 } else {
1145 return Err(Violation::new(
1146 "container_length",
1147 ViolationCode::TypeMismatched,
1148 if value.is_null() {
1149 format!("expected a container found {value}")
1150 } else {
1151 format!(
1152 "expected a container (a list/sexp/struct) but found {}",
1153 value.ion_schema_type()
1154 )
1155 },
1156 ion_path,
1157 ));
1158 };
1159
1160 let length_range: &UsizeRange = self.length();
1162
1163 if !length_range.contains(&size) {
1165 return Err(Violation::new(
1166 "container_length",
1167 ViolationCode::InvalidLength,
1168 format!("expected container length {length_range} found {size}"),
1169 ion_path,
1170 ));
1171 }
1172
1173 Ok(())
1174 }
1175}
1176
1177#[derive(Debug, Clone, PartialEq)]
1180pub struct ByteLengthConstraint {
1181 length_range: UsizeRange,
1182}
1183
1184impl ByteLengthConstraint {
1185 pub fn new(length_range: UsizeRange) -> Self {
1186 Self { length_range }
1187 }
1188
1189 pub fn length(&self) -> &UsizeRange {
1190 &self.length_range
1191 }
1192}
1193
1194impl ConstraintValidator for ByteLengthConstraint {
1195 fn validate(
1196 &self,
1197 value: &IonSchemaElement,
1198 type_store: &TypeStore,
1199 ion_path: &mut IonPath,
1200 ) -> ValidationResult {
1201 let size = value
1203 .expect_element_of_type(&[IonType::Blob, IonType::Clob], "byte_length", ion_path)?
1204 .as_lob()
1205 .unwrap()
1206 .len();
1207
1208 let length_range: &UsizeRange = self.length();
1210
1211 if !length_range.contains(&size) {
1213 return Err(Violation::new(
1214 "byte_length",
1215 ViolationCode::InvalidLength,
1216 format!("expected byte length {length_range} found {size}"),
1217 ion_path,
1218 ));
1219 }
1220
1221 Ok(())
1222 }
1223}
1224
1225#[derive(Debug, Clone, PartialEq)]
1228pub struct CodepointLengthConstraint {
1229 length_range: UsizeRange,
1230}
1231
1232impl CodepointLengthConstraint {
1233 pub fn new(length_range: UsizeRange) -> Self {
1234 Self { length_range }
1235 }
1236
1237 pub fn length(&self) -> &UsizeRange {
1238 &self.length_range
1239 }
1240}
1241
1242impl ConstraintValidator for CodepointLengthConstraint {
1243 fn validate(
1244 &self,
1245 value: &IonSchemaElement,
1246 type_store: &TypeStore,
1247 ion_path: &mut IonPath,
1248 ) -> ValidationResult {
1249 let size = value
1251 .expect_element_of_type(
1252 &[IonType::String, IonType::Symbol],
1253 "codepoint_length",
1254 ion_path,
1255 )?
1256 .as_text()
1257 .unwrap()
1258 .chars()
1259 .count();
1260
1261 let length_range: &UsizeRange = self.length();
1263
1264 if !length_range.contains(&size) {
1266 return Err(Violation::new(
1267 "codepoint_length",
1268 ViolationCode::InvalidLength,
1269 format!("expected codepoint length {length_range} found {size}"),
1270 ion_path,
1271 ));
1272 }
1273
1274 Ok(())
1275 }
1276}
1277
1278#[derive(Debug, Clone, PartialEq, Eq)]
1281pub struct ElementConstraint {
1282 type_reference: TypeReference,
1283 required_distinct_elements: bool,
1286}
1287
1288impl ElementConstraint {
1289 pub fn new(type_reference: TypeReference, required_distinct_elements: bool) -> Self {
1290 Self {
1291 type_reference,
1292 required_distinct_elements,
1293 }
1294 }
1295}
1296
1297impl ConstraintValidator for ElementConstraint {
1298 fn validate(
1299 &self,
1300 value: &IonSchemaElement,
1301 type_store: &TypeStore,
1302 ion_path: &mut IonPath,
1303 ) -> ValidationResult {
1304 let mut violations: Vec<Violation> = vec![];
1305
1306 let mut element_set = vec![];
1308
1309 let elements: Vec<(IonPathElement, &Element)> =
1311 if let Some(element_iter) = value.as_sequence_iter() {
1312 element_iter
1313 .enumerate()
1314 .map(|(index, val)| (IonPathElement::Index(index), val))
1315 .collect()
1316 } else if let Some(strukt) = value.as_struct() {
1317 strukt
1318 .fields()
1319 .map(|(name, val)| (IonPathElement::Field(name.to_string()), val))
1320 .collect()
1321 } else {
1322 if value.is_null() {
1324 return Err(Violation::new(
1325 "element",
1326 ViolationCode::TypeMismatched,
1327 format!("expected a container but found {value}"),
1328 ion_path,
1329 ));
1330 }
1331 return Err(Violation::new(
1333 "element",
1334 ViolationCode::TypeMismatched,
1335 format!(
1336 "expected a container (a list/sexp/struct) but found {}",
1337 value.ion_schema_type()
1338 ),
1339 ion_path,
1340 ));
1341 };
1342
1343 for (ion_path_element, val) in elements {
1345 ion_path.push(ion_path_element);
1346 let schema_element: IonSchemaElement = val.into();
1347
1348 if let Err(violation) =
1349 self.type_reference
1350 .validate(&schema_element, type_store, ion_path)
1351 {
1352 violations.push(violation);
1353 }
1354 if self.required_distinct_elements && element_set.contains(&val) {
1355 violations.push(Violation::new(
1356 "element",
1357 ViolationCode::ElementNotDistinct,
1358 format!("expected distinct elements but found duplicate element {val}",),
1359 ion_path,
1360 ))
1361 }
1362 element_set.push(val);
1363 ion_path.pop();
1364 }
1365
1366 if !violations.is_empty() {
1367 return Err(Violation::with_violations(
1368 "element",
1369 ViolationCode::ElementMismatched,
1370 "one or more elements don't satisfy element constraint",
1371 ion_path,
1372 violations,
1373 ));
1374 }
1375 Ok(())
1376 }
1377}
1378
1379#[derive(Debug, Clone, PartialEq, Eq)]
1384pub struct AnnotationsConstraint2_0 {
1385 type_ref: TypeReference,
1386}
1387
1388impl AnnotationsConstraint2_0 {
1389 pub fn new(type_ref: TypeReference) -> Self {
1390 Self { type_ref }
1391 }
1392}
1393
1394impl ConstraintValidator for AnnotationsConstraint2_0 {
1395 fn validate(
1396 &self,
1397 value: &IonSchemaElement,
1398 type_store: &TypeStore,
1399 ion_path: &mut IonPath,
1400 ) -> ValidationResult {
1401 if let Some(element) = value.as_element() {
1402 let annotations: Vec<Element> =
1403 element.annotations().iter().map(Element::symbol).collect();
1404 let annotations_element: Element = ion_rs::Value::List(annotations.into()).into();
1405 let annotations_ion_schema_element = IonSchemaElement::from(&annotations_element);
1406
1407 self.type_ref
1408 .validate(&annotations_ion_schema_element, type_store, ion_path)
1409 .map_err(|v| {
1410 Violation::with_violations(
1411 "annotations",
1412 ViolationCode::AnnotationMismatched,
1413 "one or more annotations don't satisfy annotations constraint",
1414 ion_path,
1415 vec![v],
1416 )
1417 })
1418 } else {
1419 Err(Violation::new(
1421 "annotations",
1422 ViolationCode::AnnotationMismatched,
1423 "annotations constraint is not applicable for document type",
1424 ion_path,
1425 ))
1426 }
1427 }
1428}
1429
1430#[derive(Debug, Clone, PartialEq, Eq)]
1435pub struct AnnotationsConstraint {
1436 is_closed: bool,
1437 is_ordered: bool,
1438 annotations: Vec<Annotation>,
1439}
1440
1441impl AnnotationsConstraint {
1442 pub fn new(is_closed: bool, is_ordered: bool, annotations: Vec<Annotation>) -> Self {
1443 Self {
1444 is_closed,
1445 is_ordered,
1446 annotations,
1447 }
1448 }
1449
1450 pub fn find_expected_annotation<'a, I: Iterator<Item = &'a str>>(
1453 &self,
1454 value_annotations: &mut Peekable<I>,
1455 expected_annotation: &Annotation,
1456 ) -> bool {
1457 while Option::is_some(&value_annotations.peek()) && !self.is_closed {
1460 if expected_annotation.value() == value_annotations.next().unwrap() {
1461 return true;
1462 }
1463 }
1464
1465 false
1467 }
1468
1469 pub fn validate_ordered_annotations(
1470 &self,
1471 value: &Element,
1472 type_store: &TypeStore,
1473 violations: Vec<Violation>,
1474 ion_path: &mut IonPath,
1475 ) -> ValidationResult {
1476 let mut value_annotations = value
1477 .annotations()
1478 .iter()
1479 .map(|sym| sym.text().unwrap())
1480 .peekable();
1481
1482 for expected_annotation in &self.annotations {
1484 if let Some(actual_annotation) = value_annotations.peek() {
1485 if expected_annotation.is_required()
1486 && expected_annotation.value() != actual_annotation
1487 {
1488 if !self.find_expected_annotation(&mut value_annotations, expected_annotation) {
1490 return Err(Violation::new(
1492 "annotations",
1493 ViolationCode::AnnotationMismatched,
1494 "annotations don't match expectations",
1495 ion_path,
1496 ));
1497 }
1498 } else if expected_annotation.value() == actual_annotation {
1499 let _ = value_annotations.next(); }
1501 } else if expected_annotation.is_required() {
1502 return Err(Violation::new(
1504 "annotations",
1505 ViolationCode::AnnotationMismatched,
1506 "annotations don't match expectations",
1507 ion_path,
1508 ));
1509 }
1510 }
1511
1512 if self.is_closed && Option::is_some(&value_annotations.peek()) {
1513 return Err(Violation::with_violations(
1515 "annotations",
1516 ViolationCode::AnnotationMismatched,
1517 format!(
1519 "Unexpected annotations found {}",
1520 value_annotations.next().unwrap()
1521 ),
1522 ion_path,
1523 violations,
1524 ));
1525 }
1526
1527 Ok(())
1528 }
1529
1530 pub fn validate_unordered_annotations(
1531 &self,
1532 value: &Element,
1533 type_store: &TypeStore,
1534 violations: Vec<Violation>,
1535 ion_path: &mut IonPath,
1536 ) -> ValidationResult {
1537 let mut missing_annotations: Vec<&Annotation> = vec![];
1539
1540 let value_annotations: Vec<&str> = value
1541 .annotations()
1542 .iter()
1543 .map(|sym| sym.text().unwrap())
1544 .collect();
1545
1546 for expected_annotation in &self.annotations {
1547 if expected_annotation.is_required()
1549 && !value.annotations().contains(expected_annotation.value())
1550 {
1551 missing_annotations.push(expected_annotation);
1552 }
1553 }
1554
1555 if !missing_annotations.is_empty() {
1557 return Err(Violation::with_violations(
1558 "annotations",
1559 ViolationCode::MissingAnnotation,
1560 format!("missing annotation(s): {missing_annotations:?}"),
1561 ion_path,
1562 violations,
1563 ));
1564 }
1565
1566 if self.is_closed
1569 && !value_annotations.iter().all(|v| {
1570 self.annotations
1571 .iter()
1572 .any(|expected_ann| v == expected_ann.value())
1573 })
1574 {
1575 return Err(Violation::with_violations(
1576 "annotations",
1577 ViolationCode::UnexpectedAnnotation,
1578 "found one or more unexpected annotations",
1579 ion_path,
1580 violations,
1581 ));
1582 }
1583
1584 Ok(())
1585 }
1586}
1587
1588impl ConstraintValidator for AnnotationsConstraint {
1589 fn validate(
1590 &self,
1591 value: &IonSchemaElement,
1592 type_store: &TypeStore,
1593 ion_path: &mut IonPath,
1594 ) -> ValidationResult {
1595 let violations: Vec<Violation> = vec![];
1596
1597 if let Some(element) = value.as_element() {
1598 if self.is_ordered {
1600 return self
1601 .validate_ordered_annotations(element, type_store, violations, ion_path);
1602 }
1603
1604 self.validate_unordered_annotations(element, type_store, violations, ion_path)
1606 } else {
1607 Err(Violation::new(
1609 "annotations",
1610 ViolationCode::AnnotationMismatched,
1611 "annotations constraint is not applicable for document type",
1612 ion_path,
1613 ))
1614 }
1615 }
1616}
1617
1618#[derive(Debug, Clone, PartialEq)]
1621pub struct PrecisionConstraint {
1622 precision_range: U64Range,
1623}
1624
1625impl PrecisionConstraint {
1626 pub fn new(precision_range: U64Range) -> Self {
1627 Self { precision_range }
1628 }
1629
1630 pub fn precision(&self) -> &U64Range {
1631 &self.precision_range
1632 }
1633}
1634
1635impl ConstraintValidator for PrecisionConstraint {
1636 fn validate(
1637 &self,
1638 value: &IonSchemaElement,
1639 type_store: &TypeStore,
1640 ion_path: &mut IonPath,
1641 ) -> ValidationResult {
1642 let value_precision = value
1644 .expect_element_of_type(&[IonType::Decimal], "precision", ion_path)?
1645 .as_decimal()
1646 .unwrap()
1647 .precision();
1648
1649 let precision_range: &U64Range = self.precision();
1651
1652 if !precision_range.contains(&value_precision) {
1654 return Err(Violation::new(
1655 "precision",
1656 ViolationCode::InvalidLength,
1657 format!("expected precision {precision_range} found {value_precision}"),
1658 ion_path,
1659 ));
1660 }
1661
1662 Ok(())
1663 }
1664}
1665
1666#[derive(Debug, Clone, PartialEq)]
1669pub struct ScaleConstraint {
1670 scale_range: I64Range,
1671}
1672
1673impl ScaleConstraint {
1674 pub fn new(scale_range: I64Range) -> Self {
1675 Self { scale_range }
1676 }
1677
1678 pub fn scale(&self) -> &I64Range {
1679 &self.scale_range
1680 }
1681}
1682
1683impl ConstraintValidator for ScaleConstraint {
1684 fn validate(
1685 &self,
1686 value: &IonSchemaElement,
1687 type_store: &TypeStore,
1688 ion_path: &mut IonPath,
1689 ) -> ValidationResult {
1690 let value_scale = value
1692 .expect_element_of_type(&[IonType::Decimal], "scale", ion_path)?
1693 .as_decimal()
1694 .unwrap()
1695 .scale();
1696
1697 let scale_range: &I64Range = self.scale();
1699
1700 if !scale_range.contains(&value_scale) {
1702 return Err(Violation::new(
1703 "scale",
1704 ViolationCode::InvalidLength,
1705 format!("expected scale {scale_range} found {value_scale}"),
1706 ion_path,
1707 ));
1708 }
1709
1710 Ok(())
1711 }
1712}
1713
1714#[derive(Debug, Clone, PartialEq)]
1717pub struct ExponentConstraint {
1718 exponent_range: I64Range,
1719}
1720
1721impl ExponentConstraint {
1722 pub fn new(exponent_range: I64Range) -> Self {
1723 Self { exponent_range }
1724 }
1725
1726 pub fn exponent(&self) -> &I64Range {
1727 &self.exponent_range
1728 }
1729}
1730
1731impl ConstraintValidator for ExponentConstraint {
1732 fn validate(
1733 &self,
1734 value: &IonSchemaElement,
1735 type_store: &TypeStore,
1736 ion_path: &mut IonPath,
1737 ) -> ValidationResult {
1738 let value_exponent = value
1740 .expect_element_of_type(&[IonType::Decimal], "exponent", ion_path)?
1741 .as_decimal()
1742 .unwrap()
1743 .scale()
1744 .neg();
1745
1746 let exponent_range: &I64Range = self.exponent();
1748
1749 if !exponent_range.contains(&value_exponent) {
1751 return Err(Violation::new(
1752 "exponent",
1753 ViolationCode::InvalidLength,
1754 format!("expected exponent {exponent_range} found {value_exponent}"),
1755 ion_path,
1756 ));
1757 }
1758
1759 Ok(())
1760 }
1761}
1762
1763#[derive(Debug, Clone, PartialEq)]
1766pub struct TimestampPrecisionConstraint {
1767 timestamp_precision_range: TimestampPrecisionRange,
1768}
1769
1770impl TimestampPrecisionConstraint {
1771 pub fn new(scale_range: TimestampPrecisionRange) -> Self {
1772 Self {
1773 timestamp_precision_range: scale_range,
1774 }
1775 }
1776
1777 pub fn timestamp_precision(&self) -> &TimestampPrecisionRange {
1778 &self.timestamp_precision_range
1779 }
1780}
1781
1782impl ConstraintValidator for TimestampPrecisionConstraint {
1783 fn validate(
1784 &self,
1785 value: &IonSchemaElement,
1786 type_store: &TypeStore,
1787 ion_path: &mut IonPath,
1788 ) -> ValidationResult {
1789 let timestamp_value = value
1791 .expect_element_of_type(&[IonType::Timestamp], "timestamp_precision", ion_path)?
1792 .as_timestamp()
1793 .unwrap();
1794
1795 let precision_range: &TimestampPrecisionRange = self.timestamp_precision();
1797 let precision = &TimestampPrecision::from_timestamp(×tamp_value);
1798 if !precision_range.contains(precision) {
1800 return Err(Violation::new(
1801 "precision",
1802 ViolationCode::InvalidLength,
1803 format!("expected precision {precision_range} found {precision:?}"),
1804 ion_path,
1805 ));
1806 }
1807
1808 Ok(())
1809 }
1810}
1811
1812#[derive(Debug, Clone, PartialEq)]
1815pub struct ValidValuesConstraint {
1816 valid_values: Vec<ValidValue>,
1817}
1818
1819impl ValidValuesConstraint {
1820 pub fn new(valid_values: Vec<ValidValue>, isl_version: IslVersion) -> IonSchemaResult<Self> {
1821 Ok(Self { valid_values })
1822 }
1823}
1824
1825impl Display for ValidValuesConstraint {
1826 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
1827 write!(f, "[ ")?;
1828 let mut itr = self.valid_values.iter();
1829 if let Some(item) = itr.next() {
1830 write!(f, "{item}")?;
1831 }
1832 for item in itr {
1833 write!(f, ", {item}")?;
1834 }
1835 write!(f, " ]")
1836 }
1837}
1838
1839impl ConstraintValidator for ValidValuesConstraint {
1840 fn validate(
1841 &self,
1842 value: &IonSchemaElement,
1843 type_store: &TypeStore,
1844 ion_path: &mut IonPath,
1845 ) -> ValidationResult {
1846 match value.as_element() {
1847 Some(element) => {
1848 for valid_value in &self.valid_values {
1849 let does_match = match valid_value {
1850 ValidValue::Element(valid_value) => {
1851 IonData::eq(valid_value, element.value())
1853 }
1854 ValidValue::NumberRange(range) => match element.any_number_as_decimal() {
1855 Some(d) => range.contains(&d),
1856 _ => false,
1857 },
1858 ValidValue::TimestampRange(range) => {
1859 if let Value::Timestamp(t) = element.value() {
1860 range.contains(t)
1861 } else {
1862 false
1863 }
1864 }
1865 };
1866 if does_match {
1867 return Ok(());
1868 }
1869 }
1870 Err(Violation::new(
1871 "valid_values",
1872 ViolationCode::InvalidValue,
1873 format!(
1874 "expected valid_values to be from {}, found {}",
1875 &self, element
1876 ),
1877 ion_path,
1878 ))
1879 }
1880 _ => Err(Violation::new(
1881 "valid_values",
1882 ViolationCode::InvalidValue,
1883 format!(
1884 "expected valid_values to be from {}, found {}",
1885 &self, value
1886 ),
1887 ion_path,
1888 )),
1889 }
1890 }
1891}
1892
1893#[derive(Debug, Clone)]
1896pub struct RegexConstraint {
1897 expression: Regex,
1898 case_insensitive: bool,
1899 multiline: bool,
1900}
1901
1902impl RegexConstraint {
1903 fn new(expression: Regex, case_insensitive: bool, multiline: bool) -> Self {
1904 Self {
1905 expression,
1906 case_insensitive,
1907 multiline,
1908 }
1909 }
1910
1911 fn from_isl(isl_regex: &IslRegexConstraint, isl_version: IslVersion) -> IonSchemaResult<Self> {
1912 let pattern =
1913 RegexConstraint::convert_to_pattern(isl_regex.expression().to_owned(), isl_version)?;
1914
1915 let regex = RegexBuilder::new(pattern.as_str())
1916 .case_insensitive(isl_regex.case_insensitive())
1917 .multi_line(isl_regex.multi_line())
1918 .build()
1919 .map_err(|e| {
1920 invalid_schema_error_raw(format!("Invalid regex {}", isl_regex.expression()))
1921 })?;
1922
1923 Ok(RegexConstraint::new(
1924 regex,
1925 isl_regex.case_insensitive(),
1926 isl_regex.multi_line(),
1927 ))
1928 }
1929
1930 fn convert_to_pattern(expression: String, isl_version: IslVersion) -> IonSchemaResult<String> {
1933 let mut sb = String::new();
1934 let mut si = expression.as_str().chars().peekable();
1935
1936 while let Some(ch) = si.next() {
1937 match ch {
1938 '[' => {
1939 sb.push(ch);
1942 RegexConstraint::parse_char_class(&mut sb, &mut si, isl_version)?;
1943 }
1944 '(' => {
1945 sb.push(ch);
1946 if let Some(ch) = si.next() {
1947 if ch == '?' {
1948 return invalid_schema_error(format!("invalid character {ch}"));
1949 }
1950 sb.push(ch)
1951 }
1952 }
1953 '\\' => {
1954 if let Some(ch) = si.next() {
1955 match ch {
1956 '.' | '^' | '$' | '|' | '?' | '*' | '+' | '\\' | '[' | ']' | '('
1959 | ')' | '{' | '}' | 'w' | 'W' | 'd' | 'D' => {
1960 sb.push('\\');
1961 sb.push(ch)
1962 }
1963 's' => sb.push_str("[ \\f\\n\\r\\t]"),
1964 'S' => sb.push_str("[^ \\f\\n\\r\\t]"),
1965 _ => {
1966 return invalid_schema_error(format!(
1967 "invalid escape character {ch}",
1968 ))
1969 }
1970 }
1971 }
1972 }
1973 '\r' => sb.push('\n'), _ => sb.push(ch),
1976 }
1977 RegexConstraint::parse_quantifier(&mut sb, &mut si)?;
1978 }
1979
1980 Ok(sb)
1981 }
1982
1983 fn parse_char_class(
1984 sb: &mut String,
1985 si: &mut Peekable<Chars<'_>>,
1986 isl_version: IslVersion,
1987 ) -> IonSchemaResult<()> {
1988 while let Some(ch) = si.next() {
1989 sb.push(ch);
1990 match ch {
1991 '&' => {
1992 if si.peek() == Some(&'&') {
1993 return invalid_schema_error("'&&' is not supported in a character class");
1994 }
1995 }
1996 '[' => return invalid_schema_error("'[' must be escaped within a character class"),
1997 '\\' => {
1998 if let Some(ch2) = si.next() {
1999 match ch2 {
2000 '[' | ']' | '\\' => sb.push(ch2),
2002 'd' | 's' | 'w' | 'D' | 'S' | 'W' => {
2003 match isl_version {
2004 IslVersion::V1_0 => {
2005 return invalid_schema_error(format!(
2007 "invalid sequence '{:?}' in character class",
2008 si
2009 ));
2010 }
2011 IslVersion::V2_0 => {
2012 if ch2 == 'w' {
2016 sb.pop();
2017 sb.push_str(r"[a-zA-Z0-9_]");
2018 } else if ch2 == 'W' {
2019 sb.pop();
2020 sb.push_str(r"[^a-zA-Z0-9_]");
2021 } else {
2022 sb.push(ch2);
2023 }
2024 }
2025 }
2026 }
2027 _ => {
2028 return invalid_schema_error(format!(
2029 "invalid sequence '\\{ch2}' in character class"
2030 ))
2031 }
2032 }
2033 }
2034 }
2035 ']' => return Ok(()),
2036 _ => {}
2037 }
2038 }
2039
2040 invalid_schema_error("character class missing ']'")
2041 }
2042
2043 fn parse_quantifier(sb: &mut String, si: &mut Peekable<Chars<'_>>) -> IonSchemaResult<()> {
2044 let initial_length = sb.len();
2045 if let Some(ch) = si.peek().cloned() {
2046 match ch {
2047 '?' | '*' | '+' => {
2048 if let Some(ch) = si.next() {
2049 sb.push(ch);
2050 }
2051 }
2052 '{' => {
2053 let ch = si.next().unwrap();
2055 sb.push(ch);
2056 let mut complete = false;
2058 for ch in si.by_ref() {
2059 match ch {
2060 '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | ',' => {
2061 sb.push(ch)
2062 }
2063 '}' => {
2064 sb.push(ch);
2065 complete = true;
2067 break;
2068 }
2069 _ => return invalid_schema_error(format!("invalid character {ch}")),
2070 }
2071 }
2072
2073 if !complete {
2074 return invalid_schema_error("range quantifier missing '}'");
2075 }
2076 }
2077 _ => {}
2078 }
2079 if sb.len() > initial_length {
2080 if let Some(ch) = si.peek().cloned() {
2081 match ch {
2082 '?' => return invalid_schema_error(format!("invalid character {ch}")),
2083
2084 '+' => return invalid_schema_error(format!("invalid character {ch}")),
2085 _ => {}
2086 }
2087 }
2088 }
2089 }
2090
2091 Ok(())
2092 }
2093}
2094
2095impl ConstraintValidator for RegexConstraint {
2096 fn validate(
2097 &self,
2098 value: &IonSchemaElement,
2099 type_store: &TypeStore,
2100 ion_path: &mut IonPath,
2101 ) -> ValidationResult {
2102 let string_value = value
2104 .expect_element_of_type(&[IonType::String, IonType::Symbol], "regex", ion_path)?
2105 .as_text()
2106 .unwrap();
2107
2108 let re = Regex::new(r"\r").unwrap();
2110 let result = re.replace_all(string_value, "\n");
2111 let value = result.to_string();
2112
2113 if !self.expression.is_match(value.as_str()) {
2115 return Err(Violation::new(
2116 "regex",
2117 ViolationCode::RegexMismatched,
2118 format!("{} doesn't match regex {}", value, self.expression),
2119 ion_path,
2120 ));
2121 }
2122
2123 Ok(())
2124 }
2125}
2126
2127impl PartialEq for RegexConstraint {
2128 fn eq(&self, other: &Self) -> bool {
2129 self.expression.as_str().eq(other.expression.as_str())
2130 && self.case_insensitive == other.case_insensitive
2131 && self.multiline == other.multiline
2132 }
2133}
2134
2135#[derive(Debug, Clone, PartialEq)]
2138pub struct Utf8ByteLengthConstraint {
2139 length_range: UsizeRange,
2140}
2141
2142impl Utf8ByteLengthConstraint {
2143 pub fn new(length_range: UsizeRange) -> Self {
2144 Self { length_range }
2145 }
2146
2147 pub fn length(&self) -> &UsizeRange {
2148 &self.length_range
2149 }
2150}
2151
2152impl ConstraintValidator for Utf8ByteLengthConstraint {
2153 fn validate(
2154 &self,
2155 value: &IonSchemaElement,
2156 type_store: &TypeStore,
2157 ion_path: &mut IonPath,
2158 ) -> ValidationResult {
2159 let size = value
2161 .expect_element_of_type(
2162 &[IonType::String, IonType::Symbol],
2163 "utf8_byte_length",
2164 ion_path,
2165 )?
2166 .as_text()
2167 .unwrap()
2168 .len();
2169
2170 let length_range: &UsizeRange = self.length();
2172
2173 if !length_range.contains(&size) {
2175 return Err(Violation::new(
2176 "utf8_byte_length",
2177 ViolationCode::InvalidLength,
2178 format!("expected utf8 byte length {length_range} found {size}"),
2179 ion_path,
2180 ));
2181 }
2182
2183 Ok(())
2184 }
2185}
2186
2187#[derive(Debug, Clone, PartialEq, Eq)]
2190pub struct TimestampOffsetConstraint {
2191 valid_offsets: Vec<TimestampOffset>,
2192}
2193
2194impl TimestampOffsetConstraint {
2195 pub fn new(valid_offsets: Vec<TimestampOffset>) -> Self {
2196 Self { valid_offsets }
2197 }
2198
2199 pub fn valid_offsets(&self) -> &Vec<TimestampOffset> {
2200 &self.valid_offsets
2201 }
2202}
2203
2204impl ConstraintValidator for TimestampOffsetConstraint {
2205 fn validate(
2206 &self,
2207 value: &IonSchemaElement,
2208 type_store: &TypeStore,
2209 ion_path: &mut IonPath,
2210 ) -> ValidationResult {
2211 let timestamp_value = value
2213 .expect_element_of_type(&[IonType::Timestamp], "timestamp_offset", ion_path)?
2214 .as_timestamp()
2215 .unwrap();
2216
2217 let valid_offsets: &Vec<TimestampOffset> = self.valid_offsets();
2219
2220 if !valid_offsets.contains(×tamp_value.offset().into()) {
2222 let formatted_valid_offsets: Vec<String> =
2223 valid_offsets.iter().map(|t| format!("{t}")).collect();
2224
2225 return Err(Violation::new(
2226 "timestamp_offset",
2227 ViolationCode::InvalidLength,
2228 format!(
2229 "expected timestamp offset from {:?} found {}",
2230 formatted_valid_offsets,
2231 <Option<i32> as Into<TimestampOffset>>::into(timestamp_value.offset())
2232 ),
2233 ion_path,
2234 ));
2235 }
2236
2237 Ok(())
2238 }
2239}
2240
2241#[derive(Debug, Clone, PartialEq, Eq)]
2244pub struct Ieee754FloatConstraint {
2245 interchange_format: Ieee754InterchangeFormat,
2246}
2247
2248impl Ieee754FloatConstraint {
2249 pub fn new(interchange_format: Ieee754InterchangeFormat) -> Self {
2250 Self { interchange_format }
2251 }
2252
2253 pub fn interchange_format(&self) -> Ieee754InterchangeFormat {
2254 self.interchange_format
2255 }
2256}
2257
2258impl ConstraintValidator for Ieee754FloatConstraint {
2259 fn validate(
2260 &self,
2261 value: &IonSchemaElement,
2262 type_store: &TypeStore,
2263 ion_path: &mut IonPath,
2264 ) -> ValidationResult {
2265 let float_value = value
2267 .expect_element_of_type(&[IonType::Float], "ieee754_float", ion_path)?
2268 .as_float()
2269 .unwrap();
2270
2271 if !float_value.is_finite() {
2272 return Ok(());
2273 }
2274
2275 let is_valid = match self.interchange_format {
2276 Ieee754InterchangeFormat::Binary16 => {
2277 half::f16::from_f64(float_value).to_f64() == float_value
2278 }
2279 Ieee754InterchangeFormat::Binary32 => float_value
2280 .to_f32()
2281 .and_then(|f32_value| f32_value.to_f64().map(|f64_value| f64_value == float_value))
2282 .unwrap_or(false),
2283 Ieee754InterchangeFormat::Binary64 => true,
2284 };
2285
2286 if is_valid {
2287 Ok(())
2288 } else {
2289 Err(Violation::new(
2290 "ieee754_float",
2291 ViolationCode::InvalidIeee754Float,
2292 format!(
2293 "value cannot be losslessly represented by the IEEE-754 {} interchange format.",
2294 self.interchange_format
2295 ),
2296 ion_path,
2297 ))
2298 }
2299 }
2300}