Skip to main content

ion_schema/
types.rs

1use crate::constraint::Constraint;
2use crate::ion_path::IonPath;
3use crate::ion_schema_element::IonSchemaElementType;
4use crate::isl::isl_constraint::IslConstraintValue;
5use crate::isl::isl_type::IslType;
6use crate::isl::IslVersion;
7use crate::result::{invalid_schema_error, IonSchemaResult, ValidationResult};
8use crate::system::{PendingTypes, TypeId, TypeStore};
9use crate::violation::{Violation, ViolationCode};
10use crate::IonSchemaElement;
11use ion_rs::Element;
12use ion_rs::Symbol;
13use std::fmt::{Display, Formatter};
14use std::sync::Arc;
15
16/// Provides validation for type definition
17pub(crate) trait TypeValidator {
18    /// If the specified value violates one or more of this type's constraints,
19    /// returns `false`, otherwise `true`
20    fn is_valid(
21        &self,
22        value: &IonSchemaElement,
23        type_store: &TypeStore,
24        ion_path: &mut IonPath,
25    ) -> bool;
26
27    /// Returns `Err(violation)` with details as to which constraints were violated,
28    /// otherwise returns `Ok(())` indicating no violations were found during the validation
29    fn validate(
30        &self,
31        value: &IonSchemaElement,
32        type_store: &TypeStore,
33        ion_path: &mut IonPath,
34    ) -> ValidationResult;
35}
36
37// Provides a public facing schema type which has a reference to TypeStore
38// to get the underlying TypeDefinitionKind from TypeStore
39/// Represents a top level ISL type definition
40#[derive(Debug, Clone)]
41pub struct TypeDefinition {
42    id: TypeId,
43    type_store: Arc<TypeStore>,
44}
45
46impl TypeDefinition {
47    pub(crate) fn new(id: TypeId, type_store: Arc<TypeStore>) -> Self {
48        Self { id, type_store }
49    }
50
51    pub fn id(&self) -> TypeId {
52        self.id
53    }
54
55    /// Provides the validation for the given value based on this schema type
56    /// ```
57    /// use ion_rs::Element;
58    /// use ion_schema::IonSchemaElement;
59    /// use ion_schema::authority::{FileSystemDocumentAuthority, DocumentAuthority};
60    /// use ion_schema::system::SchemaSystem;
61    /// use ion_schema::result::IonSchemaResult;
62    /// use std::path::Path;
63    /// use ion_schema::authority::MapDocumentAuthority;
64    /// use ion_schema::AsDocumentHint;
65    ///
66    /// fn main() -> IonSchemaResult<()> {
67    ///     // create an IonSchemaElement from an Element
68    ///     let owned_element: Element = 4.into();
69    ///     let elements: Vec<Element> = vec![4.into(), "hello".to_string().into(), true.into()];
70    ///
71    ///     let map_authority = [
72    ///         (
73    ///            "sample.isl",
74    ///             r#"
75    ///                 type::{
76    ///                     name: my_int,
77    ///                     type: int,
78    ///                 }
79    ///             "#
80    ///         )   
81    ///     ];
82    ///
83    ///     // create a vector of authorities and construct schema system
84    ///     // this example uses above mentioned map as the authority
85    ///     let authorities: Vec<Box<dyn DocumentAuthority>> = vec![Box::new(
86    ///             MapDocumentAuthority::new(map_authority),
87    ///         )];
88    ///     let mut schema_system = SchemaSystem::new(authorities);
89    ///
90    ///     // use this schema_system to load a schema as following
91    ///     let schema = schema_system.load_schema("sample.isl")?;
92    ///
93    ///     // unwrap() here because we know that the `my_int` type exists in sample.isl
94    ///     let type_ref = schema.get_type("my_int").unwrap();
95    ///
96    ///     assert!(type_ref.validate(&owned_element).is_ok()); // 4 is valid for `my_int`
97    ///     assert!(type_ref.validate(elements.as_document()).is_err()); // document type is invalid for `my_int` type
98    ///     Ok(())
99    /// }
100    /// ```
101    pub fn validate<'a, I: Into<IonSchemaElement<'a>>>(&self, value: I) -> ValidationResult {
102        let type_def = self.type_store.get_type_by_id(self.id).unwrap();
103
104        // convert given IonSchemaElement to an Element
105        let schema_element: IonSchemaElement = value.into();
106
107        type_def.validate(&schema_element, &self.type_store, &mut IonPath::default())
108    }
109}
110
111/// Represents a [`BuiltInTypeDefinition`] which stores a resolved builtin ISl type using [`TypeStore`]
112#[derive(Debug, Clone, PartialEq)]
113pub(crate) enum BuiltInTypeDefinition {
114    Atomic(IonSchemaElementType, Nullability),
115    Derived(TypeDefinitionImpl),
116}
117
118/// Represents whether an atomic built-in type is nullable or not
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub(crate) enum Nullability {
121    Nullable,
122    NotNullable,
123}
124
125impl BuiltInTypeDefinition {
126    pub(crate) fn parse_from_isl_type(
127        isl_version: IslVersion,
128        isl_type: &IslType,
129        type_store: &mut TypeStore,
130        pending_types: &mut PendingTypes,
131    ) -> IonSchemaResult<Self> {
132        let mut constraints = vec![];
133
134        // parses an isl_type to a TypeDefinition
135        let type_name = isl_type.name().map(|x| x.to_owned());
136
137        // convert IslConstraint to Constraint
138        for isl_constraint in isl_type.constraints() {
139            // For built in types, open_content is set as true as Ion Schema by default allows open content
140            let constraint = Constraint::resolve_from_isl_constraint(
141                isl_version,
142                &isl_constraint.constraint_value,
143                type_store,
144                pending_types,
145                true,
146            )?;
147            constraints.push(constraint);
148        }
149
150        let builtin_type_def = BuiltInTypeDefinition::Derived(TypeDefinitionImpl::new(
151            type_name.to_owned(),
152            constraints,
153            None,
154        ));
155        Ok(builtin_type_def)
156    }
157}
158
159impl TypeValidator for BuiltInTypeDefinition {
160    fn is_valid(
161        &self,
162        value: &IonSchemaElement,
163        type_store: &TypeStore,
164        ion_path: &mut IonPath,
165    ) -> bool {
166        let violation = self.validate(value, type_store, ion_path);
167        violation.is_ok()
168    }
169
170    fn validate(
171        &self,
172        value: &IonSchemaElement,
173        type_store: &TypeStore,
174        ion_path: &mut IonPath,
175    ) -> ValidationResult {
176        match &self {
177            BuiltInTypeDefinition::Atomic(ion_type, is_nullable) => {
178                if *is_nullable == Nullability::NotNullable && value.is_null() {
179                    return Err(Violation::new(
180                        "type_constraint",
181                        ViolationCode::InvalidNull,
182                        format!("expected type {ion_type:?} doesn't allow null"),
183                        ion_path,
184                    ));
185                }
186                if value.ion_schema_type() != *ion_type {
187                    return Err(Violation::new(
188                        "type_constraint",
189                        ViolationCode::TypeMismatched,
190                        format!(
191                            "expected type {:?}, found {:?}",
192                            ion_type,
193                            value.ion_schema_type()
194                        ),
195                        ion_path,
196                    ));
197                }
198                Ok(())
199            }
200            BuiltInTypeDefinition::Derived(other_type) => {
201                other_type.validate(value, type_store, ion_path)
202            }
203        }
204    }
205}
206
207impl Display for BuiltInTypeDefinition {
208    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
209        match &self {
210            BuiltInTypeDefinition::Atomic(ion_type, _) => write!(f, "{ion_type}"),
211            BuiltInTypeDefinition::Derived(type_def) => write!(f, "{type_def}"),
212        }
213    }
214}
215
216/// Represents a [`TypeDefinitionKind`] which stores a resolved ISL type using [`TypeStore`]
217#[derive(Debug, Clone, PartialEq)]
218pub(crate) enum TypeDefinitionKind {
219    Named(TypeDefinitionImpl),
220    Anonymous(TypeDefinitionImpl),
221    BuiltIn(BuiltInTypeDefinition),
222}
223
224impl TypeDefinitionKind {
225    /// Creates a named [`TypeDefinitionKind`] using the [`Constraint`]s defined within it
226    pub fn named<A: Into<String>, B: Into<Vec<Constraint>>>(
227        name: A,
228        constraints: B,
229    ) -> TypeDefinitionKind {
230        TypeDefinitionKind::Named(TypeDefinitionImpl::new(
231            Some(name.into()),
232            constraints.into(),
233            None,
234        ))
235    }
236
237    /// Provides a boolean that represents whether this type is deferred type or not
238    pub fn is_deferred_type_def(&self) -> bool {
239        match self {
240            TypeDefinitionKind::Named(type_def) => type_def.is_deferred_type_def,
241            _ => false,
242        }
243    }
244
245    /// Creates an anonymous [`TypeDefinitionKind`] using the [`Constraint`]s defined within it
246    pub fn anonymous<A: Into<Vec<Constraint>>>(constraints: A) -> TypeDefinitionKind {
247        TypeDefinitionKind::Anonymous(TypeDefinitionImpl::new(None, constraints.into(), None))
248    }
249
250    /// Provides the underlying constraints of [`TypeDefinitionKind`]
251    pub fn constraints(&self) -> &[Constraint] {
252        match &self {
253            TypeDefinitionKind::Named(named_type) => named_type.constraints(),
254            TypeDefinitionKind::Anonymous(anonymous_type) => anonymous_type.constraints(),
255            _ => &[],
256        }
257    }
258
259    /// Provides the validation result for base built in type of the type definition with the given value
260    // This method is only used by nullable type reference for validation, it searches for a base type name which is built in type and nullable.
261    // It returns the result of validation for that nullable base type.
262    pub fn is_valid_for_base_nullable_type(
263        &self,
264        value: &IonSchemaElement,
265        type_store: &TypeStore,
266        ion_path: &mut IonPath,
267    ) -> bool {
268        // get a nullable built in base type name which can be used to perform validation to check for correct `null.*` type
269        let built_in_type_name = match self {
270            TypeDefinitionKind::Named(_) | TypeDefinitionKind::Anonymous(_) => {
271                // only built in type names are needed for base type
272                None
273            }
274            TypeDefinitionKind::BuiltIn(built_int_type) => match built_int_type {
275                BuiltInTypeDefinition::Atomic(ion_type, nullability) => {
276                    Some(format!("${ion_type}"))
277                }
278                BuiltInTypeDefinition::Derived(type_def) => {
279                    let type_name = type_def.name().as_ref().unwrap().to_string();
280                    if !type_name.starts_with('$') {
281                        Some(format!("${type_name}"))
282                    } else {
283                        Some(type_name)
284                    }
285                }
286            },
287        }
288        .unwrap(); // safe unwrap() because nullable type references are only used for built in types
289
290        // unwrap() here are safe because it has been verified while constructing the schema that built type definition exist in the type store
291        let type_def = type_store
292            .get_type_by_id(
293                type_store
294                    .get_builtin_type_id(built_in_type_name.as_str())
295                    .unwrap(),
296            )
297            .unwrap();
298
299        type_def.is_valid(value, type_store, ion_path)
300    }
301}
302
303impl Display for TypeDefinitionKind {
304    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
305        match &self {
306            TypeDefinitionKind::Named(named_type_def) => {
307                write!(f, "{named_type_def}")
308            }
309            TypeDefinitionKind::Anonymous(anonymous_type_def) => {
310                write!(f, "{anonymous_type_def}")
311            }
312            TypeDefinitionKind::BuiltIn(builtin_type_def) => {
313                write!(f, "{builtin_type_def}")
314            }
315        }
316    }
317}
318
319impl TypeValidator for TypeDefinitionKind {
320    fn is_valid(
321        &self,
322        value: &IonSchemaElement,
323        type_store: &TypeStore,
324        ion_path: &mut IonPath,
325    ) -> bool {
326        let violation = self.validate(value, type_store, ion_path);
327        violation.is_ok()
328    }
329
330    fn validate(
331        &self,
332        value: &IonSchemaElement,
333        type_store: &TypeStore,
334        ion_path: &mut IonPath,
335    ) -> ValidationResult {
336        match self {
337            TypeDefinitionKind::Named(named_type) => {
338                named_type.validate(value, type_store, ion_path)
339            }
340            TypeDefinitionKind::Anonymous(anonymous_type) => {
341                anonymous_type.validate(value, type_store, ion_path)
342            }
343            TypeDefinitionKind::BuiltIn(built_in_type) => {
344                built_in_type.validate(value, type_store, ion_path)
345            }
346        }
347    }
348}
349
350/// A [`TypeDefinitionImpl`] consists of an optional name and zero or more constraints.
351#[derive(Debug, Clone)]
352pub(crate) struct TypeDefinitionImpl {
353    name: Option<String>,
354    constraints: Vec<Constraint>,
355    // `is_deferred_type_def` indicates if this is a deferred type def which will be resolved later
356    // e.g.
357    // ```ion
358    // type:: {
359    //  name: foo,
360    //  type: bar,
361    // }
362    // type:: {
363    //  name: bar,
364    //  type: int
365    // }
366    // ```
367    // For above example, `bar` will be saved as deferred type definition until we resolve the definition of `bar`
368    is_deferred_type_def: bool,
369    // Represents the ISL type struct in string format, this will be used for violation messages
370    isl_type_struct: Option<Element>,
371}
372
373impl TypeDefinitionImpl {
374    pub fn new(
375        name: Option<String>,
376        constraints: Vec<Constraint>,
377        isl_type_struct: Option<Element>,
378    ) -> Self {
379        Self {
380            name,
381            constraints,
382            is_deferred_type_def: false,
383            isl_type_struct,
384        }
385    }
386
387    pub fn new_deferred_type_def(name: String) -> Self {
388        Self {
389            name: Some(name),
390            constraints: vec![],
391            is_deferred_type_def: true,
392            isl_type_struct: None,
393        }
394    }
395
396    pub fn name(&self) -> &Option<String> {
397        &self.name
398    }
399
400    pub fn with_name(self, alias: String) -> Self {
401        Self {
402            name: Some(alias),
403            constraints: self.constraints,
404            is_deferred_type_def: self.is_deferred_type_def,
405            isl_type_struct: None,
406        }
407    }
408
409    pub fn is_deferred_type_def(&self) -> bool {
410        self.is_deferred_type_def
411    }
412
413    pub fn constraints(&self) -> &[Constraint] {
414        &self.constraints
415    }
416
417    /// Parse constraints inside an [`IslType`] to a schema type definition, update the [`PendingTypes`]
418    /// and return its [`TypeId`] if the conversion was successful, otherwise return an [`IonSchemaError`]
419    ///
420    /// [`IonSchemaError`]: crate::result::IonSchemaError
421    pub(crate) fn parse_from_isl_type_and_update_pending_types(
422        isl_version: IslVersion,
423        isl_type: &IslType,
424        type_store: &mut TypeStore,
425        pending_types: &mut PendingTypes,
426    ) -> IonSchemaResult<TypeId> {
427        let mut constraints = vec![];
428
429        // parses an isl_type to a TypeDefinition
430        let type_name = isl_type.name();
431
432        if let Some(type_name) = type_name {
433            if let Some(type_def) = type_store.get_type_def_by_name(type_name) {
434                if isl_version == IslVersion::V2_0 && !type_def.is_deferred_type_def() {
435                    return invalid_schema_error(format!(
436                        "The schema document can not have two type definitions with same name: {}",
437                        type_name
438                    ));
439                }
440            }
441            // add parent information for named type
442            pending_types.add_parent(type_name.to_owned(), type_store);
443        }
444
445        // add this unresolved type to context for type_id
446        let type_id = pending_types.add_type(type_store, type_name.map(|x| x.to_owned()));
447
448        // convert IslConstraint to Constraint
449        let mut found_type_constraint = false;
450        for isl_constraint in isl_type.constraints() {
451            if let IslConstraintValue::Type(_) = isl_constraint.constraint_value {
452                found_type_constraint = true;
453            }
454
455            let constraint = Constraint::resolve_from_isl_constraint(
456                isl_version,
457                &isl_constraint.constraint_value,
458                type_store,
459                pending_types,
460                isl_type.is_open_content_allowed(),
461            )?;
462            constraints.push(constraint);
463        }
464
465        let isl_struct = isl_type.isl_type_struct.as_ref();
466        // add `type: any` as a default type constraint if there is no type constraint found
467        if !found_type_constraint && isl_version == IslVersion::V1_0 {
468            // set the isl type name for any error that is returned while parsing its constraints
469            let isl_type_name = match type_name.to_owned() {
470                Some(name) => name.to_owned(),
471                None => match isl_struct {
472                    None => "".to_owned(),
473                    Some(isl_type_struct) => format!("{isl_type_struct}"),
474                },
475            };
476
477            let isl_constraint: IslConstraintValue =
478                    // default type for ISL 1.0 is `any`
479                    IslConstraintValue::from_ion_element(
480                        isl_version,
481                        "type",
482                        &Element::symbol(Symbol::from("any")),
483                        &isl_type_name,
484                        &mut vec![],
485                    )?;
486
487            let constraint = Constraint::resolve_from_isl_constraint(
488                isl_version,
489                &isl_constraint,
490                type_store,
491                pending_types,
492                true, // by default Ion Schema allows open content
493            )?;
494            constraints.push(constraint);
495        }
496
497        let type_def = TypeDefinitionImpl::new(
498            type_name.map(|x| x.to_owned()),
499            constraints,
500            isl_type.isl_type_struct.to_owned(),
501        );
502
503        // actual type id is the type id with respect to type store length
504        let actual_type_id;
505
506        if let Some(type_name) = type_name {
507            // update with this resolved type_def to context for type_id
508            actual_type_id =
509                pending_types.update_named_type(type_id, type_name, type_def, type_store);
510
511            // clear parent information from type_store as the type is already added in the type_store now
512            pending_types.clear_parent();
513        } else {
514            // update with this resolved type_def to context for type_id
515            actual_type_id = pending_types.update_anonymous_type(type_id, type_def, type_store);
516        }
517
518        Ok(actual_type_id)
519    }
520}
521
522impl PartialEq for TypeDefinitionImpl {
523    fn eq(&self, other: &Self) -> bool {
524        self.name() == other.name() && self.constraints == other.constraints()
525    }
526}
527
528impl TypeValidator for TypeDefinitionImpl {
529    fn is_valid(
530        &self,
531        value: &IonSchemaElement,
532        type_store: &TypeStore,
533        ion_path: &mut IonPath,
534    ) -> bool {
535        let violation = self.validate(value, type_store, ion_path);
536        violation.is_ok()
537    }
538
539    fn validate(
540        &self,
541        value: &IonSchemaElement,
542        type_store: &TypeStore,
543        ion_path: &mut IonPath,
544    ) -> ValidationResult {
545        let mut violations: Vec<Violation> = vec![];
546        let type_name = match self.name() {
547            None => match self.isl_type_struct.as_ref() {
548                None => "".to_owned(),
549                Some(anonymous_struct) => {
550                    format!("{anonymous_struct}")
551                }
552            },
553            Some(name) => name.to_owned(),
554        };
555        for constraint in self.constraints() {
556            if let Err(violation) = constraint.validate(value, type_store, ion_path) {
557                violations.push(violation);
558            }
559        }
560        if violations.is_empty() {
561            return Ok(());
562        }
563        Err(Violation::with_violations(
564            type_name,
565            ViolationCode::TypeConstraintsUnsatisfied,
566            "value didn't satisfy type constraint(s)",
567            ion_path,
568            violations,
569        ))
570    }
571}
572
573impl Display for TypeDefinitionImpl {
574    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
575        let type_def_name = match &self.name {
576            None => match &self.isl_type_struct {
577                None => "".to_owned(),
578                Some(type_name) => format!("{type_name}"),
579            },
580            Some(type_name) => type_name.to_owned(),
581        };
582
583        write!(f, "{type_def_name}")
584    }
585}
586
587#[cfg(test)]
588mod type_definition_tests {
589    use super::*;
590    use crate::constraint::Constraint;
591    use crate::isl::isl_constraint::v_1_0::*;
592    use crate::isl::isl_type::v_1_0::*;
593    use crate::isl::isl_type::IslType;
594    use crate::isl::isl_type_reference::v_1_0::*;
595    use crate::isl::ranges::*;
596    use crate::isl::util::Ieee754InterchangeFormat;
597    use crate::isl::util::TimestampPrecision;
598    use crate::isl::*;
599    use crate::system::PendingTypes;
600
601    use rstest::*;
602    use std::collections::HashSet;
603
604    // TODO: Remove type ids for assertion to make tests more readable
605    #[rstest(
606    isl_type, type_def,
607    case::type_constraint_with_anonymous_type(
608        /* For a schema with single anonymous type as below:
609            { type: int }
610         */
611        anonymous_type([type_constraint(named_type_ref("int"))]),
612    TypeDefinitionKind::anonymous([Constraint::type_constraint(0)])
613    ),
614    case::type_constraint_with_named_type(
615        /* For a schema with named type as below:
616            { name: my_int, type: int }
617         */
618        named_type("my_int", [type_constraint(named_type_ref("int"))]),
619    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(0)])
620    ),
621    case::type_constraint_with_self_reference_type(
622        /* For a schema with self reference type as below:
623            { name: my_int, type: my_int }
624         */
625        named_type("my_int", [type_constraint(named_type_ref("my_int"))]),
626    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(35)])
627    ),
628    case::type_constraint_with_nested_self_reference_type(
629        /* For a schema with nested self reference type as below:
630            { name: my_int, type: { type: my_int } }
631         */
632        named_type("my_int", [type_constraint(anonymous_type_ref([type_constraint(named_type_ref("my_int"))]))]),
633    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(36)]) // 0-35 are built-in types which are preloaded to the type_store
634    ),
635    case::type_constraint_with_nested_type(
636        /* For a schema with nested types as below:
637            { name: my_int, type: { type: int } }
638         */
639        named_type("my_int", [type_constraint(anonymous_type_ref([type_constraint(named_type_ref("int"))]))]),
640    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(36)])
641    ),
642    case::type_constraint_with_nested_multiple_types(
643        /* For a schema with nested multiple types as below:
644            { name: my_int, type: { type: int }, type: { type: my_int } }
645         */
646        named_type("my_int", [type_constraint(anonymous_type_ref([type_constraint(named_type_ref("int"))])), type_constraint(anonymous_type_ref([type_constraint(named_type_ref("my_int"))]))]),
647    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(36), Constraint::type_constraint(37)])
648    ),
649    case::all_of_constraint(
650        /* For a schema with all_of type as below:
651            { all_of: [{ type: int }] }
652        */
653        anonymous_type([all_of([anonymous_type_ref([type_constraint(named_type_ref("int"))])])]),
654    TypeDefinitionKind::anonymous([Constraint::all_of([36]), Constraint::type_constraint(34)])
655    ),
656    case::any_of_constraint(
657        /* For a schema with any_of constraint as below:
658            { any_of: [{ type: int }, { type: decimal }] }
659        */
660        anonymous_type([any_of([anonymous_type_ref([type_constraint(named_type_ref("int"))]), anonymous_type_ref([type_constraint(named_type_ref("decimal"))])])]),
661    TypeDefinitionKind::anonymous([Constraint::any_of([36, 37]), Constraint::type_constraint(34)])
662    ),
663    case::one_of_constraint(
664        /* For a schema with one_of constraint as below:
665            { any_of: [{ type: int }, { type: decimal }] }
666        */
667        anonymous_type([one_of([anonymous_type_ref([type_constraint(named_type_ref("int"))]), anonymous_type_ref([type_constraint(named_type_ref("decimal"))])])]),
668    TypeDefinitionKind::anonymous([Constraint::one_of([36, 37]), Constraint::type_constraint(34)])
669    ),
670    case::not_constraint(
671        /* For a schema with not constraint as below:
672            { not: { type: int } }
673        */
674        anonymous_type([not(anonymous_type_ref([type_constraint(named_type_ref("int"))]))]),
675    TypeDefinitionKind::anonymous([Constraint::not(36), Constraint::type_constraint(34)])
676    ),
677    case::ordered_elements_constraint(
678        /* For a schema with ordered_elements constraint as below:
679            { ordered_elements: [ symbol, { type: int }, ] }
680        */
681        anonymous_type([ordered_elements([variably_occurring_type_ref(named_type_ref("symbol"), UsizeRange::new_single_value(1)), variably_occurring_type_ref(anonymous_type_ref([type_constraint(named_type_ref("int"))]), UsizeRange::new_single_value(1))])]),
682    TypeDefinitionKind::anonymous([Constraint::ordered_elements([5, 36]), Constraint::type_constraint(34)])
683    ),
684    case::fields_constraint(
685        /* For a schema with fields constraint as below:
686            { fields: { name: string, id: int} }
687        */
688        anonymous_type([fields(vec![("name".to_owned(), variably_occurring_type_ref(named_type_ref("string"), UsizeRange::zero_or_one())), ("id".to_owned(), variably_occurring_type_ref(named_type_ref("int"), UsizeRange::zero_or_one()))].into_iter())]),
689    TypeDefinitionKind::anonymous([Constraint::fields(vec![("name".to_owned(), 4), ("id".to_owned(), 0)].into_iter()), Constraint::type_constraint(34)])
690    ),
691    case::field_names_constraint(
692        /* For a schema with field_names constraint as below:
693            { field_names: distinct::symbol }
694        */
695    isl_type::v_2_0::anonymous_type([isl_constraint::v_2_0::field_names(named_type_ref("symbol"), true)]),
696    TypeDefinitionKind::anonymous([Constraint::field_names(5, true), Constraint::type_constraint(34)])
697    ),
698    case::contains_constraint(
699        /* For a schema with contains constraint as below:
700            { contains: [true, 1, "hello"] }
701        */
702        anonymous_type([contains([true.into(), 1.into(), "hello".to_owned().into()])]),
703    TypeDefinitionKind::anonymous([Constraint::contains([true.into(), 1.into(), "hello".to_owned().into()]), Constraint::type_constraint(34)])
704        ),
705    case::container_length_constraint(
706        /* For a schema with container_length constraint as below:
707            { container_length: 3 }
708        */
709        anonymous_type([container_length(3.into())]),
710    TypeDefinitionKind::anonymous([Constraint::container_length(3.into()), Constraint::type_constraint(34)])
711    ),
712    case::byte_length_constraint(
713        /* For a schema with byte_length constraint as below:
714            { byte_length: 3 }
715        */
716        anonymous_type([byte_length(3.into())]),
717    TypeDefinitionKind::anonymous([Constraint::byte_length(3.into()), Constraint::type_constraint(34)])
718    ),
719    case::codepoint_length_constraint(
720        /* For a schema with codepoint_length constraint as below:
721            { codepoint_length: 3 }
722        */
723        anonymous_type([codepoint_length(3.into())]),
724    TypeDefinitionKind::anonymous([Constraint::codepoint_length(3.into()), Constraint::type_constraint(34)])
725    ),
726    case::element_constraint(
727        /* For a schema with element constraint as below:
728            { element: int }
729        */
730        anonymous_type([element(named_type_ref("int"))]),
731    TypeDefinitionKind::anonymous([Constraint::element(0, false), Constraint::type_constraint(34)])
732    ),
733    case::distinct_element_constraint(
734        /* For a schema with distinct element constraint as below:
735            { element: distinct::int }
736        */
737        isl_type::v_2_0::anonymous_type([isl_constraint::v_2_0::element(named_type_ref("int"), true)]),
738    TypeDefinitionKind::anonymous([Constraint::element(0, true), Constraint::type_constraint(34)])
739    ),
740    case::annotations_constraint(
741        /* For a schema with annotations constraint as below:
742            { annotations: closed::[red, blue, green] }
743        */
744        anonymous_type([annotations(vec!["closed"], vec![Symbol::from("red").into(), Symbol::from("blue").into(), Symbol::from("green").into()])]),
745    TypeDefinitionKind::anonymous([Constraint::annotations(vec!["closed"], vec![Symbol::from("red").into(), Symbol::from("blue").into(), Symbol::from("green").into()]), Constraint::type_constraint(34)])
746    ),
747    case::annotations_v2_0_constraint(
748        /* For a schema with annotations constraint as below:
749            { annotations: { container_length: 1 } }
750        */
751        isl_type::v_2_0::anonymous_type([isl_constraint::v_2_0::annotations(isl_type_reference::v_2_0::anonymous_type_ref([isl_constraint::v_2_0::container_length(1.into())]))]),
752        TypeDefinitionKind::anonymous([Constraint::annotations_v2_0(36), Constraint::type_constraint(34)])
753    ),
754    case::precision_constraint(
755        /* For a schema with precision constraint as below:
756            { precision: 3 }
757        */
758        anonymous_type([precision(3.into())]),
759    TypeDefinitionKind::anonymous([Constraint::precision(3.into()), Constraint::type_constraint(34)])
760    ),
761    case::scale_constraint(
762        /* For a schema with scale constraint as below:
763            { scale: 2 }
764        */
765        anonymous_type([scale(2.into())]),
766    TypeDefinitionKind::anonymous([Constraint::scale(2.into()), Constraint::type_constraint(34)])
767    ),
768    case::exponent_constraint(
769        /* For a schema with exponent constraint as below:
770            { exponent: 2 }
771        */
772        isl_type::v_2_0::anonymous_type([isl_constraint::v_2_0::exponent(2.into())]),
773    TypeDefinitionKind::anonymous([Constraint::exponent(2.into()), Constraint::type_constraint(34)])
774    ),
775    case::timestamp_precision_constraint(
776        /* For a schema with timestamp_precision constraint as below:
777            { timestamp_precision: month }
778        */
779        anonymous_type([timestamp_precision(TimestampPrecisionRange::new_single_value(TimestampPrecision::Month))]),
780    TypeDefinitionKind::anonymous([Constraint::timestamp_precision(TimestampPrecision::Month.into()), Constraint::type_constraint(34)])
781    ),
782    case::valid_values_constraint(
783    /* For a schema with valid_values constraint as below:
784        { valid_values: [2, 3.5, 5e7, "hello", hi, range::[2, 3]] }
785    */
786    anonymous_type([valid_values(vec![2.into(), ion_rs::Decimal::new(35, -1).into(), 5e7.into(), "hello".to_owned().into(), Symbol::from("hi").into(), NumberRange::new_inclusive(2.into(), 3.into()).unwrap().into()]).unwrap()]),
787    TypeDefinitionKind::anonymous([Constraint::valid_values(vec![2.into(), ion_rs::Decimal::new(35, -1).into(), 5e7.into(), "hello".to_owned().into(), Symbol::from("hi").into(), NumberRange::new_inclusive(2.into(), 3.into()).unwrap().into()], IslVersion::V1_0).unwrap(), Constraint::type_constraint(34)])
788    ),
789    case::utf8_byte_length_constraint(
790        /* For a schema with utf8_byte_length constraint as below:
791            { utf8_byte_length: 3 }
792        */
793        anonymous_type([utf8_byte_length(3.into())]),
794    TypeDefinitionKind::anonymous([Constraint::utf8_byte_length(3.into()), Constraint::type_constraint(34)])
795    ),
796    case::regex_constraint(
797        /* For a schema with regex constraint as below:
798            { regex: "[abc]" }
799        */
800        anonymous_type(
801            [regex(
802                false, // case insensitive
803                false, // multiline
804                "[abc]".to_string()
805            )]
806        ),
807    TypeDefinitionKind::anonymous([
808            Constraint::regex(
809                false, // case insensitive
810                false, // multiline
811                "[abc]".to_string(),
812                IslVersion::V1_0
813            ).unwrap(),
814            Constraint::type_constraint(34)
815        ])
816    ),
817    case::timestamp_offset_constraint(
818        /* For a schema with timestamp_offset constraint as below:
819            { timestamp_offset: ["-00:00"] }
820        */
821        anonymous_type(
822            [timestamp_offset(vec!["-00:00".try_into().unwrap()])]
823        ),
824    TypeDefinitionKind::anonymous([Constraint::timestamp_offset(vec!["-00:00".try_into().unwrap()]),
825            Constraint::type_constraint(34)
826        ])
827    ),
828    case::ieee754_float_constraint(
829        /* For a schema with ieee754_float constraint as below:
830            { ieee754_float: binary16 }
831        */
832        isl_type::v_2_0::anonymous_type([isl_constraint::v_2_0::ieee754_float(Ieee754InterchangeFormat::Binary16)]),
833        TypeDefinitionKind::anonymous([Constraint::ieee754_float(Ieee754InterchangeFormat::Binary16), Constraint::type_constraint(34)])
834    ),
835    )]
836    fn isl_type_to_type_definition(isl_type: IslType, type_def: TypeDefinitionKind) {
837        // assert if both the TypeDefinitionKind are same in terms of constraints and name
838        let type_store = &mut TypeStore::default();
839        let pending_types = &mut PendingTypes::default();
840        let this_type_def = {
841            let type_id = TypeDefinitionImpl::parse_from_isl_type_and_update_pending_types(
842                IslVersion::V1_0,
843                &isl_type,
844                type_store,
845                pending_types,
846            )
847            .unwrap();
848            pending_types
849                .update_type_store(type_store, None, &HashSet::new())
850                .unwrap();
851            type_store.get_type_by_id(type_id).unwrap()
852        };
853        assert_eq!(this_type_def, &type_def);
854    }
855}