Skip to content

Commit

Permalink
Adds writers for Fields, OrderedElements, and logic constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
popematt committed Oct 12, 2023
1 parent 6030538 commit b046fd7
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ interface Constraint {
* See relevant section in [ISL 1.0 spec](https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#all_of) and
* [ISL 2.0 spec](https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#all_of).
*/
data class AllOf(val types: TypeArguments) : Constraint
data class AllOf(val types: TypeArguments) : Constraint {
constructor(vararg types: TypeArgument) : this(types.toSet())

Check warning on line 24 in ion-schema/src/main/kotlin/com/amazon/ionschema/model/Constraint.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/model/Constraint.kt#L24

Added line #L24 was not covered by tests
}

/**
* Represents the `annotations` constraint for Ion Schema 1.0.
Expand Down Expand Up @@ -54,7 +56,9 @@ interface Constraint {
* See relevant section in [ISL 1.0 spec](https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#any_of) and
* [ISL 2.0 spec](https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#any_of).
*/
data class AnyOf(val types: TypeArguments) : Constraint
data class AnyOf(val types: TypeArguments) : Constraint {
constructor(vararg types: TypeArgument) : this(types.toSet())

Check warning on line 60 in ion-schema/src/main/kotlin/com/amazon/ionschema/model/Constraint.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/model/Constraint.kt#L60

Added line #L60 was not covered by tests
}

/**
* Represents the `byte_length` constraint.
Expand Down Expand Up @@ -152,14 +156,18 @@ interface Constraint {
* See relevant section in [ISL 1.0 spec](https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#one_of) and
* [ISL 2.0 spec](https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#one_of).
*/
data class OneOf(val types: TypeArguments) : Constraint
data class OneOf(val types: TypeArguments) : Constraint {
constructor(vararg types: TypeArgument) : this(types.toSet())

Check warning on line 160 in ion-schema/src/main/kotlin/com/amazon/ionschema/model/Constraint.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/model/Constraint.kt#L160

Added line #L160 was not covered by tests
}

/**
* Represents the `ordered_elements` constraint.
* See relevant section in [ISL 1.0 spec](https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#ordered_elements) and
* [ISL 2.0 spec](https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#ordered_elements).
*/
data class OrderedElements(val types: List<VariablyOccurringTypeArgument>) : Constraint
data class OrderedElements(val types: List<VariablyOccurringTypeArgument>) : Constraint {
constructor(vararg types: VariablyOccurringTypeArgument) : this(types.toList())

Check warning on line 169 in ion-schema/src/main/kotlin/com/amazon/ionschema/model/Constraint.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/model/Constraint.kt#L169

Added line #L169 was not covered by tests
}

/**
* Represents the `precision` constraint.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,23 @@ data class VariablyOccurringTypeArgument(val occurs: DiscreteIntRange, val typeA
val OCCURS_OPTIONAL = DiscreteIntRange(0, 1)
@JvmStatic
val OCCURS_REQUIRED = DiscreteIntRange(1, 1)

@JvmStatic
fun optional(arg: TypeArgument) = VariablyOccurringTypeArgument(OCCURS_OPTIONAL, arg)

@JvmStatic
fun required(arg: TypeArgument) = VariablyOccurringTypeArgument(OCCURS_REQUIRED, arg)
}
}

@ExperimentalIonSchemaModel
fun TypeArgument.optional() = VariablyOccurringTypeArgument.optional(this)

@ExperimentalIonSchemaModel
fun TypeArgument.required() = VariablyOccurringTypeArgument.required(this)

@ExperimentalIonSchemaModel
fun TypeArgument.occurs(n: Int) = VariablyOccurringTypeArgument(DiscreteIntRange(n), this)

Check warning on line 31 in ion-schema/src/main/kotlin/com/amazon/ionschema/model/VariablyOccurringTypeArgument.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/model/VariablyOccurringTypeArgument.kt#L31

Added line #L31 was not covered by tests

@ExperimentalIonSchemaModel
fun TypeArgument.occurs(min: Int?, max: Int?) = VariablyOccurringTypeArgument(DiscreteIntRange(min, max), this)
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ internal interface TypeWriter {
* Writes a [VariablyOccurringTypeArgument] to the given [IonWriter].
*/
fun writeVariablyOccurringTypeArg(ionWriter: IonWriter, varTypeArg: VariablyOccurringTypeArgument, elideOccursValue: DiscreteIntRange)
}

/**
* Writes a [TypeArguments] to the given [IonWriter].
*/
fun writeTypeArguments(ionWriter: IonWriter, typeArgs: TypeArguments) {
ionWriter.writeToList(typeArgs) { writeTypeArg(ionWriter, it) }
}
/**
* Writes a [TypeArguments] to the given [IonWriter].
*/
@ExperimentalIonSchemaModel
internal fun TypeWriter.writeTypeArguments(ionWriter: IonWriter, typeArgs: TypeArguments) {
ionWriter.writeToList(typeArgs) { writeTypeArg(ionWriter, it) }

Check warning on line 38 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/TypeWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/TypeWriter.kt#L38

Added line #L38 was not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazon.ionschema.writer.internal.constraints

import com.amazon.ion.IonWriter
import com.amazon.ionschema.IonSchemaVersion
import com.amazon.ionschema.model.Constraint
import com.amazon.ionschema.model.ExperimentalIonSchemaModel
import com.amazon.ionschema.model.VariablyOccurringTypeArgument.Companion.OCCURS_OPTIONAL
import com.amazon.ionschema.writer.internal.TypeWriter
import com.amazon.ionschema.writer.internal.writeToStruct

@ExperimentalIonSchemaModel
internal class FieldsWriter(private val typeWriter: TypeWriter, private val ionSchemaVersion: IonSchemaVersion) : ConstraintWriter {
override val supportedClasses = setOf(Constraint.Fields::class)

override fun IonWriter.write(c: Constraint) {
check(c is Constraint.Fields)

if (c.closed && ionSchemaVersion == IonSchemaVersion.v1_0) {
setFieldName("content")
writeSymbol("closed")
}

setFieldName("fields")
if (c.closed && ionSchemaVersion != IonSchemaVersion.v1_0) setTypeAnnotations("closed")
writeToStruct(c.fields) {
typeWriter.writeVariablyOccurringTypeArg(this@writeToStruct, it, elideOccursValue = OCCURS_OPTIONAL)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazon.ionschema.writer.internal.constraints

import com.amazon.ion.IonWriter
import com.amazon.ionschema.model.Constraint
import com.amazon.ionschema.model.ExperimentalIonSchemaModel
import com.amazon.ionschema.writer.internal.TypeWriter
import com.amazon.ionschema.writer.internal.writeTypeArguments
import kotlin.reflect.KClass

@ExperimentalIonSchemaModel
internal class LogicConstraintsWriter(private val typeWriter: TypeWriter) : ConstraintWriter {
override val supportedClasses: Set<KClass<out Constraint>> = setOf(
Constraint.AllOf::class,
Constraint.AnyOf::class,
Constraint.Not::class,
Constraint.OneOf::class,
Constraint.Type::class,

Check warning on line 20 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt#L14-L20

Added lines #L14 - L20 were not covered by tests
)

override fun IonWriter.write(c: Constraint) {
when (c) {

Check warning on line 24 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt#L24

Added line #L24 was not covered by tests
is Constraint.AllOf -> {
setFieldName("all_of")
typeWriter.writeTypeArguments(this@write, c.types)

Check warning on line 27 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt#L26-L27

Added lines #L26 - L27 were not covered by tests
}
is Constraint.AnyOf -> {
setFieldName("any_of")
typeWriter.writeTypeArguments(this@write, c.types)

Check warning on line 31 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt#L30-L31

Added lines #L30 - L31 were not covered by tests
}
is Constraint.OneOf -> {
setFieldName("one_of")
typeWriter.writeTypeArguments(this@write, c.types)

Check warning on line 35 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt#L34-L35

Added lines #L34 - L35 were not covered by tests
}
is Constraint.Not -> {
setFieldName("not")
typeWriter.writeTypeArg(this@write, c.type)

Check warning on line 39 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt#L38-L39

Added lines #L38 - L39 were not covered by tests
}
is Constraint.Type -> {
setFieldName("type")
typeWriter.writeTypeArg(this@write, c.type)

Check warning on line 43 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt#L42-L43

Added lines #L42 - L43 were not covered by tests
}
else -> check(false)

Check warning on line 45 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/LogicConstraintsWriter.kt#L45

Added line #L45 was not covered by tests
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazon.ionschema.writer.internal.constraints

import com.amazon.ion.IonWriter
import com.amazon.ionschema.model.Constraint
import com.amazon.ionschema.model.ExperimentalIonSchemaModel
import com.amazon.ionschema.model.VariablyOccurringTypeArgument.Companion.OCCURS_REQUIRED
import com.amazon.ionschema.writer.internal.TypeWriter
import com.amazon.ionschema.writer.internal.writeToList

@ExperimentalIonSchemaModel
internal class OrderedElementsWriter(private val typeWriter: TypeWriter) : ConstraintWriter {
override val supportedClasses = setOf(Constraint.OrderedElements::class)

Check warning on line 15 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/OrderedElementsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/OrderedElementsWriter.kt#L14-L15

Added lines #L14 - L15 were not covered by tests

override fun IonWriter.write(c: Constraint) {
check(c is Constraint.OrderedElements)
setFieldName("ordered_elements")
writeToList(c.types) {
typeWriter.writeVariablyOccurringTypeArg(this, it, elideOccursValue = OCCURS_REQUIRED)
}

Check warning on line 22 in ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/OrderedElementsWriter.kt

View check run for this annotation

Codecov / codecov/patch

ion-schema/src/main/kotlin/com/amazon/ionschema/writer/internal/constraints/OrderedElementsWriter.kt#L19-L22

Added lines #L19 - L22 were not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazon.ionschema.writer.internal.constraints

import com.amazon.ionschema.IonSchemaVersion
import com.amazon.ionschema.model.Constraint
import com.amazon.ionschema.model.ExperimentalIonSchemaModel
import com.amazon.ionschema.model.TypeArgument
import com.amazon.ionschema.model.occurs
import com.amazon.ionschema.model.optional
import org.junit.jupiter.api.Test

@OptIn(ExperimentalIonSchemaModel::class)
class FieldsWriterTest : ConstraintTestBase(
writer = FieldsWriter(stubTypeWriterWithRefs("foo_type", "bar_type"), IonSchemaVersion.v2_0),
expectedConstraints = setOf(Constraint.Fields::class),
writeTestCases = listOf(
Constraint.Fields(fieldsMap, closed = true) to "fields: closed::{ a: foo_type, b: bar_type }",
Constraint.Fields(fieldsMap, closed = false) to "fields: { a: foo_type, b: bar_type }",
)
) {
companion object {
private val fieldsMap = mapOf(
"a" to TypeArgument.Reference("foo_type").optional(),
"b" to TypeArgument.Reference("bar_type").occurs(0, 1),
)
}

@Test
fun `writer should write content closed for v1_0`() {
val writer = FieldsWriter(stubTypeWriterWithRefs("foo_type", "bar_type"), IonSchemaVersion.v1_0)
runWriteCase(
writer,
Constraint.Fields(fieldsMap, closed = true) to "content: closed, fields: { a: foo_type, b: bar_type }"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazon.ionschema.writer.internal.constraints

import com.amazon.ionschema.model.Constraint
import com.amazon.ionschema.model.ExperimentalIonSchemaModel
import com.amazon.ionschema.model.TypeArgument.Reference

@OptIn(ExperimentalIonSchemaModel::class)
class LogicConstraintsWriterTest : ConstraintTestBase(
writer = LogicConstraintsWriter(stubTypeWriterWithRefs("foo", "bar")),
expectedConstraints = setOf(
Constraint.AllOf::class,
Constraint.AnyOf::class,
Constraint.OneOf::class,
Constraint.Not::class,
Constraint.Type::class,
),
writeTestCases = listOf(
Constraint.AllOf(emptySet()) to "all_of: []",
Constraint.AllOf(Reference("foo")) to "all_of: [foo]",
Constraint.AllOf(Reference("foo"), Reference("bar")) to "all_of: [foo, bar]",
Constraint.AnyOf(emptySet()) to "any_of: []",
Constraint.AnyOf(Reference("foo")) to "any_of: [foo]",
Constraint.AnyOf(Reference("foo"), Reference("bar")) to "any_of: [foo, bar]",
Constraint.OneOf(emptySet()) to "one_of: []",
Constraint.OneOf(Reference("foo")) to "one_of: [foo]",
Constraint.OneOf(Reference("foo"), Reference("bar")) to "one_of: [foo, bar]",
Constraint.Not(Reference("foo")) to "not: foo",
Constraint.Type(Reference("foo")) to "type: foo",
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazon.ionschema.writer.internal.constraints

import com.amazon.ionschema.model.Constraint
import com.amazon.ionschema.model.ExperimentalIonSchemaModel
import com.amazon.ionschema.model.TypeArgument.Reference
import com.amazon.ionschema.model.required

@OptIn(ExperimentalIonSchemaModel::class)
class OrderedElementsWriterTest : ConstraintTestBase(
writer = OrderedElementsWriter(stubTypeWriterWithRefs("foo", "bar")),
expectedConstraints = setOf(Constraint.OrderedElements::class),
writeTestCases = listOf(
Constraint.OrderedElements(emptyList()) to "ordered_elements: []",
Constraint.OrderedElements(Reference("foo").required()) to "ordered_elements: [foo]",
Constraint.OrderedElements(
Reference("foo").required(),
Reference("bar").required(),
Reference("foo").required(),
) to "ordered_elements: [foo, bar, foo]",
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,37 @@

package com.amazon.ionschema.writer.internal.constraints

import com.amazon.ion.IonWriter
import com.amazon.ion.system.IonSystemBuilder
import com.amazon.ionschema.model.ExperimentalIonSchemaModel
import com.amazon.ionschema.model.TypeArgument
import com.amazon.ionschema.model.optional
import com.amazon.ionschema.model.required
import com.amazon.ionschema.writer.internal.TypeWriter
import io.mockk.every
import io.mockk.mockk

private val ION = IonSystemBuilder.standard().build()

/** Helper fun for creating IonValue instances */
fun ion(text: String) = ION.singleValue(text)

/**
* Creates a mock TypeWriter that can write the given type names.
* This is a mock, though, and it's functionality is not complete. Does not write nullability annotations.
* Does not respect the "occurs" value of any [VariablyOccurringTypeArgument]s.
*/
@OptIn(ExperimentalIonSchemaModel::class)
internal fun stubTypeWriterWithRefs(vararg refs: String) = mockk<TypeWriter> {
refs.forEach { ref ->
every { writeTypeArg(any(), TypeArgument.Reference(ref)) } answers {
(it.invocation.args[0] as IonWriter).writeSymbol(ref)
}
every { writeVariablyOccurringTypeArg(any(), TypeArgument.Reference(ref).optional(), any()) } answers {
(it.invocation.args[0] as IonWriter).writeSymbol(ref)
}
every { writeVariablyOccurringTypeArg(any(), TypeArgument.Reference(ref).required(), any()) } answers {
(it.invocation.args[0] as IonWriter).writeSymbol(ref)
}
}
}

0 comments on commit b046fd7

Please sign in to comment.