1use crate::authority::DocumentAuthority;
23use crate::ion_schema_element::IonSchemaElementType;
24use crate::isl::isl_import::{IslImport, IslImportType};
25use crate::isl::isl_type::IslType;
26use crate::isl::{IslSchema, IslVersion};
27use crate::result::{
28 invalid_schema_error, invalid_schema_error_raw, unresolvable_schema_error,
29 unresolvable_schema_error_raw, IonSchemaError, IonSchemaResult,
30};
31use crate::schema::Schema;
32use crate::types::{BuiltInTypeDefinition, Nullability, TypeDefinitionImpl, TypeDefinitionKind};
33use crate::{is_isl_version_marker, is_reserved_word, UserReservedFields};
34use ion_rs::IonType;
35use ion_rs::{Annotations, Element};
36use std::collections::{HashMap, HashSet};
37use std::io::ErrorKind;
38use std::sync::Arc;
39
40#[derive(Debug, Clone, Default)]
75pub struct PendingTypes {
76 builtin_type_ids_by_name: HashMap<String, TypeId>,
77 ids_by_name: HashMap<String, TypeId>,
78 parent: Option<(String, TypeId)>,
79 types_by_id: Vec<Option<TypeDefinitionKind>>, }
81
82impl PendingTypes {
83 pub fn update_type_store(
98 &mut self,
99 type_store: &mut TypeStore,
100 load_isl_import: Option<&IslImport>,
101 isl_type_names: &HashSet<&str>,
102 ) -> IonSchemaResult<bool> {
103 if let Some(import) = load_isl_import {
105 match import {
106 IslImport::Schema(_) => {
107 self.update_type_store_with_all_isl_imported_types(
108 None,
109 type_store,
110 isl_type_names,
111 )?;
112 }
113 IslImport::Type(isl_import) => {
114 if let Some(named_type_def) =
116 self.get_type_by_name_for_import(isl_import.type_name(), type_store)
117 {
118 type_store
119 .add_isl_imported_type(isl_import.alias().as_ref(), named_type_def?);
120 self.update_type_store_with_all_isl_imported_types(
121 Some(isl_import.type_name()),
122 type_store,
123 isl_type_names,
124 )?;
125 } else {
126 return Ok(false);
130 }
131 }
132 IslImport::TypeAlias(isl_import) => {
133 if type_store
135 .imported_type_ids_by_name
136 .contains_key(isl_import.alias().as_ref().unwrap())
137 {
138 return Ok(true);
140 } else if let Some(named_type_def) =
141 self.get_type_by_name_for_import(isl_import.type_name(), type_store)
142 {
143 let aliased_type_def = named_type_def?
144 .with_name(isl_import.alias().as_ref().unwrap().to_owned());
145 type_store
146 .add_isl_imported_type(isl_import.alias().as_ref(), aliased_type_def);
147 self.update_type_store_with_all_isl_imported_types(
148 Some(isl_import.type_name()),
149 type_store,
150 isl_type_names,
151 )?;
152 } else {
153 return Ok(false);
157 }
158 }
159 }
160 } else {
161 self.update_type_store_with_all_types(type_store, isl_type_names)?;
163 }
164 self.types_by_id.clear();
165 self.ids_by_name.clear();
166 Ok(true)
167 }
168
169 fn get_type_by_name_for_import(
172 &self,
173 import_type_name: &str,
174 type_store: &mut TypeStore,
175 ) -> Option<IonSchemaResult<TypeDefinitionImpl>> {
176 return match self.ids_by_name.get(import_type_name) {
177 Some(id) => self.types_by_id[*id]
178 .to_owned()
179 .map(|type_def| match type_def {
180 TypeDefinitionKind::Named(named_type_def) => Ok(named_type_def),
181 TypeDefinitionKind::Anonymous(_) => {
182 unreachable!(
183 "The TypeDefinition for the imported type '{}' was Anonymous.",
184 import_type_name
185 )
186 }
187 TypeDefinitionKind::BuiltIn(_) => {
188 unreachable!(
189 "The TypeDefinition for the imported type '{}' was a builtin type.",
190 import_type_name
191 )
192 }
193 }),
194 None => {
195 match type_store.get_defined_type_id_or_imported_type_id_by_name(import_type_name) {
196 Some(id) => match type_store.types_by_id[*id].to_owned() {
197 TypeDefinitionKind::Named(named_type_def) => Some(Ok(named_type_def)),
198 TypeDefinitionKind::Anonymous(_) => {
199 unreachable!(
200 "The TypeDefinition for the imported type '{}' was Anonymous.",
201 import_type_name
202 )
203 }
204 TypeDefinitionKind::BuiltIn(_) => {
205 unreachable!(
206 "The TypeDefinition for the imported type '{}' was a builtin type.",
207 import_type_name
208 )
209 }
210 },
211 None => None,
212 }
213 }
214 };
215 }
216
217 fn update_type_store_with_all_types(
219 &self,
220 type_store: &mut TypeStore,
221 isl_type_names: &HashSet<&str>,
222 ) -> IonSchemaResult<()> {
223 for optional_type in &self.types_by_id {
224 let type_def = optional_type.to_owned().ok_or_else(|| {
226 unresolvable_schema_error_raw("Unable to load schema due to unresolvable type")
227 })?;
228
229 match type_def {
230 TypeDefinitionKind::Named(named_type_def) => {
231 if named_type_def.is_deferred_type_def()
234 && !isl_type_names
235 .contains(named_type_def.name().as_ref().unwrap().as_str())
236 {
237 return unresolvable_schema_error(format!(
238 "Unable to load schema due to unresolvable type {}",
239 named_type_def.name().as_ref().unwrap()
240 ));
241 }
242 type_store.add_named_type(named_type_def)
243 }
244 TypeDefinitionKind::Anonymous(anonymous_type_def) => {
245 type_store.add_anonymous_type(anonymous_type_def)
246 }
247 TypeDefinitionKind::BuiltIn(builtin_type) => {
248 type_store.add_builtin_type(&builtin_type)
249 }
250 };
251 }
252 Ok(())
253 }
254
255 fn update_type_store_with_all_isl_imported_types(
261 &self,
262 isl_imported_type_name: Option<&str>,
263 type_store: &mut TypeStore,
264 isl_type_names: &HashSet<&str>,
265 ) -> IonSchemaResult<()> {
266 for optional_type in &self.types_by_id {
267 let type_def = optional_type.to_owned().ok_or_else(|| {
269 unresolvable_schema_error_raw("Unable to load schema due to unresolvable type")
270 })?;
271
272 match type_def.to_owned() {
273 TypeDefinitionKind::Named(named_type_def) => {
274 match isl_imported_type_name {
275 None => {
276 if named_type_def.is_deferred_type_def()
279 && !isl_type_names
280 .contains(named_type_def.name().as_ref().unwrap().as_str())
281 {
282 return unresolvable_schema_error(format!(
283 "Unable to load schema due to unresolvable type {}",
284 named_type_def.name().as_ref().unwrap()
285 ));
286 }
287 type_store.add_isl_imported_type(None, named_type_def);
289 }
290 Some(import_type_name) => {
291 if named_type_def.is_deferred_type_def()
294 && !isl_type_names
295 .contains(named_type_def.name().as_ref().unwrap().as_str())
296 {
297 return unresolvable_schema_error(format!(
298 "Unable to load schema due to unresolvable type {}",
299 named_type_def.name().as_ref().unwrap()
300 ));
301 }
302 if named_type_def.name().as_ref().unwrap().eq(import_type_name) {
304 continue;
305 }
306 type_store
308 .types_by_id
309 .push(TypeDefinitionKind::Named(named_type_def));
310 }
311 }
312 }
313 TypeDefinitionKind::Anonymous(anonymous_type_def) => {
314 type_store.add_anonymous_type(anonymous_type_def);
315 }
316 TypeDefinitionKind::BuiltIn(builtin_type) => {
317 type_store.add_builtin_type(&builtin_type);
318 }
319 };
320 }
321 Ok(())
322 }
323
324 pub(crate) fn get_total_types(&self, type_store: &mut TypeStore) -> usize {
326 self.types_by_id.len() + type_store.types_by_id.len()
327 }
328
329 pub(crate) fn get_type_id_by_name(
332 &self,
333 name: &str,
334 type_store: &mut TypeStore,
335 ) -> Option<TypeId> {
336 match self.ids_by_name.get(name) {
337 Some(id) => Some(*id + type_store.types_by_id.len()),
338 None => type_store
339 .get_defined_type_id_or_imported_type_id_by_name(name)
340 .copied(),
341 }
342 }
343
344 pub(crate) fn update_named_type(
347 &mut self,
348 type_id: TypeId,
349 name: &str,
350 type_def: TypeDefinitionImpl,
351 type_store: &mut TypeStore,
352 ) -> TypeId {
353 if let Some(exists) = self.ids_by_name.get(name) {
354 return exists.to_owned() + type_store.types_by_id.len();
355 }
356
357 if let Some(exists) = type_store.imported_type_ids_by_name.get(name) {
358 return exists.to_owned();
359 }
360
361 match type_store.update_deferred_type_def(type_def.to_owned(), name) {
362 None => {
363 let type_id = type_id - type_store.types_by_id.len();
364 self.ids_by_name.insert(name.to_owned(), type_id);
365 self.types_by_id[type_id] = Some(TypeDefinitionKind::Named(type_def));
366 type_id + type_store.types_by_id.len()
367 }
368 Some(exists) => exists,
369 }
370 }
371
372 pub(crate) fn update_anonymous_type(
375 &mut self,
376 type_id: TypeId,
377 type_def: TypeDefinitionImpl,
378 type_store: &mut TypeStore,
379 ) -> TypeId {
380 self.types_by_id[type_id - type_store.types_by_id.len()] =
381 Some(TypeDefinitionKind::Anonymous(type_def));
382 type_id
383 }
384
385 pub(crate) fn add_parent(&mut self, name: String, type_store: &mut TypeStore) {
387 self.parent = Some((name, self.types_by_id.len() + type_store.types_by_id.len()))
392 }
393
394 pub(crate) fn get_parent(&self) -> &Option<(String, TypeId)> {
396 &self.parent
397 }
398
399 pub(crate) fn clear_parent(&mut self) {
401 self.parent = None
402 }
403
404 pub(crate) fn add_type(
406 &mut self,
407 type_store: &mut TypeStore,
408 type_name: Option<String>,
409 ) -> TypeId {
410 if let Some(name) = type_name {
411 if let Some(exists) = self.ids_by_name.get(&name) {
412 return exists.to_owned() + type_store.types_by_id.len();
413 }
414 if let Some(exists) = type_store.get_defined_type_id_or_imported_type_id_by_name(&name)
415 {
416 return exists.to_owned();
417 }
418 }
419 let type_id = self.types_by_id.len();
420 self.types_by_id.push(None);
421 type_id + type_store.types_by_id.len()
422 }
423
424 pub(crate) fn add_deferred_type_with_name(
426 &mut self,
427 alias: &str,
428 type_store: &mut TypeStore,
429 ) -> TypeId {
430 let type_id = self.types_by_id.len();
431 self.ids_by_name.insert(alias.to_owned(), type_id);
432 self.types_by_id.push(Some(TypeDefinitionKind::Named(
433 TypeDefinitionImpl::new_deferred_type_def(alias.to_owned()),
434 )));
435 type_id + type_store.types_by_id.len()
436 }
437}
438
439static DERIVED_ISL_TYPES: [&str; 9] = [
442 "type::{ name: lob, one_of: [ blob, clob ] }",
443 "type::{ name: number, one_of: [ decimal, float, int ] }",
444 "type::{ name: text, one_of: [ string, symbol ] }",
445 "type::{ name: $lob, one_of: [ $blob, $clob ] }",
446 "type::{ name: $number, one_of: [ $decimal, $float, $int ] }",
447 "type::{ name: $text, one_of: [ $string, $symbol ] }",
448 "type::{ name: $any, one_of: [ $blob, $bool, $clob, $decimal,
449 $float, $int, $string, $symbol, $timestamp,
450 $list, $sexp, $struct, $null, document ] }",
451 "type::{ name: nothing, not: $any }",
452 "type::{ name: any, one_of: [ blob, bool, clob, decimal,
453 float, int, string, symbol, timestamp,
454 list, sexp, struct, document ] }",
455];
456
457pub type TypeId = usize;
458
459#[derive(Debug, Clone)]
461pub struct TypeStore {
462 builtin_type_ids_by_name: HashMap<String, TypeId>, imported_type_ids_by_name: HashMap<String, TypeId>, ids_by_name: HashMap<String, TypeId>, types_by_id: Vec<TypeDefinitionKind>,
466}
467
468impl Default for TypeStore {
469 fn default() -> Self {
470 let mut type_store = Self {
471 builtin_type_ids_by_name: HashMap::new(),
472 imported_type_ids_by_name: HashMap::new(),
473 ids_by_name: HashMap::new(),
474 types_by_id: Vec::new(),
475 };
476 type_store
477 .preload()
478 .expect("The type store didn't preload with built-in types correctly");
479 type_store
480 }
481}
482
483impl TypeStore {
484 pub(crate) fn preload(&mut self) -> IonSchemaResult<()> {
488 let isl_version = IslVersion::V1_0;
489 use IonSchemaElementType::*;
492 let built_in_atomic_types: [IonSchemaElementType; 12] = [
493 Int, Float, Decimal, Timestamp, String, Symbol, Bool, Blob, Clob, SExp, List, Struct,
494 ];
495 for atomic_type in built_in_atomic_types {
497 self.add_builtin_type(&BuiltInTypeDefinition::Atomic(
498 atomic_type.to_owned(),
499 Nullability::NotNullable,
500 ));
501 }
502
503 for atomic_type in built_in_atomic_types {
505 self.add_builtin_type(&BuiltInTypeDefinition::Atomic(
506 atomic_type.to_owned(),
507 Nullability::Nullable,
508 ));
509 }
510
511 self.add_builtin_type(&BuiltInTypeDefinition::Atomic(Null, Nullability::Nullable));
513
514 self.add_builtin_type(&BuiltInTypeDefinition::Atomic(
516 Document,
517 Nullability::NotNullable,
518 ));
519
520 let pending_types = &mut PendingTypes::default();
522 for text in DERIVED_ISL_TYPES {
523 let isl_type = IslType::from_owned_element(
524 isl_version,
525 &Element::read_one(text.as_bytes()).expect("parsing failed unexpectedly"),
526 &mut vec![],
527 )
528 .unwrap();
529 let type_def = BuiltInTypeDefinition::parse_from_isl_type(
530 isl_version,
531 &isl_type,
532 self,
533 pending_types,
534 )?;
535 self.add_builtin_type(&type_def);
536 }
537 Ok(())
538 }
539
540 pub(crate) fn get_types(&self) -> Vec<TypeId> {
542 self.ids_by_name.values().cloned().collect()
543 }
544
545 pub(crate) fn get_imports(&self) -> Vec<TypeId> {
547 self.imported_type_ids_by_name.values().cloned().collect()
548 }
549
550 pub(crate) fn get_imported_type_id_by_name(&self, name: &str) -> Option<&TypeId> {
553 self.imported_type_ids_by_name.get(name)
554 }
555
556 pub(crate) fn get_type_by_name(&self, name: &str) -> Option<&TypeDefinitionKind> {
559 self.ids_by_name
560 .get(name)
561 .and_then(|id| self.types_by_id.get(*id))
562 .or_else(|| {
563 self.imported_type_ids_by_name
564 .get(name)
565 .and_then(|id| self.types_by_id.get(*id))
566 })
567 }
568
569 pub(crate) fn get_built_in_type_id_or_defined_type_id_by_name(
572 &self,
573 name: &str,
574 ) -> Option<&TypeId> {
575 self.ids_by_name
576 .get(name)
577 .or_else(|| self.builtin_type_ids_by_name.get(name))
578 }
579
580 pub(crate) fn get_defined_type_id_or_imported_type_id_by_name(
583 &self,
584 name: &str,
585 ) -> Option<&TypeId> {
586 self.ids_by_name
587 .get(name)
588 .or_else(|| self.imported_type_ids_by_name.get(name))
589 }
590
591 pub(crate) fn get_type_id_by_name(&self, name: &str) -> Option<&TypeId> {
594 self.ids_by_name
595 .get(name)
596 .or_else(|| self.imported_type_ids_by_name.get(name))
597 .or_else(|| self.builtin_type_ids_by_name.get(name))
598 }
599
600 pub(crate) fn get_type_def_by_name(&self, name: &str) -> Option<&TypeDefinitionKind> {
603 self.ids_by_name
604 .get(name)
605 .and_then(|id| self.types_by_id.get(*id))
606 }
607
608 pub(crate) fn get_builtin_type_id(&self, type_name: &str) -> Option<TypeId> {
611 self.builtin_type_ids_by_name
612 .get(type_name)
613 .map(|t| t.to_owned())
614 }
615
616 pub(crate) fn get_type_by_id(&self, id: TypeId) -> Option<&TypeDefinitionKind> {
619 self.types_by_id.get(id)
620 }
621
622 pub(crate) fn add_named_type(&mut self, type_def: TypeDefinitionImpl) -> TypeId {
625 let name = type_def.name().as_ref().unwrap();
626 if let Some(exists) = self.ids_by_name.get(name) {
627 return exists.to_owned();
628 }
629 let type_id = self.types_by_id.len();
630 self.ids_by_name.insert(name.to_owned(), type_id);
631 self.types_by_id.push(TypeDefinitionKind::Named(type_def));
632 type_id
633 }
634
635 pub(crate) fn update_deferred_type_def(
639 &mut self,
640 type_def: TypeDefinitionImpl,
641 name: &str,
642 ) -> Option<TypeId> {
643 if let Some(exists) = self.ids_by_name.get(name) {
644 if let Some(TypeDefinitionKind::Named(existing_type_def)) = self.get_type_by_id(*exists)
645 {
646 if existing_type_def.is_deferred_type_def() {
649 self.types_by_id[*exists] = TypeDefinitionKind::Named(type_def);
650 }
651 }
652 return Some(*exists);
653 }
654 None
655 }
656
657 pub(crate) fn add_builtin_type(
660 &mut self,
661 builtin_type_definition: &BuiltInTypeDefinition,
662 ) -> TypeId {
663 let builtin_type_name = match builtin_type_definition {
664 BuiltInTypeDefinition::Atomic(ion_type, is_nullable) => match is_nullable {
665 Nullability::Nullable => format!("${ion_type}"),
666 Nullability::NotNullable => format!("{ion_type}"),
667 },
668 BuiltInTypeDefinition::Derived(other_type) => other_type.name().to_owned().unwrap(),
669 };
670
671 if let Some(exists) = self.builtin_type_ids_by_name.get(&builtin_type_name) {
672 return exists.to_owned();
673 }
674 let type_id = self.types_by_id.len();
675 self.builtin_type_ids_by_name
676 .insert(builtin_type_name, type_id);
677 self.types_by_id.push(TypeDefinitionKind::BuiltIn(
678 builtin_type_definition.to_owned(),
679 ));
680 type_id
681 }
682
683 pub(crate) fn add_isl_imported_type(
686 &mut self,
687 alias: Option<&String>,
688 type_def: TypeDefinitionImpl,
689 ) -> TypeId {
690 let name = match alias {
691 None => type_def.name().as_ref().unwrap(),
692 Some(name) => name,
693 };
694
695 if let Some(exists) = self.imported_type_ids_by_name.get(name) {
696 return exists.to_owned();
697 }
698 let type_id = self.types_by_id.len();
699 self.imported_type_ids_by_name
700 .insert(name.to_owned(), type_id);
701 self.types_by_id.push(TypeDefinitionKind::Named(type_def));
702 type_id
703 }
704
705 pub(crate) fn add_anonymous_type(&mut self, type_def: TypeDefinitionImpl) -> TypeId {
707 let type_id = self.types_by_id.len();
708 self.types_by_id
709 .push(TypeDefinitionKind::Anonymous(type_def));
710 type_id
711 }
712}
713
714pub struct Resolver {
716 authorities: Vec<Box<dyn DocumentAuthority>>,
717 resolved_schema_cache: HashMap<String, Arc<Schema>>,
718}
719
720impl Resolver {
721 pub fn new(authorities: Vec<Box<dyn DocumentAuthority>>) -> Self {
722 Self {
723 authorities,
724 resolved_schema_cache: HashMap::new(),
725 }
726 }
727
728 pub fn schema_from_isl_types<A: AsRef<str>, B: Into<Vec<IslType>>>(
729 &self,
730 isl_version: IslVersion,
731 id: A,
732 isl_types: B,
733 ) -> IonSchemaResult<Schema> {
734 let isl_types = isl_types.into();
735 let mut type_store = TypeStore::default();
737 let pending_types = &mut PendingTypes::default();
738
739 let isl_type_names: HashSet<&str> =
742 HashSet::from_iter(isl_types.iter().filter_map(|t| t.name()));
743
744 for isl_type in &isl_types {
745 match &isl_type.name() {
747 Some(isl_type_name) => {
748 let has_other_isl_constraints = isl_type
750 .constraints()
751 .iter()
752 .any(|c| c.version != isl_version);
753
754 if has_other_isl_constraints {
755 return invalid_schema_error(format!("ISL type: {isl_type_name} contains constraints from another ISL version. Only use {isl_version} constraints for this method."));
756 }
757
758 TypeDefinitionImpl::parse_from_isl_type_and_update_pending_types(
759 isl_version,
760 isl_type,
761 &mut type_store,
762 pending_types,
763 )?
764 }
765 None => {
766 return invalid_schema_error("Top level types must be named type definitions");
768 }
769 };
770 }
771
772 pending_types.update_type_store(&mut type_store, None, &isl_type_names)?;
774 Ok(Schema::new(id, Arc::new(type_store)))
775 }
776
777 pub fn isl_schema_from_elements<I: Iterator<Item = Element>>(
779 &mut self,
780 elements: I,
781 id: &str,
782 ) -> IonSchemaResult<IslSchema> {
783 let mut isl_imports: Vec<IslImport> = vec![];
785 let mut isl_types: Vec<IslType> = vec![];
786 let mut isl_inline_imports: Vec<IslImportType> = vec![];
787 let mut open_content = vec![];
788 let mut isl_user_reserved_fields = UserReservedFields::default();
789 let mut isl_version = IslVersion::V1_0;
790
791 let mut found_header = false;
792 let mut found_footer = false;
793 let mut found_type_definition = false;
794 let mut found_isl_version_marker = false;
795
796 for value in elements {
797 let annotations: &Annotations = value.annotations();
798
799 if !found_isl_version_marker
801 && value.ion_type() == IonType::Symbol
802 && is_isl_version_marker(value.as_text().unwrap())
803 {
804 isl_version = match value.as_text().unwrap() {
806 "$ion_schema_1_0" => IslVersion::V1_0,
807 "$ion_schema_2_0" => IslVersion::V2_0,
808 _ => {
809 return invalid_schema_error(format!(
810 "Unsupported Ion Schema Language version: {value}"
811 ));
812 }
813 };
814 found_isl_version_marker = true;
815 } else if annotations.contains("schema_header") {
816 if isl_version == IslVersion::V2_0 {
817 if found_type_definition {
818 return invalid_schema_error(
819 "The schema header must come before top level type definitions",
820 );
821 }
822
823 if found_header {
824 return invalid_schema_error(
825 "Schema must only contain a single schema header",
826 );
827 }
828
829 if annotations.len() > 1 {
830 return invalid_schema_error(
831 "schema header must not have any other annotations then `schema_header`",
832 );
833 }
834 }
835
836 found_header = true;
837
838 if !found_isl_version_marker {
841 found_isl_version_marker = true;
842 }
843
844 let schema_header = try_to!(value.as_struct());
845 if let Some(imports) = schema_header.get("imports").and_then(|it| it.as_sequence())
846 {
847 for import in imports.elements() {
848 let isl_import = IslImport::from_ion_element(import)?;
849 isl_imports.push(isl_import);
850 }
851 }
852 if isl_version == IslVersion::V2_0 {
853 if let Some(user_reserved_fields_element) =
854 schema_header.get("user_reserved_fields")
855 {
856 if !user_reserved_fields_element.annotations().is_empty() {
857 return invalid_schema_error(
858 "User reserved field must be an unannotated struct",
859 )?;
860 }
861 let user_reserved_fields_struct = user_reserved_fields_element
862 .as_struct()
863 .ok_or(invalid_schema_error_raw(
864 "User reserved field must be a non-null struct",
865 ))?;
866
867 isl_user_reserved_fields =
868 UserReservedFields::from_ion_elements(user_reserved_fields_struct)?;
869 }
870 isl_user_reserved_fields.validate_field_names_in_header(schema_header)?;
871 }
872 }
873 else if annotations.contains("type") {
875 found_type_definition = true;
876
877 if isl_version == IslVersion::V2_0 && annotations.len() > 1 {
878 return invalid_schema_error(
879 "Top level types definitions must not have any other annotations then `type`",
880 );
881 }
882 if !found_isl_version_marker {
885 found_isl_version_marker = true;
886 }
887
888 let isl_type: IslType =
890 IslType::from_owned_element(isl_version, &value, &mut isl_inline_imports)?;
891 if isl_type.name().is_none() {
892 return invalid_schema_error(
894 "Top level types must contain field `name` in their definition",
895 );
896 }
897
898 if isl_version == IslVersion::V2_0 {
899 isl_user_reserved_fields.validate_field_names_in_type(&isl_type)?;
900 }
901
902 if value.as_struct().unwrap().get("occurs").is_some() {
904 return invalid_schema_error(
905 "Top level types must not contain `occurs` field in their definition",
906 );
907 }
908
909 isl_types.push(isl_type);
910 }
911 else if annotations.contains("schema_footer") {
913 found_footer = true;
914 if isl_version == IslVersion::V2_0 {
915 if annotations.len() > 1 {
916 return invalid_schema_error(
917 "schema footer must not have any other annotations then `schema_footer`",
918 );
919 }
920 let schema_footer = try_to!(value.as_struct());
921 isl_user_reserved_fields.validate_field_names_in_footer(schema_footer)?;
922 }
923 } else {
924 if value.ion_type() == IonType::Symbol
926 && !value.is_null()
927 && is_isl_version_marker(value.as_text().unwrap())
928 {
929 return invalid_schema_error(
930 "top level open content can not be an Ion Schema version marker",
931 );
932 }
933
934 if isl_version == IslVersion::V2_0
935 && value
936 .annotations()
937 .iter()
938 .any(|a| is_reserved_word(a.text().unwrap()))
939 {
940 return invalid_schema_error(
941 "top level open content may not be annotated with any reserved keyword",
942 );
943 }
944
945 open_content.push(value);
946 continue;
947 }
948 }
949
950 if found_footer ^ found_header {
951 return invalid_schema_error("For any schema while a header and footer are both optional, a footer is required if a header is present (and vice-versa).");
952 }
953
954 match isl_version {
955 IslVersion::V1_0 => Ok(IslSchema::schema_v_1_0(
956 id,
957 isl_imports,
958 isl_types,
959 isl_inline_imports,
960 open_content,
961 )),
962 IslVersion::V2_0 => Ok(IslSchema::schema_v_2_0(
963 id,
964 isl_user_reserved_fields,
965 isl_imports,
966 isl_types,
967 isl_inline_imports,
968 open_content,
969 )),
970 }
971 }
972
973 pub fn schema_from_isl_schema(
975 &mut self,
976 isl_version: IslVersion,
977 isl: IslSchema,
978 type_store: &mut TypeStore,
979 load_isl_import: Option<&IslImport>,
980 ) -> IonSchemaResult<Arc<Schema>> {
981 let mut added_imported_type_to_type_store = false;
985
986 if isl_version != isl.version() {
987 return invalid_schema_error(format!(
988 "Expected {isl_version} schema but found {}",
989 isl.version()
990 ));
991 }
992
993 let isl_types = isl.types();
994
995 for isl_inline_imported_type in isl.inline_imported_types() {
998 let import_id = isl_inline_imported_type.id();
999 let inline_imported_type = self.load_schema(
1000 import_id,
1001 type_store,
1002 Some(&IslImport::Type(isl_inline_imported_type.to_owned())),
1003 )?;
1004 }
1005
1006 for isl_import in isl.imports() {
1008 let import_id = isl_import.id();
1009 let imported_schema = self.load_schema(import_id, type_store, Some(isl_import))?;
1010 }
1011
1012 let isl_type_names: HashSet<&str> =
1015 HashSet::from_iter(isl.types().filter_map(|t| t.name()));
1016
1017 for isl_type in isl_types {
1019 let pending_types = &mut PendingTypes::default();
1020
1021 if let Some(isl_type_name) = &isl_type.name() {
1022 let has_other_isl_constraints = isl_type
1024 .constraints()
1025 .iter()
1026 .any(|c| c.version != isl_version);
1027
1028 if has_other_isl_constraints {
1029 return invalid_schema_error(format!("ISL type: {isl_type_name} contains constraints from other ISL version. Only use {isl_version} constraints for this method."));
1030 }
1031
1032 let type_id: TypeId =
1034 TypeDefinitionImpl::parse_from_isl_type_and_update_pending_types(
1035 isl_version,
1036 isl_type,
1037 type_store,
1038 pending_types,
1039 )?;
1040 }
1041
1042 added_imported_type_to_type_store =
1044 pending_types.update_type_store(type_store, load_isl_import, &isl_type_names)?;
1045 }
1046
1047 if load_isl_import.is_some() && !added_imported_type_to_type_store {
1050 unreachable!(
1051 "Unable to load import: {} as the type/types were not added to the type_store correctly",
1052 isl.id()
1053 );
1054 }
1055
1056 let schema = Arc::new(Schema::new(isl.id(), Arc::new(type_store.clone())));
1057
1058 match load_isl_import {
1062 None => {
1063 self.resolved_schema_cache
1064 .insert(isl.id(), Arc::clone(&schema));
1065 }
1066 Some(IslImport::Schema(_)) => {
1067 self.resolved_schema_cache
1068 .insert(isl.id(), Arc::clone(&schema));
1069 }
1070 _ => {
1071 }
1073 }
1074
1075 Ok(schema)
1076 }
1077
1078 fn load_schema<A: AsRef<str>>(
1085 &mut self,
1086 id: A,
1087 type_store: &mut TypeStore,
1088 load_isl_import: Option<&IslImport>,
1089 ) -> IonSchemaResult<Arc<Schema>> {
1090 let id: &str = id.as_ref();
1091
1092 if let Some(schema) = self.resolved_schema_cache.get(id) {
1093 return Ok(Arc::clone(schema));
1094 }
1095
1096 for authority in &self.authorities {
1097 return match authority.elements(id) {
1098 Err(error) => match error {
1099 IonSchemaError::IoError { source } => match source.kind() {
1100 ErrorKind::NotFound => continue,
1101 _ => Err(IonSchemaError::IoError { source }),
1102 },
1103 _ => Err(error),
1104 },
1105 Ok(schema_content) => {
1106 let isl = self.isl_schema_from_elements(schema_content.into_iter(), id)?;
1107 self.schema_from_isl_schema(isl.version(), isl, type_store, load_isl_import)
1108 }
1109 };
1110 }
1111 unresolvable_schema_error("Unable to load schema: ".to_owned() + id)
1112 }
1113
1114 fn load_isl_schema<A: AsRef<str>>(
1121 &mut self,
1122 id: A,
1123 load_isl_import: Option<&IslImport>,
1124 ) -> IonSchemaResult<IslSchema> {
1125 let id: &str = id.as_ref();
1126
1127 for authority in &self.authorities {
1128 return match authority.elements(id) {
1129 Ok(schema_content) => self.isl_schema_from_elements(schema_content.into_iter(), id),
1130 Err(IonSchemaError::IoError { source: e }) if e.kind() == ErrorKind::NotFound => {
1131 continue;
1132 }
1133 Err(error) => Err(error),
1134 };
1135 }
1136 unresolvable_schema_error("Unable to load ISL model: ".to_owned() + id)
1137 }
1138}
1139
1140pub struct SchemaSystem {
1147 resolver: Resolver,
1148}
1149
1150impl SchemaSystem {
1152 pub fn new(authorities: Vec<Box<dyn DocumentAuthority>>) -> Self {
1153 Self {
1154 resolver: Resolver::new(authorities),
1155 }
1156 }
1157
1158 pub fn load_schema<A: AsRef<str>>(&mut self, id: A) -> IonSchemaResult<Arc<Schema>> {
1165 self.resolver
1166 .load_schema(id, &mut TypeStore::default(), None)
1167 }
1168
1169 pub fn new_schema(&mut self, schema_content: &[u8], id: &str) -> IonSchemaResult<Arc<Schema>> {
1171 let elements = Element::read_all(schema_content)?;
1172 let isl = self
1173 .resolver
1174 .isl_schema_from_elements(elements.into_iter(), id)?;
1175 self.resolver
1176 .schema_from_isl_schema(isl.version(), isl, &mut TypeStore::default(), None)
1177 }
1178
1179 pub fn load_isl_schema<A: AsRef<str>>(&mut self, id: A) -> IonSchemaResult<IslSchema> {
1183 self.resolver.load_isl_schema(id, None)
1184 }
1185
1186 pub fn new_isl_schema(
1188 &mut self,
1189 schema_content: &[u8],
1190 id: &str,
1191 ) -> IonSchemaResult<IslSchema> {
1192 let elements = Element::read_all(schema_content)?;
1193 self.resolver
1194 .isl_schema_from_elements(elements.into_iter(), id)
1195 }
1196
1197 pub fn load_schema_from_isl_schema_v1_0(
1203 &mut self,
1204 isl: IslSchema,
1205 ) -> IonSchemaResult<Arc<Schema>> {
1206 self.resolver
1207 .schema_from_isl_schema(IslVersion::V1_0, isl, &mut TypeStore::default(), None)
1208 }
1209
1210 pub fn load_schema_from_isl_schema_v2_0(
1216 &mut self,
1217 isl: IslSchema,
1218 ) -> IonSchemaResult<Arc<Schema>> {
1219 self.resolver
1220 .schema_from_isl_schema(IslVersion::V2_0, isl, &mut TypeStore::default(), None)
1221 }
1222
1223 fn authorities(&mut self) -> &[Box<dyn DocumentAuthority>] {
1225 &self.resolver.authorities
1226 }
1227
1228 fn add_authority(&mut self, authority: Box<dyn DocumentAuthority>) {
1230 self.resolver.authorities.push(authority);
1231 }
1232
1233 fn with_authority(&mut self, authority: Box<dyn DocumentAuthority>) {
1235 let authorities: Vec<Box<dyn DocumentAuthority>> = vec![authority];
1236 self.resolver.authorities = authorities;
1237 }
1238
1239 fn with_authorities(&mut self, authorities: Vec<Box<dyn DocumentAuthority>>) {
1242 self.resolver.authorities = authorities;
1243 }
1244
1245 pub fn schema_from_isl_types_v1_0<A: AsRef<str>, B: Into<Vec<IslType>>>(
1248 &self,
1249 id: A,
1250 isl_types: B,
1251 ) -> IonSchemaResult<Schema> {
1252 self.resolver
1253 .schema_from_isl_types(IslVersion::V1_0, id, isl_types)
1254 }
1255
1256 pub fn schema_from_isl_types_v2_0<A: AsRef<str>, B: Into<Vec<IslType>>>(
1259 &self,
1260 id: A,
1261 isl_types: B,
1262 ) -> IonSchemaResult<Schema> {
1263 self.resolver
1264 .schema_from_isl_types(IslVersion::V2_0, id, isl_types)
1265 }
1266}
1267
1268#[cfg(test)]
1269mod schema_system_tests {
1270 use super::*;
1271 use crate::authority::{FileSystemDocumentAuthority, MapDocumentAuthority};
1272 use crate::isl::isl_constraint;
1273 use crate::isl::isl_type;
1274 use crate::isl::isl_type_reference;
1275 use crate::system::IonSchemaError::InvalidSchemaError;
1276 use std::path::Path;
1277
1278 #[test]
1279 fn schema_system_add_authorities_test() {
1280 let mut schema_system = SchemaSystem::new(vec![Box::new(
1281 FileSystemDocumentAuthority::new(Path::new("")),
1282 )]);
1283 schema_system.add_authority(Box::new(FileSystemDocumentAuthority::new(Path::new(
1284 "test",
1285 ))));
1286 let schema_system_authorities = schema_system.authorities();
1287 assert_eq!(2, schema_system_authorities.len());
1288 }
1289
1290 #[test]
1291 fn schema_system_with_authority_test() {
1292 let mut schema_system = SchemaSystem::new(vec![Box::new(
1293 FileSystemDocumentAuthority::new(Path::new("")),
1294 )]);
1295 schema_system.with_authority(Box::new(FileSystemDocumentAuthority::new(Path::new(
1296 "test",
1297 ))));
1298 let schema_system_authorities = schema_system.authorities();
1299 assert_eq!(1, schema_system_authorities.len());
1300 }
1301
1302 #[test]
1303 fn schema_system_with_authorities_test() {
1304 let mut schema_system = SchemaSystem::new(vec![Box::new(
1305 FileSystemDocumentAuthority::new(Path::new("")),
1306 )]);
1307 schema_system.with_authorities(vec![
1308 Box::new(FileSystemDocumentAuthority::new(Path::new("test"))),
1309 Box::new(FileSystemDocumentAuthority::new(Path::new("ion"))),
1310 ]);
1311 let schema_system_authorities = schema_system.authorities();
1312 assert_eq!(2, schema_system_authorities.len());
1313 }
1314
1315 #[test]
1316 fn schema_system_map_authority_with_type_alias_import_test() {
1317 let map_authority = [
1319 (
1320 "sample_number.isl",
1321 r#"
1322 schema_header::{
1323 imports: [{ id: "sample_decimal.isl", type: my_decimal, as: other_decimal }],
1324 }
1325
1326 type::{
1327 name: my_int,
1328 type: int,
1329 }
1330
1331 type::{
1332 name: my_number,
1333 all_of: [
1334 my_int,
1335 other_decimal,
1336 ],
1337 }
1338
1339 schema_footer::{
1340 }
1341 "#,
1342 ),
1343 (
1344 "sample_decimal.isl",
1345 r#"
1346 schema_header::{
1347 imports: [],
1348 }
1349
1350 type::{
1351 name: my_decimal,
1352 type: decimal,
1353 }
1354
1355 type::{
1356 name: my_string,
1357 type: string,
1358 }
1359
1360 schema_footer::{
1361 }
1362 "#,
1363 ),
1364 ];
1365 let mut schema_system =
1366 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1367 let schema = schema_system.load_schema("sample_number.isl");
1369 assert!(schema.is_ok());
1370 }
1371
1372 #[test]
1373 fn schema_system_map_authority_with_type_import_test() {
1374 let map_authority = [
1376 (
1377 "sample_number.isl",
1378 r#"
1379 schema_header::{
1380 imports: [{ id: "sample_decimal.isl", type: my_decimal }],
1381 }
1382
1383 type::{
1384 name: my_int,
1385 type: int,
1386 }
1387
1388 type::{
1389 name: my_number,
1390 all_of: [
1391 my_int,
1392 my_decimal,
1393 ],
1394 }
1395
1396 schema_footer::{
1397 }
1398 "#,
1399 ),
1400 (
1401 "sample_decimal.isl",
1402 r#"
1403 schema_header::{
1404 imports: [],
1405 }
1406
1407 type::{
1408 name: my_decimal,
1409 type: decimal,
1410 }
1411
1412 type::{
1413 name: my_string,
1414 type: string,
1415 }
1416
1417 schema_footer::{
1418 }
1419 "#,
1420 ),
1421 ];
1422 let mut schema_system =
1423 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1424 let schema = schema_system.load_schema("sample_number.isl");
1426 assert!(schema.is_ok());
1427
1428 let isl_imported_type = schema.as_ref().unwrap().get_imported_type("my_decimal");
1430 assert!(isl_imported_type.is_some());
1431 let isl_defined_type = schema
1432 .as_ref()
1433 .unwrap()
1434 .get_built_in_or_defined_type("my_number");
1435 assert!(isl_defined_type.is_some());
1436 let isl_type = schema.as_ref().unwrap().get_type("my_decimal");
1437 assert!(isl_type.is_some());
1438 }
1439
1440 #[test]
1441 fn schema_system_map_authority_with_schema_import_test() {
1442 let map_authority = [
1444 (
1445 "sample_import_string.isl",
1446 r#"
1447 schema_header::{
1448 imports: [{ id: "sample_string.isl" }],
1449 }
1450
1451 type::{
1452 name: import_string,
1453 type: my_string,
1454 }
1455
1456 schema_footer::{
1457 }
1458 "#,
1459 ),
1460 (
1461 "sample_string.isl",
1462 r#"
1463 schema_header::{
1464 imports: [],
1465 }
1466
1467 type::{
1468 name: my_string,
1469 type: string,
1470 }
1471
1472 schema_footer::{
1473 }
1474 "#,
1475 ),
1476 ];
1477 let mut schema_system =
1478 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1479 let schema = schema_system.load_schema("sample_import_string.isl");
1481 assert!(schema.is_ok());
1482 }
1483
1484 #[test]
1485 fn schema_system_map_authority_with_inline_import_type_test() {
1486 let map_authority = [
1488 (
1489 "sample_number.isl",
1490 r#"
1491 schema_header::{
1492 imports: [],
1493 }
1494
1495 type::{
1496 name: my_int,
1497 type: int,
1498 }
1499
1500 type::{
1501 name: my_number,
1502 all_of: [
1503 my_int,
1504 { id: "sample_decimal.isl", type: my_decimal },
1505 ],
1506 }
1507
1508 schema_footer::{
1509 }
1510 "#,
1511 ),
1512 (
1513 "sample_decimal.isl",
1514 r#"
1515 schema_header::{
1516 imports: [],
1517 }
1518
1519 type::{
1520 name: my_decimal,
1521 type: decimal,
1522 }
1523
1524 type::{
1525 name: my_string,
1526 type: string,
1527 }
1528
1529 schema_footer::{
1530 }
1531 "#,
1532 ),
1533 ];
1534 let mut schema_system =
1535 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1536 let schema = schema_system.load_schema("sample_number.isl");
1538 assert!(schema.is_ok());
1539 }
1540
1541 #[test]
1542 fn schema_system_map_authority_with_incorrect_inline_import_type_test() {
1543 let map_authority = [
1545 (
1546 "sample_number.isl",
1547 r#"
1548 $ion_schema_2_0
1549 schema_header::{
1550 imports: [],
1551 }
1552
1553 type::{
1554 name: my_int,
1555 type: int,
1556 }
1557
1558 type::{
1559 name: my_number,
1560 all_of: [
1561 my_int,
1562 { id: "sample_decimal.isl", type: my_decimal, as: other_decimal},
1563 ],
1564 }
1565
1566 schema_footer::{
1567 }
1568 "#,
1569 ),
1570 (
1571 "sample_decimal.isl",
1572 r#"
1573 $ion_schema_2_0
1574 schema_header::{
1575 imports: [],
1576 }
1577
1578 type::{
1579 name: my_decimal,
1580 type: decimal,
1581 }
1582
1583 type::{
1584 name: my_string,
1585 type: string,
1586 }
1587
1588 schema_footer::{
1589 }
1590 "#,
1591 ),
1592 ];
1593 let mut schema_system =
1594 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1595 let schema = schema_system.load_schema("sample_number.isl");
1597 assert!(schema.is_err());
1598 assert!(matches!(schema.unwrap_err(), InvalidSchemaError { .. }));
1599 }
1600
1601 #[test]
1602 fn schema_system_map_authority_with_isl_builtin_derived_types() {
1603 let map_authority = [
1605 (
1606 "sample.isl",
1607 r#"
1608 schema_header::{
1609 imports: [ { id: "sample_builtin_types.isl" } ],
1610 }
1611
1612 type::{
1613 name: my_number,
1614 type: number,
1615 }
1616
1617 type::{
1618 name: my_type,
1619 one_of: [
1620 my_number,
1621 my_text,
1622 my_lob
1623 ],
1624 }
1625
1626 schema_footer::{
1627 }
1628 "#,
1629 ),
1630 (
1631 "sample_builtin_types.isl",
1632 r#"
1633 schema_header::{
1634 imports: [],
1635 }
1636
1637 type::{
1638 name: my_text,
1639 type: text,
1640 }
1641
1642 type::{
1643 name: my_lob,
1644 type: lob,
1645 }
1646
1647 schema_footer::{
1648 }
1649 "#,
1650 ),
1651 ];
1652 let mut schema_system =
1653 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1654 let schema = schema_system.load_schema("sample.isl");
1656 assert!(schema.is_ok());
1657 }
1658
1659 #[test]
1660 fn schema_system_map_authority_with_isl_builtin_derived_nullable_types() {
1661 let map_authority = [
1663 (
1664 "sample.isl",
1665 r#"
1666 schema_header::{
1667 imports: [ { id: "sample_builtin_nullable_types.isl" } ],
1668 }
1669
1670 type::{
1671 name: my_number,
1672 type: $number,
1673 }
1674
1675 type::{
1676 name: my_any,
1677 type: $any,
1678 }
1679
1680 type::{
1681 name: my_type,
1682 one_of: [
1683 my_number,
1684 my_text,
1685 my_lob
1686 ],
1687 }
1688
1689 schema_footer::{
1690 }
1691 "#,
1692 ),
1693 (
1694 "sample_builtin_nullable_types.isl",
1695 r#"
1696 schema_header::{
1697 imports: [],
1698 }
1699
1700 type::{
1701 name: my_text,
1702 type: $text,
1703 }
1704
1705 type::{
1706 name: my_lob,
1707 type: $lob,
1708 }
1709
1710 schema_footer::{
1711 }
1712 "#,
1713 ),
1714 ];
1715 let mut schema_system =
1716 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1717 let schema = schema_system.load_schema("sample.isl");
1719 assert!(schema.is_ok());
1720 }
1721
1722 #[test]
1723 fn schema_system_map_authority_with_valid_transitive_type_import_test() {
1724 let map_authority = [
1726 (
1727 "sample.isl",
1728 r#"
1729 schema_header::{
1730 imports: [ { id: "sample_builtin_nullable_types.isl", type: my_text } ],
1731 }
1732
1733 type::{
1734 name: my_type,
1735 type: my_text
1736 }
1737
1738 schema_footer::{
1739 }
1740 "#,
1741 ),
1742 (
1743 "sample_builtin_nullable_types.isl",
1744 r#"
1745 schema_header::{
1746 imports: [],
1747 }
1748
1749 type::{
1750 name: my_text,
1751 one_of: [
1752 my_string,
1753 symbol,
1754 ],
1755 }
1756
1757 type::{
1758 name: my_string,
1759 type: string,
1760 }
1761
1762 schema_footer::{
1763 }
1764 "#,
1765 ),
1766 ];
1767 let mut schema_system =
1768 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1769 let schema = schema_system.load_schema("sample.isl");
1771 assert!(schema.is_ok());
1772 }
1773
1774 #[test]
1775 fn schema_system_map_authority_with_invalid_transitive_type_import_test() {
1776 let map_authority = [
1778 (
1779 "sample.isl",
1780 r#"
1781 schema_header::{
1782 imports: [ { id: "sample_builtin_nullable_types.isl", type: my_text } ],
1783 }
1784
1785 type::{
1786 name: my_type,
1787 type: my_string, // this type reference was not imported by name in sample.isl
1788 }
1789
1790 schema_footer::{
1791 }
1792 "#,
1793 ),
1794 (
1795 "sample_builtin_nullable_types.isl",
1796 r#"
1797 schema_header::{
1798 imports: [],
1799 }
1800
1801 type::{
1802 name: my_text,
1803 one_of: [
1804 my_string,
1805 symbol,
1806 ],
1807 }
1808
1809 type::{
1810 name: my_string,
1811 type: string,
1812 }
1813
1814 schema_footer::{
1815 }
1816 "#,
1817 ),
1818 ];
1819 let mut schema_system =
1820 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1821 let schema = schema_system.load_schema("sample.isl");
1823 assert!(schema.is_err());
1824 }
1825
1826 #[test]
1827 fn schema_system_map_authority_with_multiple_type_definitions() {
1828 let map_authority = [(
1830 "sample.isl",
1831 r#"
1832 schema_header::{
1833 imports: [],
1834 }
1835
1836 type::{
1837 name: my_text,
1838 one_of: [
1839 my_string,
1840 symbol,
1841 ],
1842 }
1843
1844 type::{
1845 name: my_string,
1846 type: string,
1847 }
1848
1849 schema_footer::{
1850 }
1851 "#,
1852 )];
1853 let mut schema_system =
1854 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1855 let schema = schema_system.load_schema("sample.isl");
1857 assert!(schema.is_ok());
1858 }
1859
1860 #[test]
1861 fn schema_system_map_authority_with_multiple_codependent_type_definitions() {
1862 let map_authority = [(
1864 "sample.isl",
1865 r#"
1866 type::{
1867 name: node_a,
1868 type: list,
1869 element: node_b,
1870 }
1871
1872 type::{
1873 name: node_b,
1874 type: sexp,
1875 element: node_a,
1876 }
1877 "#,
1878 )];
1879 let mut schema_system =
1880 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1881 let schema = schema_system.load_schema("sample.isl");
1883 assert!(schema.is_ok());
1884 }
1885
1886 #[test]
1887 fn schema_system_map_authority_with_self_referencing_type_definition() {
1888 let map_authority = [(
1890 "sample.isl",
1891 r#"
1892 type::{
1893 name: binary_heap_node,
1894 type: sexp,
1895 element: { one_of: [ binary_heap_node, int ] },
1896 container_length: range::[0, 2],
1897 }
1898 "#,
1899 )];
1900 let mut schema_system =
1901 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1902 let schema = schema_system.load_schema("sample.isl");
1904 assert!(schema.is_ok());
1905 }
1906
1907 #[test]
1908 fn top_level_type_def_without_name_field() {
1909 let map_authority = [(
1911 "sample.isl",
1912 r#"
1913 type::{
1914 // top level type definition must contain a `name` field
1915 }
1916 "#,
1917 )];
1918 let mut schema_system =
1919 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1920 let schema = schema_system.load_schema("sample.isl");
1922 assert!(schema.is_err());
1923 }
1924
1925 #[test]
1926 fn top_level_type_def_with_multiple_name_field() {
1927 let map_authority = [(
1929 "sample.isl",
1930 r#"
1931 type::{
1932 name: my_type,
1933 name: new_type
1934 }
1935 "#,
1936 )];
1937 let mut schema_system =
1938 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1939 let schema = schema_system.load_schema("sample.isl");
1941 assert!(schema.is_err());
1942 }
1943
1944 #[test]
1945 fn top_level_type_def_with_non_symbol_name_field() {
1946 let map_authority = [(
1948 "sample.isl",
1949 r#"
1950 type::{
1951 name: "my_type"
1952 }
1953 "#,
1954 )];
1955 let mut schema_system =
1956 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1957 let schema = schema_system.load_schema("sample.isl");
1959 assert!(schema.is_err());
1960 }
1961
1962 #[test]
1963 fn valid_isl_version_marker_test() {
1964 let map_authority = [(
1966 "sample.isl",
1967 r#"
1968 $ion_schema_1_0
1969 "#,
1970 )];
1971 let mut schema_system =
1972 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1973 let schema = schema_system.load_schema("sample.isl");
1975 assert!(schema.is_ok());
1976 }
1977
1978 #[test]
1979 fn invalid_isl_version_marker_test() {
1980 let map_authority = [(
1982 "sample.isl",
1983 r#"
1984 $ion_schema_4_5
1985 "#,
1986 )];
1987 let mut schema_system =
1988 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
1989 let schema = schema_system.load_schema("sample.isl");
1991 assert!(schema.is_err());
1992 }
1993
1994 #[test]
1995 fn unsupported_isl_version_marker_test() {
1996 let map_authority = [(
1998 "sample.isl",
1999 r#"
2000 $ion_schema_2_1
2001 "#,
2002 )];
2003 let mut schema_system =
2004 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
2005 let schema = schema_system.load_schema("sample.isl");
2007 assert!(schema.is_err());
2008 }
2009
2010 #[test]
2011 fn open_content_test() -> IonSchemaResult<()> {
2012 let map_authority = [(
2014 "sample.isl",
2015 r#"
2016 schema_header::{}
2017
2018 type::{
2019 name: my_type,
2020 type: string,
2021 }
2022
2023 open_content_1::{
2024 unknown_constraint: "this is an open content struct"
2025 }
2026
2027 open_content_2::{
2028 unknown_constraint: "this is an open content struct"
2029 }
2030
2031 schema_footer::{}
2032 "#,
2033 )];
2034 let mut schema_system =
2035 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
2036 let schema = schema_system.load_isl_schema("sample.isl")?;
2037 let expected_open_content: Vec<_> = Element::read_all(
2038 r#"
2039 open_content_1::{
2040 unknown_constraint: "this is an open content struct"
2041 }
2042
2043 open_content_2::{
2044 unknown_constraint: "this is an open content struct"
2045 }
2046 "#
2047 .as_bytes(),
2048 )?
2049 .into_iter()
2050 .collect();
2051
2052 let open_content: Vec<_> = schema.open_content().map(|x| x.to_owned()).collect();
2054 assert_eq!(open_content.len(), 2);
2055 assert_eq!(open_content, expected_open_content);
2056 Ok(())
2057 }
2058
2059 #[test]
2060 fn unexpected_fields_in_isl_v2_0_header() {
2061 let map_authority = [(
2063 "sample.isl",
2064 r#"
2065 $ion_schema_2_0
2066 schema_header::{
2067 user_reserved_fields: {
2068 schema_header: [ foo, bar ],
2069 type: [ baz ]
2070 },
2071 baz: "this is an unexpected field in schema header"
2072 }
2073
2074 type::{
2075 name: my_type,
2076 type: string,
2077 }
2078
2079 schema_footer::{}
2080 "#,
2081 )];
2082 let mut schema_system =
2083 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
2084 let schema = schema_system.load_isl_schema("sample.isl");
2085 assert!(schema.is_err());
2086 }
2087
2088 #[test]
2089 fn unexpected_fields_in_isl_v2_0_footer() {
2090 let map_authority = [(
2092 "sample.isl",
2093 r#"
2094 $ion_schema_2_0
2095 schema_header::{
2096 user_reserved_fields: {
2097 schema_footer: [ foo, bar ],
2098 sdhema_header: [ baz ]
2099 },
2100 }
2101
2102 type::{
2103 name: my_type,
2104 type: string,
2105 }
2106
2107 schema_footer::{
2108 baz: "this is an unexpected field in schema header"
2109 }
2110 "#,
2111 )];
2112 let mut schema_system =
2113 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
2114 let schema = schema_system.load_isl_schema("sample.isl");
2115 assert!(schema.is_err());
2116 }
2117
2118 #[test]
2119 fn unexpected_fields_in_isl_v2_0_type() {
2120 let map_authority = [(
2122 "sample.isl",
2123 r#"
2124 $ion_schema_2_0
2125 schema_header::{
2126 user_reserved_fields: {
2127 schema_header: [ baz ],
2128 type: [ foo, bar ]
2129 },
2130 }
2131
2132 type::{
2133 name: my_type,
2134 type: string,
2135 baz: "this is an unexpected field in schema header"
2136 }
2137
2138 schema_footer::{}
2139 "#,
2140 )];
2141 let mut schema_system =
2142 SchemaSystem::new(vec![Box::new(MapDocumentAuthority::new(map_authority))]);
2143 let schema = schema_system.load_isl_schema("sample.isl");
2144 assert!(schema.is_err());
2145 }
2146
2147 #[test]
2148 fn load_schema_from_isl_schema_v1_0_test() {
2149 let isl_type = isl_type::v_1_0::named_type(
2151 "my_type",
2152 [isl_constraint::v_1_0::element(
2153 isl_type_reference::v_1_0::named_type_ref("int"),
2154 )],
2155 );
2156 let isl = IslSchema::schema_v_1_0("sample.isl", vec![], vec![isl_type], vec![], vec![]);
2157 let mut schema_system = SchemaSystem::new(vec![]);
2158 let schema = schema_system.load_schema_from_isl_schema_v1_0(isl);
2159
2160 assert!(schema.is_ok());
2162 }
2163
2164 #[test]
2165 fn load_schema_from_isl_schema_v2_0_test() {
2166 let isl_type = isl_type::v_2_0::named_type(
2168 "my_type",
2169 [isl_constraint::v_2_0::type_constraint(
2170 isl_type_reference::v_2_0::named_type_ref("int"),
2171 )],
2172 );
2173 let isl = IslSchema::schema_v_2_0(
2174 "sample.isl",
2175 UserReservedFields::default(),
2176 vec![],
2177 vec![isl_type],
2178 vec![],
2179 vec![],
2180 );
2181 let mut schema_system = SchemaSystem::new(vec![]);
2182 let schema = schema_system.load_schema_from_isl_schema_v2_0(isl);
2183
2184 assert!(schema.is_ok());
2186 }
2187
2188 #[test]
2189 fn load_schema_from_isl_schema_v1_0_with_isl_v2_0_constraints_test() {
2190 let isl_type = isl_type::v_1_0::named_type(
2192 "my_type",
2193 [isl_constraint::v_2_0::type_constraint(
2194 isl_type_reference::v_2_0::named_type_ref("int"),
2195 )],
2196 );
2197 let isl = IslSchema::schema_v_1_0("sample.isl", vec![], vec![isl_type], vec![], vec![]);
2198 let mut schema_system = SchemaSystem::new(vec![]);
2199 let schema = schema_system.load_schema_from_isl_schema_v1_0(isl);
2200
2201 assert!(schema.is_err());
2203 }
2204
2205 #[test]
2206 fn load_schema_from_isl_schema_v2_0_with_isl_v1_0_constraints_test() {
2207 let isl_type = isl_type::v_2_0::named_type(
2209 "my_type",
2210 [isl_constraint::v_1_0::type_constraint(
2211 isl_type_reference::v_1_0::named_type_ref("int"),
2212 )],
2213 );
2214 let isl = IslSchema::schema_v_2_0(
2215 "sample.isl",
2216 UserReservedFields::default(),
2217 vec![],
2218 vec![isl_type],
2219 vec![],
2220 vec![],
2221 );
2222 let mut schema_system = SchemaSystem::new(vec![]);
2223 let schema = schema_system.load_schema_from_isl_schema_v2_0(isl);
2224
2225 assert!(&schema.is_err());
2227 }
2228
2229 #[test]
2230 fn new_schema_test() {
2231 let mut schema_system = SchemaSystem::new(vec![]);
2232 let schema = schema_system.new_schema(
2233 br#"
2234 $ion_schema_2_0
2235 schema_header::{}
2236
2237 type::{
2238 name: my_type,
2239 type: string,
2240 }
2241
2242 schema_footer::{}
2243 "#,
2244 "sample.isl",
2245 );
2246 assert!(schema.is_ok());
2247 }
2248
2249 #[test]
2250 fn new_schema_invalid_test() {
2251 let mut schema_system = SchemaSystem::new(vec![]);
2252 let schema = schema_system.new_schema(
2253 br#"
2254 $ion_schema_2_0
2255 schema_header::{}
2256
2257 type::{
2258 name: my_type,
2259 type: nullable::string, // `nullable` annotation is not supported in ISL 2.0
2260 }
2261
2262 schema_footer::{}
2263 "#,
2264 "sample.isl",
2265 );
2266 assert!(schema.is_err());
2267 }
2268
2269 #[test]
2270 fn new_isl_schema_test() {
2271 let mut schema_system = SchemaSystem::new(vec![]);
2272 let isl = schema_system.new_isl_schema(
2273 br#"
2274 $ion_schema_2_0
2275 schema_header::{}
2276
2277 type::{
2278 name: my_type,
2279 type: string,
2280 }
2281
2282 schema_footer::{}
2283 "#,
2284 "sample.isl",
2285 );
2286 assert!(isl.is_ok());
2287 }
2288
2289 #[test]
2290 fn new_isl_schema_invalid_test() {
2291 let mut schema_system = SchemaSystem::new(vec![]);
2292 let isl = schema_system.new_isl_schema(
2293 br#"
2294 $ion_schema_2_0
2295 schema_header::{}
2296
2297 type::{
2298 name: my_type,
2299 type: nullable::string, // `nullable` annotation is not supported in ISL 2.0
2300 }
2301
2302 schema_footer::{}
2303 "#,
2304 "sample.isl",
2305 );
2306 assert!(isl.is_err());
2307 }
2308
2309 #[test]
2310 fn test_send_schema() {
2311 fn assert_send<T: Send>() {}
2312 assert_send::<Schema>();
2313 }
2314
2315 #[test]
2316 fn test_sync_schema() {
2317 fn assert_sync<T: Sync>() {}
2318 assert_sync::<Schema>();
2319 }
2320
2321 #[test]
2322 fn test_send_schema_system() {
2323 fn assert_send<T: Send>() {}
2324 assert_send::<SchemaSystem>();
2325 }
2326
2327 #[test]
2328 fn test_sync_schema_system() {
2329 fn assert_sync<T: Sync>() {}
2330 assert_sync::<SchemaSystem>();
2331 }
2332}