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 type_name.is_some() {
507            // update with this resolved type_def to context for type_id
508            actual_type_id = pending_types.update_named_type(
509                type_id,
510                type_name.as_ref().unwrap(),
511                type_def,
512                type_store,
513            );
514
515            // clear parent information from type_store as the type is already added in the type_store now
516            pending_types.clear_parent();
517        } else {
518            // update with this resolved type_def to context for type_id
519            actual_type_id = pending_types.update_anonymous_type(type_id, type_def, type_store);
520        }
521
522        Ok(actual_type_id)
523    }
524}
525
526impl PartialEq for TypeDefinitionImpl {
527    fn eq(&self, other: &Self) -> bool {
528        self.name() == other.name() && self.constraints == other.constraints()
529    }
530}
531
532impl TypeValidator for TypeDefinitionImpl {
533    fn is_valid(
534        &self,
535        value: &IonSchemaElement,
536        type_store: &TypeStore,
537        ion_path: &mut IonPath,
538    ) -> bool {
539        let violation = self.validate(value, type_store, ion_path);
540        violation.is_ok()
541    }
542
543    fn validate(
544        &self,
545        value: &IonSchemaElement,
546        type_store: &TypeStore,
547        ion_path: &mut IonPath,
548    ) -> ValidationResult {
549        let mut violations: Vec<Violation> = vec![];
550        let type_name = match self.name() {
551            None => match self.isl_type_struct.as_ref() {
552                None => "".to_owned(),
553                Some(anonymous_struct) => {
554                    format!("{anonymous_struct}")
555                }
556            },
557            Some(name) => name.to_owned(),
558        };
559        for constraint in self.constraints() {
560            if let Err(violation) = constraint.validate(value, type_store, ion_path) {
561                violations.push(violation);
562            }
563        }
564        if violations.is_empty() {
565            return Ok(());
566        }
567        Err(Violation::with_violations(
568            type_name,
569            ViolationCode::TypeConstraintsUnsatisfied,
570            "value didn't satisfy type constraint(s)",
571            ion_path,
572            violations,
573        ))
574    }
575}
576
577impl Display for TypeDefinitionImpl {
578    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
579        let type_def_name = match &self.name {
580            None => match &self.isl_type_struct {
581                None => "".to_owned(),
582                Some(type_name) => format!("{type_name}"),
583            },
584            Some(type_name) => type_name.to_owned(),
585        };
586
587        write!(f, "{type_def_name}")
588    }
589}
590
591#[cfg(test)]
592mod type_definition_tests {
593    use super::*;
594    use crate::constraint::Constraint;
595    use crate::isl::isl_constraint::v_1_0::*;
596    use crate::isl::isl_type::v_1_0::*;
597    use crate::isl::isl_type::IslType;
598    use crate::isl::isl_type_reference::v_1_0::*;
599    use crate::isl::ranges::*;
600    use crate::isl::util::Ieee754InterchangeFormat;
601    use crate::isl::util::TimestampPrecision;
602    use crate::isl::*;
603    use crate::system::PendingTypes;
604
605    use rstest::*;
606    use std::collections::HashSet;
607
608    // TODO: Remove type ids for assertion to make tests more readable
609    #[rstest(
610    isl_type, type_def,
611    case::type_constraint_with_anonymous_type(
612        /* For a schema with single anonymous type as below:
613            { type: int }
614         */
615        anonymous_type([type_constraint(named_type_ref("int"))]),
616    TypeDefinitionKind::anonymous([Constraint::type_constraint(0)])
617    ),
618    case::type_constraint_with_named_type(
619        /* For a schema with named type as below:
620            { name: my_int, type: int }
621         */
622        named_type("my_int", [type_constraint(named_type_ref("int"))]),
623    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(0)])
624    ),
625    case::type_constraint_with_self_reference_type(
626        /* For a schema with self reference type as below:
627            { name: my_int, type: my_int }
628         */
629        named_type("my_int", [type_constraint(named_type_ref("my_int"))]),
630    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(35)])
631    ),
632    case::type_constraint_with_nested_self_reference_type(
633        /* For a schema with nested self reference type as below:
634            { name: my_int, type: { type: my_int } }
635         */
636        named_type("my_int", [type_constraint(anonymous_type_ref([type_constraint(named_type_ref("my_int"))]))]),
637    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(36)]) // 0-35 are built-in types which are preloaded to the type_store
638    ),
639    case::type_constraint_with_nested_type(
640        /* For a schema with nested types as below:
641            { name: my_int, type: { type: int } }
642         */
643        named_type("my_int", [type_constraint(anonymous_type_ref([type_constraint(named_type_ref("int"))]))]),
644    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(36)])
645    ),
646    case::type_constraint_with_nested_multiple_types(
647        /* For a schema with nested multiple types as below:
648            { name: my_int, type: { type: int }, type: { type: my_int } }
649         */
650        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"))]))]),
651    TypeDefinitionKind::named("my_int", [Constraint::type_constraint(36), Constraint::type_constraint(37)])
652    ),
653    case::all_of_constraint(
654        /* For a schema with all_of type as below:
655            { all_of: [{ type: int }] }
656        */
657        anonymous_type([all_of([anonymous_type_ref([type_constraint(named_type_ref("int"))])])]),
658    TypeDefinitionKind::anonymous([Constraint::all_of([36]), Constraint::type_constraint(34)])
659    ),
660    case::any_of_constraint(
661        /* For a schema with any_of constraint as below:
662            { any_of: [{ type: int }, { type: decimal }] }
663        */
664        anonymous_type([any_of([anonymous_type_ref([type_constraint(named_type_ref("int"))]), anonymous_type_ref([type_constraint(named_type_ref("decimal"))])])]),
665    TypeDefinitionKind::anonymous([Constraint::any_of([36, 37]), Constraint::type_constraint(34)])
666    ),
667    case::one_of_constraint(
668        /* For a schema with one_of constraint as below:
669            { any_of: [{ type: int }, { type: decimal }] }
670        */
671        anonymous_type([one_of([anonymous_type_ref([type_constraint(named_type_ref("int"))]), anonymous_type_ref([type_constraint(named_type_ref("decimal"))])])]),
672    TypeDefinitionKind::anonymous([Constraint::one_of([36, 37]), Constraint::type_constraint(34)])
673    ),
674    case::not_constraint(
675        /* For a schema with not constraint as below:
676            { not: { type: int } }
677        */
678        anonymous_type([not(anonymous_type_ref([type_constraint(named_type_ref("int"))]))]),
679    TypeDefinitionKind::anonymous([Constraint::not(36), Constraint::type_constraint(34)])
680    ),
681    case::ordered_elements_constraint(
682        /* For a schema with ordered_elements constraint as below:
683            { ordered_elements: [ symbol, { type: int }, ] }
684        */
685        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))])]),
686    TypeDefinitionKind::anonymous([Constraint::ordered_elements([5, 36]), Constraint::type_constraint(34)])
687    ),
688    case::fields_constraint(
689        /* For a schema with fields constraint as below:
690            { fields: { name: string, id: int} }
691        */
692        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())]),
693    TypeDefinitionKind::anonymous([Constraint::fields(vec![("name".to_owned(), 4), ("id".to_owned(), 0)].into_iter()), Constraint::type_constraint(34)])
694    ),
695    case::field_names_constraint(
696        /* For a schema with field_names constraint as below:
697            { field_names: distinct::symbol }
698        */
699    isl_type::v_2_0::anonymous_type([isl_constraint::v_2_0::field_names(named_type_ref("symbol"), true)]),
700    TypeDefinitionKind::anonymous([Constraint::field_names(5, true), Constraint::type_constraint(34)])
701    ),
702    case::contains_constraint(
703        /* For a schema with contains constraint as below:
704            { contains: [true, 1, "hello"] }
705        */
706        anonymous_type([contains([true.into(), 1.into(), "hello".to_owned().into()])]),
707    TypeDefinitionKind::anonymous([Constraint::contains([true.into(), 1.into(), "hello".to_owned().into()]), Constraint::type_constraint(34)])
708        ),
709    case::container_length_constraint(
710        /* For a schema with container_length constraint as below:
711            { container_length: 3 }
712        */
713        anonymous_type([container_length(3.into())]),
714    TypeDefinitionKind::anonymous([Constraint::container_length(3.into()), Constraint::type_constraint(34)])
715    ),
716    case::byte_length_constraint(
717        /* For a schema with byte_length constraint as below:
718            { byte_length: 3 }
719        */
720        anonymous_type([byte_length(3.into())]),
721    TypeDefinitionKind::anonymous([Constraint::byte_length(3.into()), Constraint::type_constraint(34)])
722    ),
723    case::codepoint_length_constraint(
724        /* For a schema with codepoint_length constraint as below:
725            { codepoint_length: 3 }
726        */
727        anonymous_type([codepoint_length(3.into())]),
728    TypeDefinitionKind::anonymous([Constraint::codepoint_length(3.into()), Constraint::type_constraint(34)])
729    ),
730    case::element_constraint(
731        /* For a schema with element constraint as below:
732            { element: int }
733        */
734        anonymous_type([element(named_type_ref("int"))]),
735    TypeDefinitionKind::anonymous([Constraint::element(0, false), Constraint::type_constraint(34)])
736    ),
737    case::distinct_element_constraint(
738        /* For a schema with distinct element constraint as below:
739            { element: distinct::int }
740        */
741        isl_type::v_2_0::anonymous_type([isl_constraint::v_2_0::element(named_type_ref("int"), true)]),
742    TypeDefinitionKind::anonymous([Constraint::element(0, true), Constraint::type_constraint(34)])
743    ),
744    case::annotations_constraint(
745        /* For a schema with annotations constraint as below:
746            { annotations: closed::[red, blue, green] }
747        */
748        anonymous_type([annotations(vec!["closed"], vec![Symbol::from("red").into(), Symbol::from("blue").into(), Symbol::from("green").into()])]),
749    TypeDefinitionKind::anonymous([Constraint::annotations(vec!["closed"], vec![Symbol::from("red").into(), Symbol::from("blue").into(), Symbol::from("green").into()]), Constraint::type_constraint(34)])
750    ),
751    case::annotations_v2_0_constraint(
752        /* For a schema with annotations constraint as below:
753            { annotations: { container_length: 1 } }
754        */
755        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())]))]),
756        TypeDefinitionKind::anonymous([Constraint::annotations_v2_0(36), Constraint::type_constraint(34)])
757    ),
758    case::precision_constraint(
759        /* For a schema with precision constraint as below:
760            { precision: 3 }
761        */
762        anonymous_type([precision(3.into())]),
763    TypeDefinitionKind::anonymous([Constraint::precision(3.into()), Constraint::type_constraint(34)])
764    ),
765    case::scale_constraint(
766        /* For a schema with scale constraint as below:
767            { scale: 2 }
768        */
769        anonymous_type([scale(2.into())]),
770    TypeDefinitionKind::anonymous([Constraint::scale(2.into()), Constraint::type_constraint(34)])
771    ),
772    case::exponent_constraint(
773        /* For a schema with exponent constraint as below:
774            { exponent: 2 }
775        */
776        isl_type::v_2_0::anonymous_type([isl_constraint::v_2_0::exponent(2.into())]),
777    TypeDefinitionKind::anonymous([Constraint::exponent(2.into()), Constraint::type_constraint(34)])
778    ),
779    case::timestamp_precision_constraint(
780        /* For a schema with timestamp_precision constraint as below:
781            { timestamp_precision: month }
782        */
783        anonymous_type([timestamp_precision(TimestampPrecisionRange::new_single_value(TimestampPrecision::Month))]),
784    TypeDefinitionKind::anonymous([Constraint::timestamp_precision(TimestampPrecision::Month.into()), Constraint::type_constraint(34)])
785    ),
786    case::valid_values_constraint(
787    /* For a schema with valid_values constraint as below:
788        { valid_values: [2, 3.5, 5e7, "hello", hi, range::[2, 3]] }
789    */
790    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()]),
791    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)])
792    ),
793    case::utf8_byte_length_constraint(
794        /* For a schema with utf8_byte_length constraint as below:
795            { utf8_byte_length: 3 }
796        */
797        anonymous_type([utf8_byte_length(3.into())]),
798    TypeDefinitionKind::anonymous([Constraint::utf8_byte_length(3.into()), Constraint::type_constraint(34)])
799    ),
800    case::regex_constraint(
801        /* For a schema with regex constraint as below:
802            { regex: "[abc]" }
803        */
804        anonymous_type(
805            [regex(
806                false, // case insensitive
807                false, // multiline
808                "[abc]".to_string()
809            )]
810        ),
811    TypeDefinitionKind::anonymous([
812            Constraint::regex(
813                false, // case insensitive
814                false, // multiline
815                "[abc]".to_string(),
816                IslVersion::V1_0
817            ).unwrap(),
818            Constraint::type_constraint(34)
819        ])
820    ),
821    case::timestamp_offset_constraint(
822        /* For a schema with timestamp_offset constraint as below:
823            { timestamp_offset: ["-00:00"] }
824        */
825        anonymous_type(
826            [timestamp_offset(vec!["-00:00".try_into().unwrap()])]
827        ),
828    TypeDefinitionKind::anonymous([Constraint::timestamp_offset(vec!["-00:00".try_into().unwrap()]),
829            Constraint::type_constraint(34)
830        ])
831    ),
832    case::ieee754_float_constraint(
833        /* For a schema with ieee754_float constraint as below:
834            { ieee754_float: binary16 }
835        */
836        isl_type::v_2_0::anonymous_type([isl_constraint::v_2_0::ieee754_float(Ieee754InterchangeFormat::Binary16)]),
837        TypeDefinitionKind::anonymous([Constraint::ieee754_float(Ieee754InterchangeFormat::Binary16), Constraint::type_constraint(34)])
838    ),
839    )]
840    fn isl_type_to_type_definition(isl_type: IslType, type_def: TypeDefinitionKind) {
841        // assert if both the TypeDefinitionKind are same in terms of constraints and name
842        let type_store = &mut TypeStore::default();
843        let pending_types = &mut PendingTypes::default();
844        let this_type_def = {
845            let type_id = TypeDefinitionImpl::parse_from_isl_type_and_update_pending_types(
846                IslVersion::V1_0,
847                &isl_type,
848                type_store,
849                pending_types,
850            )
851            .unwrap();
852            pending_types
853                .update_type_store(type_store, None, &HashSet::new())
854                .unwrap();
855            type_store.get_type_by_id(type_id).unwrap()
856        };
857        assert_eq!(this_type_def, &type_def);
858    }
859}