1use crate::import::Import;
10use crate::system::{TypeId, TypeStore};
11use crate::types::{TypeDefinition, TypeDefinitionImpl};
12use std::sync::Arc;
13
14#[derive(Debug, Clone)]
22pub struct Schema {
23 id: String,
24 types: Arc<TypeStore>,
25}
26
27impl Schema {
28 pub(crate) fn new<A: AsRef<str>>(id: A, types: Arc<TypeStore>) -> Self {
29 Self {
30 id: id.as_ref().to_owned(),
31 types,
32 }
33 }
34
35 pub fn id(&self) -> &str {
37 &self.id
38 }
39
40 fn import(&self, id: String) -> Option<Import> {
43 todo!()
44 }
45
46 fn imports(&self) -> SchemaTypeIterator {
48 todo!()
49 }
50
51 fn imported_types(&self) -> SchemaTypeIterator {
53 SchemaTypeIterator::new(Arc::clone(&self.types), self.types.get_imports())
54 }
55
56 pub fn get_imported_type<A: AsRef<str>>(&self, name: A) -> Option<TypeDefinition> {
59 let type_id = self.types.get_imported_type_id_by_name(name.as_ref())?;
60 Some(TypeDefinition::new(*type_id, Arc::clone(&self.types)))
61 }
62
63 pub fn get_built_in_or_defined_type<A: AsRef<str>>(&self, name: A) -> Option<TypeDefinition> {
66 let type_id = self
67 .types
68 .get_built_in_type_id_or_defined_type_id_by_name(name.as_ref())?;
69 Some(TypeDefinition::new(*type_id, Arc::clone(&self.types)))
70 }
71
72 pub fn get_type<A: AsRef<str>>(&self, name: A) -> Option<TypeDefinition> {
75 let type_id = self.types.get_type_id_by_name(name.as_ref())?;
76 Some(TypeDefinition::new(*type_id, Arc::clone(&self.types)))
77 }
78
79 pub fn get_types(&self) -> SchemaTypeIterator {
82 SchemaTypeIterator::new(Arc::clone(&self.types), self.types.get_types())
83 }
84
85 fn plus_type(&self, schema_type: TypeDefinitionImpl) -> Self {
90 todo!()
91 }
92}
93
94pub struct SchemaTypeIterator {
96 type_store: Arc<TypeStore>,
97 index: usize,
98 types: Vec<TypeId>,
99}
100
101impl SchemaTypeIterator {
102 fn new(type_store: Arc<TypeStore>, types: Vec<TypeId>) -> Self {
103 Self {
104 type_store,
105 index: 0,
106 types,
107 }
108 }
109}
110
111impl Iterator for SchemaTypeIterator {
112 type Item = TypeDefinition;
113
114 fn next(&mut self) -> Option<Self::Item> {
115 if self.index >= self.types.len() {
116 return None;
117 }
118 self.index += 1;
119 Some(TypeDefinition::new(
120 self.types[self.index - 1],
121 Arc::clone(&self.type_store),
122 ))
123 }
124}
125
126#[cfg(test)]
127mod schema_tests {
128 use super::*;
129 use crate::authority::MapDocumentAuthority;
130 use crate::system::{Resolver, SchemaSystem};
131 use ion_rs::Element;
132 use rstest::*;
133 use std::sync::Arc;
134
135 fn load(text: &str) -> Vec<Element> {
137 Element::read_all(text.as_bytes())
138 .expect("parsing failed unexpectedly")
139 .into_iter()
140 .collect()
141 }
142
143 fn load_schema_from_text(text: &str) -> Arc<Schema> {
145 let map_authority = [("sample.isl", text)];
147 let mut schema_system =
148 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
149 schema_system.load_schema("sample.isl").unwrap()
150 }
151
152 #[rstest(
153 owned_elements, total_types,
154 case::type_constraint_with_named_type(
155 load(r#" // For a schema with named type as below:
156 type:: { name: my_type, type: any }
157 "#).into_iter(),
158 1 ),
160 case::type_constraint_with_self_reference_type(
161 load(r#" // For a schema with self reference type as below:
162 type:: { name: my_int, type: my_int }
163 "#).into_iter(),
164 1 ),
166 case::type_constraint_with_nested_self_reference_type(
167 load(r#" // For a schema with nested self reference type as below:
168 type:: { name: my_int, type: { type: my_int } }
169 "#).into_iter(),
170 1 ),
172 case::type_constraint_with_nested_type(
173 load(r#" // For a schema with nested types as below:
174 type:: { name: my_int, type: { type: int } }
175 "#).into_iter(),
176 1 ),
178 case::type_constraint_with_nested_multiple_types(
179 load(r#" // For a schema with nested multiple types as below:
180 type:: { name: my_int, type: { type: int }, type: { type: my_int } }
181 "#).into_iter(),
182 1 ),
184 case::type_constraint_with_multiple_types(
185 load(r#" // For a schema with multiple type as below:
186 type:: { name: my_int, type: int }
187 type:: { name: my_bool, type: bool }
188 "#).into_iter(),
189 2
190 ),
191 case::all_of_constraint(
192 load(r#" // For a schema with all_of type as below:
193 type:: { name: all_of_type, all_of: [{ type: int }] }
194 "#).into_iter(),
195 1 ),
197 case::any_of_constraint(
198 load(r#" // For a schema with any_of constraint as below:
199 type:: { name: any_of_type, any_of: [{ type: int }, { type: decimal }] }
200 "#).into_iter(),
201 1 ),
203 case::one_of_constraint(
204 load(r#" // For a schema with one_of constraint as below:
205 type:: { name: one_of_type, one_of: [{ type: int }, { type: decimal }] }
206 "#).into_iter(),
207 1 ),
209 case::not_constraint(
210 load(r#" // For a schema with not constraint as below:
211 type:: { name: not_type, not: { type: int } }
212 "#).into_iter(),
213 1 ),
215 case::ordred_elements_constraint(
216 load(r#" // For a schema with ordered_elements constraint as below:
217 type:: { name: ordred_elements_type, ordered_elements: [ symbol, { type: int, occurs: optional }, ] }
218 "#).into_iter(),
219 1 ),
221 case::fields_constraint(
222 load(r#" // For a schema with fields constraint as below:
223 type:: { name: fields_type, fields: { name: string, id: int} }
224 "#).into_iter(),
225 1 ),
227 case::field_names_constraint(
228 load(r#" // For a schema with field_names constraint as below:
229 $ion_schema_2_0
230 type:: { name: field_names_type, field_names: distinct::symbol }
231 "#).into_iter(),
232 1 ),
234 case::contains_constraint(
235 load(r#" // For a schema with contains constraint as below:
236 type:: { name: contains_type, contains: [true, 1, "hello"] }
237 "#).into_iter(),
238 1 ),
240 case::container_length_constraint(
241 load(r#" // For a schema with container_length constraint as below:
242 type:: { name: container_length_type, container_length: 3 }
243 "#).into_iter(),
244 1 ),
246 case::byte_length_constraint(
247 load(r#" // For a schema with byte_length constraint as below:
248 type:: { name: byte_length_type, byte_length: 3 }
249 "#).into_iter(),
250 1 ),
252 case::codepoint_length_constraint(
253 load(r#" // For a schema with codepoint_length constraint as below:
254 type:: { name: codepoint_length_type, codepoint_length: 3 }
255 "#).into_iter(),
256 1 ),
258 case::element_constraint(
259 load(r#" // For a schema with element constraint as below:
260 type:: { name: element_type, element: int }
261 "#).into_iter(),
262 1 ),
264 case::distinct_element_constraint(
265 load(r#" // For a schema with distinct element constraint as below:
266 $ion_schema_2_0
267 type:: { name: distinct_element_type, element: distinct::int }
268 "#).into_iter(),
269 1 ),
271 case::annotations_constraint(
272 load(r#" // For a schema with annotations constraint as below:
273 type:: { name: annotations_type, annotations: closed::[red, blue, green] }
274 "#).into_iter(),
275 1 ),
277 case::precision_constraint(
278 load(r#" // For a schema with precision constraint as below:
279 type:: { name: precision_type, precision: 2 }
280 "#).into_iter(),
281 1 ),
283 case::scale_constraint(
284 load(r#" // For a schema with scale constraint as below:
285 type:: { name: scale_type, scale: 2 }
286 "#).into_iter(),
287 1 ),
289 case::exponent_constraint(
290 load(r#" // For a schema with exponent constraint as below:
291 $ion_schema_2_0
292 type:: { name: exponent_type, exponent: -2 }
293 "#).into_iter(),
294 1 ),
296 case::timestamp_precision_constraint(
297 load(r#" // For a schema with timestamp_precision constraint as below:
298 type:: { name: timestamp_precision_type, timestamp_precision: month }
299 "#).into_iter(),
300 1 ),
302 case::valid_values_constraint(
303 load(r#" // For a schema with valid_values constraint as below:
304 type:: { name: valid_values_type, valid_values: range::[1, 3] }
305 "#).into_iter(),
306 1 ),
308 case::utf8_byte_length_constraint(
309 load(r#" // For a schema with utf8_byte_length constraint as below:
310 type:: { name: utf8_byte_length_type, utf8_byte_length: 3 }
311 "#).into_iter(),
312 1 ),
314 case::regex_constraint(
315 load(r#" // For a schema with regex constraint as below:
316 type:: { name: regex_type, regex: "[abc]" }
317 "#).into_iter(),
318 1 ),
320 case::timestamp_offset_constraint(
321 load(r#" // For a schema with timestamp_offset constraint as below:
322 type:: { name: timestamp_offset_type, timestamp_offset: ["+07:00", "+08:00", "+08:45", "+09:00"] }
323 "#).into_iter(),
324 1 ),
326 case::ieee754_float_constraint(
327 load(r#" // For a schema with ieee754_float constraint as below:
328 $ion_schema_2_0
329 type:: { name: ieee754_float_type, ieee754_float: binary16 }
330 "#).into_iter(),
331 1 ),
333 )]
334 fn owned_elements_to_schema<I: Iterator<Item = Element>>(
335 owned_elements: I,
336 total_types: usize,
337 ) {
338 let type_store = &mut TypeStore::default();
340 let mut resolver = Resolver::new(vec![]);
341
342 let isl_result = resolver.isl_schema_from_elements(owned_elements, "my_schema.isl");
344 assert!(isl_result.is_ok());
345
346 let isl = isl_result.expect("ISL schema should be syntactically correct");
347 let schema = resolver.schema_from_isl_schema(isl.version(), isl, type_store, None);
349 assert!(schema.is_ok());
350
351 assert_eq!(schema.unwrap().get_types().count(), total_types);
353 }
354
355 #[rstest(
356 valid_values, invalid_values, schema, type_name,
357 case::built_in_type(
358 load(r#"
359 5
360 0
361 -2
362 "#),
363 load(r#"
364 false
365 "hello"
366 5.4
367 "#),
368 load_schema_from_text(r#" // No schema defined, uses built-in types"#),
369 "int"
370 ),
371 case::type_constraint(
372 load(r#"
373 5
374 0
375 -2
376 "#),
377 load(r#"
378 false
379 "hello"
380 5.4
381 "#),
382 load_schema_from_text(r#" // For a schema with named type as below:
383 type:: { name: my_int, type: int }
384 "#),
385 "my_int"
386 ),
387 case::nullable_annotation_int_type_constraint(
388 load(r#"
389 null
390 null.null
391 null.int
392 0
393 -5
394 "#),
395 load(r#"
396 null.decimal
397 a
398 "hello"
399 false
400 "#),
401 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
402 type:: { name: my_int, type: nullable::int }
403 "#),
404 "my_int"
405 ),
406 case::null_or_annotation_int_type_constraint(
407 load(r#"
408 null
409 null.null
410 0
411 -5
412 "#),
413 load(r#"
414 null.decimal
415 a
416 "hello"
417 false
418 "#),
419 load_schema_from_text(r#" // For a schema with named type and `$null_or` annotation as below:
420 $ion_schema_2_0
421 type:: { name: my_int, type: $null_or::int }
422 "#),
423 "my_int"
424 ),
425 case::nullable_annotation_float_type_constraint(
426 load(r#"
427 null
428 null.null
429 null.float
430 5e2
431 "#),
432 load(r#"
433 null.decimal
434 a
435 "hello"
436 false
437 10
438 "#),
439 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
440 type:: { name: my_float, type: nullable::float }
441 "#),
442 "my_float"
443 ),
444 case::nullable_annotation_string_type_constraint(
445 load(r#"
446 null
447 null.null
448 null.string
449 "hi"
450 "#),
451 load(r#"
452 null.decimal
453 null.symbol
454 hello
455 false
456 10
457 "#),
458 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
459 type:: { name: my_string, type: nullable::string }
460 "#),
461 "my_string"
462 ),
463 case::nullable_annotation_symbol_type_constraint(
464 load(r#"
465 null
466 null.null
467 null.symbol
468 a
469 "#),
470 load(r#"
471 null.string
472 10.5
473 "hello"
474 false
475 10
476 "#),
477 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
478 type:: { name: my_symbol, type: nullable::symbol }
479 "#),
480 "my_symbol"
481 ),
482 case::nullable_annotation_decimal_type_constraint(
483 load(r#"
484 null
485 null.null
486 null.decimal
487 10.5
488 "#),
489 load(r#"
490 null.int
491 a
492 "hello"
493 false
494 10
495 "#),
496 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
497 type:: { name: my_decimal, type: nullable::decimal }
498 "#),
499 "my_decimal"
500 ),
501 case::nullable_annotation_timestamp_type_constraint(
502 load(r#"
503 null
504 null.null
505 null.timestamp
506 2000-01T
507 "#),
508 load(r#"
509 null.decimal
510 a
511 "hello"
512 false
513 10
514 "#),
515 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
516 type:: { name: my_timestamp, type: nullable::timestamp }
517 "#),
518 "my_timestamp"
519 ),
520 case::nullable_annotation_blob_type_constraint(
521 load(r#"
522 null
523 null.null
524 null.blob
525 {{ aGVsbG8= }}
526 "#),
527 load(r#"
528 null.clob
529 a
530 "hello"
531 false
532 10
533 "#),
534 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
535 type:: { name: my_blob, type: nullable::blob }
536 "#),
537 "my_blob"
538 ),
539 case::nullable_annotation_clob_type_constraint(
540 load(r#"
541 null
542 null.null
543 null.clob
544 {{"12345"}}
545 "#),
546 load(r#"
547 null.blob
548 a
549 "hello"
550 false
551 10
552 "#),
553 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
554 type:: { name: my_clob, type: nullable::clob }
555 "#),
556 "my_clob"
557 ),
558 case::nullable_annotation_text_type_constraint(
559 load(r#"
560 null
561 null.null
562 null.symbol
563 null.string
564 "hello"
565 hello
566 "#),
567 load(r#"
568 null.decimal
569 10.5
570 false
571 10
572 "#),
573 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
574 type:: { name: my_text, type: nullable::text }
575 "#),
576 "my_text"
577 ),
578 case::nullable_annotation_lob_type_constraint(
579 load(r#"
580 null
581 null.null
582 null.blob
583 null.clob
584 {{ aGVsbG8= }}
585 {{"12345"}}
586 "#),
587 load(r#"
588 null.decimal
589 10.5
590 false
591 10
592 "#),
593 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
594 type:: { name: my_lob, type: nullable::lob }
595 "#),
596 "my_lob"
597 ),
598 case::nullable_annotation_number_type_constraint(
599 load(r#"
600 null
601 null.null
602 null.int
603 null.float
604 null.decimal
605 10.5
606 10e2
607 5
608 "#),
609 load(r#"
610 null.string
611 "hello"
612 false
613 a
614 "#),
615 load_schema_from_text(r#" // For a schema with named type and `nullable` annotation as below:
616 type:: { name: my_number, type: nullable::number }
617 "#),
618 "my_number"
619 ),
620 case::nullable_atomic_type_constraint(
621 load(r#"
622 5
623 0
624 -2
625 null.int
626 "#),
627 load(r#"
628 false
629 "hello"
630 5.4
631 "#),
632 load_schema_from_text(r#" // For a schema with named type as below:
633 type:: { name: my_nullable_int, type: $int }
634 "#),
635 "my_nullable_int"
636 ),
637 case::nullable_derived_type_constraint(
638 load(r#"
639 "hello"
640 hello
641 null.string
642 null.symbol
643 "#),
644 load(r#"
645 false
646 5
647 null.int
648 null.decimal
649 null.null
650 5.4
651 "#),
652 load_schema_from_text(r#" // For a schema with named type as below:
653 type:: { name: my_nullable_text, type: $text }
654 "#),
655 "my_nullable_text"
656 ),
657 case::not_constraint(
658 load(r#"
659 true
660 "hello"
661 5.4
662 6e10
663 "#),
664 load(r#"
665 5
666 0
667 -1
668 "#),
669 load_schema_from_text(r#" // For a schema with not constraint as below:
670 type:: { name: not_type, not: { type: int } }
671 "#),
672 "not_type"
673 ),
674 case::one_of_constraint(
675 load(r#"
676 5
677 -5
678 5.4
679 -5.4
680 "#),
681 load(r#"
682 false
683 "hello"
684 hey
685 null.int
686 "#),
687 load_schema_from_text(r#" // For a schema with one_of constraint as below:
688 type:: { name: one_of_type, one_of: [int, decimal] }
689 "#),
690 "one_of_type"
691 ),
692 case::any_of_constraint(
694 load(r#"
695 5
696 5.4
697 true
698 "#),
699 load(r#"
700 "hello"
701 hey
702 6e10
703 "#),
704 load_schema_from_text(r#" // For a schema with any_of constraint as below:
705 type:: { name: any_of_type, any_of: [int, decimal, bool] }
706 "#),
707 "any_of_type"
708 ),
709 case::ordered_elements_constraint(
710 load(r#"
711 [true, 5, 6, 7, "hey"]
712 [false, 5, 6, 7]
713 [false, 7, 8, "hello"]
714 [true, 7, "hi"]
715 [true, 8]
716 "#),
717 load(r#"
718 [true]
719 [5, true, "hey"]
720 [null.bool, 5]
721 ["hello", 5]
722 [true, "hey"]
723 "hello"
724 hey
725 6e10
726 null.list
727 "#),
728 load_schema_from_text(r#" // For a schema with ordered_elements constraint as below:
729 type:: { name: ordered_elements_type, ordered_elements: [bool, { type: int, occurs: range::[1, 3] }, { type: string, occurs: optional } ] }
730 "#),
731 "ordered_elements_type"
732 ),
733 case::ordered_elements_constraint_for_overlapping_types(
734 load(r#"
735 [1, 2, 3]
736 [1, 2, foo]
737 [1.0, foo]
738 [1, 2.0, 3]
739 [1, 2]
740 [1, foo]
741 "#),
742 load(r#"
743 [1]
744 [foo]
745 [true, 1, foo]
746 "#),
747 load_schema_from_text(r#" // For a schema with ordered_elements constraint as below:
748 type:: { name: ordered_elements_type, ordered_elements:[{ type: int, occurs: optional }, { type: number, occurs: required }, { type: any, occurs: required }] }
749 "#),
750 "ordered_elements_type"
751 ),
752 case::fields_constraint(
753 load(r#"
754 { name: "Ion", id: 1 }
755 { id: 1 }
756 { name: "Ion" }
757 { name: "Ion", id: 1, name: "Schema" }
758 { } // This is valid because all fields are optional
759 { greetings: "hello" } // This is valid because open content is allowed by default
760 "#),
761 load(r#"
762 null.struct
763 null
764 { name: "Ion", id: 1, id: 2 }
765 "#),
766 load_schema_from_text(r#" // For a schema with fields constraint as below:
767 type:: { name: fields_type, fields: { name: { type: string, occurs: range::[0,2] }, id: int } }
768 "#),
769 "fields_type"
770 ),
771 case::fields_constraint_with_closed_content(
772 load(r#"
773 { name: "Ion", id: 1 }
774 { id: 1 }
775 { name: "Ion" }
776 { name: "Ion", id: 1, name: "Schema" }
777 { } // This is valid because all fields are optional
778 { greetings: "hello" } // This is valid because open content is allowed by default
779 "#),
780 load(r#"
781 null.struct
782 null
783 { name: "Ion", id: 1, id: 2 }
784 "#),
785 load_schema_from_text(r#" // For a schema with fields constraint as below:
786 type:: { name: fields_type, fields: { name: { type: string, occurs: range::[0,2] }, id: int } }
787 "#),
788 "fields_type"
789 ),
790 case::fields_constraint_with_closed_annotation(
791 load(r#"
792 { name: "Ion", id: 1 }
793 { id: 1 }
794 { name: "Ion" }
795 { name: "Ion", id: 1, name: "Schema" }
796 { }
797 "#),
798 load(r#"
799 null.struct
800 null
801 { name: "Ion", id: 1, id: 2 }
802 { greetings: "hello" }
803 "#),
804 load_schema_from_text(r#" // For a schema with fields constraint with `closed` annotation as below:
805 $ion_schema_2_0
806 type:: { name: fields_type, fields: closed::{ name: { type: string, occurs: range::[0,2] }, id: int } }
807 "#),
808 "fields_type"
809 ),
810 case::field_names_constraint(
811 load(r#"
812 { name: "Ion", id: 1 }
813 { id: 1 }
814 { name: "Ion" }
815 { }
816 "#),
817 load(r#"
818 null.struct
819 null
820 { name: "Ion", id: 1, name: "Schema" }
821 { name: "Ion", id: 1, id: 2 }
822 "#),
823 load_schema_from_text(r#" // For a schema with field_names constraint as below:
824 $ion_schema_2_0
825 type:: { name: field_names_type, field_names: distinct::symbol }
826 "#),
827 "field_names_type"
828 ),
829 case::contains_constraint(
830 load(r#"
831 [[5], '3', {a: 7}, true, 2.0, "4", (6), 1, extra_value]
832 ([5] '3' {a: 7} true 2.0 "4" (6) 1 extra_value)
833 "#),
834 load(r#"
835 null
836 null.null
837 null.int
838 null.list
839 null.sexp
840 null.struct
841 [true, 1, 2.0, '3', "4", [5], (6)]
842 "#),
843 load_schema_from_text(r#" // For a schema with contains constraint as below:
844 type::{ name: contains_type, contains: [true, 1, 2.0, '3', "4", [5], (6), {a: 7} ] }
845 "#),
846 "contains_type"
847 ),
848 case::container_length_with_range_constraint(
849 load(r#"
850 [1]
851 [1, 2]
852 [1, 2, 3]
853 (4)
854 (4 5)
855 (4 5 6)
856 { a: 7 }
857 { a: 7, b: 8 }
858 { a: 7, b: 8, c: 9 }
859 "#),
860 load(r#"
861 null
862 null.bool
863 null.null
864 null.list
865 null.sexp
866 null.struct
867 []
868 ()
869 {}
870 [1, 2, 3, 4]
871 (1 2 3 4)
872 { a: 1, b:2, c:3, d:4}
873 "#),
874 load_schema_from_text(r#" // For a schema with contianer_length constraint as below:
875 type::{ name: container_length_type, container_length: range::[1,3] }
876 "#),
877 "container_length_type"
878 ),
879 case::container_length_exact_constraint(
880 load(r#"
881 [null, null, null]
882 [1, 2, 3]
883 (4 5 6)
884 { a: 7, b: 8, c: 9 }
885 "#),
886 load(r#"
887 null
888 null.bool
889 null.null
890 null.list
891 null.sexp
892 null.struct
893 []
894 ()
895 {}
896 [1]
897 (1)
898 { a: 1 }
899 [1, 2, 3, 4]
900 (1 2 3 4)
901 { a: 1, b:2, c:3, d:4}
902 "#),
903 load_schema_from_text(r#" // For a schema with contianer_length constraint as below:
904 type::{ name: container_length_type, container_length: 3 }
905 "#),
906 "container_length_type"
907 ),
908 case::byte_length_constraint(
909 load(r#"
910 {{"12345"}}
911 {{ aGVsbG8= }}
912 "#),
913 load(r#"
914 null
915 null.bool
916 null.null
917 null.clob
918 null.blob
919 {{}}
920 {{"1234"}}
921 {{"123456"}}
922 "#),
923 load_schema_from_text(r#" // For a schema with byte_length constraint as below:
924 type::{ name: byte_length_type, byte_length: 5 }
925 "#),
926 "byte_length_type"
927 ),
928 case::codepoint_length_constraint(
929 load(r#"
930 '12345'
931 "12345"
932 "1234😎"
933 "हैलो!"
934 "#),
935 load(r#"
936 null
937 null.bool
938 null.null
939 null.string
940 null.symbol
941 ""
942 "😎"
943 '1234'
944 "123456"
945 "#),
946 load_schema_from_text(r#" // For a schema with codepoint_length constraint as below:
947 type::{ name: codepoint_length_type, codepoint_length: 5 }
948 "#),
949 "codepoint_length_type"
950 ),
951 case::element_constraint(
952 load(r#"
953 []
954 [1]
955 [1, 2, 3]
956 ()
957 (1)
958 (1 2 3)
959 { a: 1, b: 2, c: 3 }
960 "#),
961 load(r#"
962 null.list
963 [1.]
964 [1e0]
965 [1, 2, null.int]
966 (1 2 3 true 4)
967 { a: 1, b: 2, c: true }
968 { a: 1, b: 2, c: null.int }
969 "#),
970 load_schema_from_text(r#" // For a schema with element constraint as below:
971 type::{ name: element_type, element: int }
972 "#),
973 "element_type"
974 ),
975 case::distinct_element_constraint(
976 load(r#"
977 []
978 [1]
979 [1, 2, 3]
980 ()
981 (1)
982 (1 2 3)
983 { a: 1, b: 2, c: 3 }
984 "#),
985 load(r#"
986 null.list
987 [1.]
988 [1e0]
989 [1, 1, 2, 3]
990 [a::1, b::1, a::2, a::2, 3]
991 [1, 2, null.int]
992 (1 2 3 true 4)
993 (1 1 2 2 3)
994 (a::1 b::1 a::2 a::2 3)
995 { a: 1, b: 2, c: true }
996 { a: 1, b: 1 }
997 { a: c::1, b: c::1 }
998 { a: 1, b: 2, c: null.int }
999 "#),
1000 load_schema_from_text(r#" // For a schema with distinct element constraint as below:
1001 $ion_schema_2_0
1002 type::{ name: distinct_element_type, element: distinct::int }
1003 "#),
1004 "distinct_element_type"
1005 ),
1006 case::element_with_self_ref_type_constraint(
1007 load(r#"
1008 5
1009 "hello"
1010 [1, 5]
1011 ["hi", "hello"]
1012 "#),
1013 load(r#"
1014 5.5
1015 null
1016 null.list
1017 null.int
1018 null.string
1019 (1 2 3)
1020 "#),
1021 load_schema_from_text(r#" // For a schema with element constraint with self referencing typeas below:
1022 type::{ name: my_type, one_of: [ int, string, { type: list, element: my_type } ] }
1023 "#),
1024 "my_type"
1025 ),
1026 case::fields_with_self_ref_type_constraint(
1027 load(r#"
1028 5
1029 "hello"
1030 { foo: "hi" }
1031 { foo: 5 }
1032 { foo: { foo: 5 } }
1033 "#),
1034 load(r#"
1035 5.5
1036 null
1037 null.struct
1038 { foo: bar }
1039 { foo: 5.5 }
1040 "#),
1041 load_schema_from_text(r#" // For a schema with fields constraint with self referencing typeas below:
1042 type::{ name: my_type, one_of: [ int, string, { type: struct, fields: { foo: my_type} } ] }
1043 "#),
1044 "my_type"
1045 ),
1046 case::annotations_constraint(
1047 load(r#"
1048 b::d::5
1049 a::b::d::5
1050 b::c::d::5
1051 a::b::c::d::5
1052 b::a::d::5 // 'a' is treated as open content
1053 c::b::d::5 // 'c' is treated as open content
1054 c::b::a::d::5 // 'a' and 'c' are treated as open content
1055 open_content::open_content::b::d::5
1056 b::d::3.5
1057 b::d::"hello"
1058 "#),
1059 load(r#"
1060 b::5
1061 d::5
1062 d::b::5
1063 5
1064 "#),
1065 load_schema_from_text(r#" // For a schema with annotations constraint as below:
1066 type::{ name: annotations_type, annotations: ordered::[a, required::b, c, required::d] }
1067 "#),
1068 "annotations_type"
1069 ),
1070 case::precision_constraint(
1071 load(r#"
1072 42.
1073 42d0
1074 42d-0
1075 4.2d1
1076 0.42d2
1077 "#),
1078 load(r#"
1079 null
1080 null.null
1081 null.decimal
1082 null.string
1083 4.
1084 42.0
1085 "#),
1086 load_schema_from_text(r#" // For a schema with precision constraint as below:
1087 type::{ name: precision_type, precision: 2 }
1088 "#),
1089 "precision_type"
1090 ),
1091 case::scale_constraint(
1092 load(r#"
1093 0.4
1094 0.42
1095 0.432
1096 0.4321
1097 43d3
1098 0d0
1099 "#),
1100 load(r#"
1101 null
1102 null.null
1103 null.decimal
1104 null.symbol
1105 0.43210
1106 "#),
1107 load_schema_from_text(r#" // For a schema with scale constraint as below:
1108 type::{ name: scale_type, scale: range::[min, 4] }
1109 "#),
1110 "scale_type"
1111 ),
1112 case::exponent_constraint(
1113 load(r#"
1114 0.4
1115 0.42
1116 0.432
1117 0.4321
1118 43d3
1119 0d0
1120 "#),
1121 load(r#"
1122 null
1123 null.null
1124 null.decimal
1125 null.symbol
1126 0.43210
1127 "#),
1128 load_schema_from_text(r#" // For a schema with exponent constraint as below:
1129 $ion_schema_2_0
1130 type::{ name: exponent_type, exponent: range::[-4, 4] }
1131 "#),
1132 "exponent_type"
1133 ),
1134 case::timestamp_precision_constraint(
1135 load(r#"
1136 2000-01T
1137 2000-01-01T
1138 2000-01-01T00:00Z
1139 2000-01-01T00:00:00Z
1140 "#),
1141 load(r#"
1142 2000T
1143 2000-01-01T00:00:00.0Z
1144 null
1145 null.timestamp
1146 null.symbol
1147 "#),
1148 load_schema_from_text(r#" // For a schema with timestamp precision constraint as below:
1149 type::{ name: timestamp_precision_type, timestamp_precision: range::[month, second] }
1150 "#),
1151 "timestamp_precision_type"
1152 ),
1153 case::utf8_byte_length_constraint(
1154 load(r#"
1155 "hello"
1156 hello
1157 world
1158 "world"
1159 '\u00A2\u20AC'
1160 "#),
1161 load(r#"
1162 null
1163 null.bool
1164 null.null
1165 null.string
1166 null.symbol
1167 ""
1168 "hi"
1169 hi
1170 "greetings"
1171 greetings
1172 '\u20AC\u20AC'
1173 "#),
1174 load_schema_from_text(r#" // For a schema with byte_length constraint as below:
1175 type::{ name: utf8_byte_length_type, utf8_byte_length: 5 }
1176 "#),
1177 "utf8_byte_length_type"
1178 ),
1179 case::valid_values_constraint(
1180 load(r#"
1181 2
1182 3
1183 5.5
1184 "hello"
1185 "#),
1186 load(r#"
1187 5.6
1188 1
1189 [1, 2 ,3]
1190 { greetings: "hello" }
1191 {{"hello"}}
1192 null
1193 "#),
1194 load_schema_from_text(r#" // For a schema with valid values constraint as below:
1195 type::{ name: valid_values_type, valid_values: [2, 3, 5.5, "hello"] }
1196 "#),
1197 "valid_values_type"
1198 ),
1199 case::valid_values_with_range_constraint(
1200 load(r#"
1201 1
1202 2
1203 3
1204 4
1205 5
1206 4.5
1207 2d0
1208 30e-1
1209 "#),
1210 load(r#"
1211 0
1212 -2
1213 5.6
1214 6
1215 null
1216 "#),
1217 load_schema_from_text(r#" // For a schema with valid values constraint as below:
1218 type::{ name: valid_values_type, valid_values: range::[1, 5.5] }
1219 "#),
1220 "valid_values_type"
1221 ),
1222 case::regex_constraint(
1223 load(r#"
1224 "ab"
1225 "cd"
1226 "ef"
1227 "#),
1228 load(r#"
1229 "a"
1230 "ac"
1231 "ace"
1232 "bdf"
1233 "#),
1234 load_schema_from_text(r#" // For a schema with regex constraint as below:
1235 type::{ name: regex_type, regex: "ab|cd|ef" }
1236 "#),
1237 "regex_type"
1238 ),
1239 case::regex_v2_0_constraint(
1240 load(r#"
1241 "/"
1242 ":"
1243 "@"
1244 "["
1245 "`"
1246 "{"
1247 " "
1248 "#),
1249 load(r#"
1250 "a"
1251 "A"
1252 "z"
1253 "Z"
1254 "0"
1255 "9"
1256 "_"
1257 "#),
1258 load_schema_from_text(r#" // For a schema with regex constraint as below:
1259 $ion_schema_2_0
1260 type::{ name: regex_type, regex: "\\W" }
1261 "#),
1262 "regex_type"
1263 ),
1264 case::timestamp_offset_constraint(
1265 load(r#"
1266 2000T
1267 2000-01-01T00:00:00-00:00 // unknown local offset
1268 2000-01-01T00:00:00Z // UTC
1269 2000-01-01T00:00:00+00:00 // UTC
1270 2000-01-01T00:00:00+01:00
1271 2000-01-01T00:00:00-01:01
1272 "#),
1273 load(r#"
1274 2000-01-01T00:00:00-01:00
1275 2000-01-01T00:00:00+01:01
1276 2000-01-01T00:00:00+07:00
1277 "#),
1278 load_schema_from_text(r#" // For a schema with timestamp_offset constraint as below:
1279 type::{ name: timestamp_offset_type, timestamp_offset: ["-00:00", "+00:00", "+01:00", "-01:01"] }
1280 "#),
1281 "timestamp_offset_type"
1282 ),
1283 case::ieee754_float_constraint(
1284 load(r#"
1285 1e0
1286 -1e0
1287 65504e0
1288 -65504e0
1289 nan
1290 +inf
1291 -inf
1292 "#),
1293 load(r#"
1294 null.float
1295 5
1296 5d0
1297 (5e0)
1298 [5e0]
1299 65505e0
1300 -65505e0
1301 "#),
1302 load_schema_from_text(r#" // For a schema with timestamp precision constraint as below:
1303 $ion_schema_2_0
1304 type::{ name: ieee754_float_type, ieee754_float: binary16 }
1305 "#),
1306 "ieee754_float_type"
1307 ),
1308 case::annotations_constraint_with_standard_syntax(
1309 load(r#"
1310 a::0
1311 b::1
1312 c::2
1313 "#),
1314 load(r#"
1315 0
1316 $a::1
1317 _c::2
1318 ''::3
1319 "#),
1320 load_schema_from_text(r#" // For a schema with annotations constraint as below:
1321 $ion_schema_2_0
1322 type::{ name: standard_annotations_type, annotations: { element: { regex: "^[a-z]$" }, container_length: 1 } }
1323 "#),
1324 "standard_annotations_type"
1325 )
1326 )]
1327 fn type_validation(
1328 valid_values: Vec<Element>,
1329 invalid_values: Vec<Element>,
1330 schema: Arc<Schema>,
1331 type_name: &str,
1332 ) {
1333 let type_ref: TypeDefinition = schema.get_built_in_or_defined_type(type_name).unwrap();
1334 for valid_value in valid_values.iter() {
1336 let validation_result = type_ref.validate(valid_value);
1338 validation_result.unwrap();
1339 }
1340 for invalid_value in invalid_values.iter() {
1342 let validation_result = type_ref.validate(invalid_value);
1344 assert!(validation_result.is_err());
1345 }
1346 }
1347}