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