From a2dff2bcba2eb19adafae4f3e8e38d91c743bfa5 Mon Sep 17 00:00:00 2001 From: David Hoepelman <992153+dhoepelman@users.noreply.github.com> Date: Wed, 13 Nov 2024 08:47:40 +0100 Subject: [PATCH] Move io.konform.validation.jsonschema to io.konform.validation.constraints (#165) --- api/konform.api | 50 +++- .../validation/constraints/AnyConstraints.kt | 7 + .../validation/constraints/EnumConstraints.kt | 22 ++ .../constraints/IterableConstraints.kt | 34 +++ .../validation/constraints/MapConstraints.kt | 25 ++ .../constraints/NumberConstraints.kt | 38 +++ .../constraints/StringConstraints.kt | 44 ++++ .../validation/jsonschema/JsonSchema.kt | 216 ++++++++++-------- .../string/ValidationBuilderString.kt | 6 - .../konform/validation/ListValidationTest.kt | 2 +- .../konform/validation/ReadmeExampleTest.kt | 14 +- .../ConstraintsTest.kt} | 55 ++--- .../validationbuilder/InstanceOfTest.kt | 2 +- .../validation/jsonschema/package-info.java | 5 + 14 files changed, 384 insertions(+), 136 deletions(-) create mode 100644 src/commonMain/kotlin/io/konform/validation/constraints/AnyConstraints.kt create mode 100644 src/commonMain/kotlin/io/konform/validation/constraints/EnumConstraints.kt create mode 100644 src/commonMain/kotlin/io/konform/validation/constraints/IterableConstraints.kt create mode 100644 src/commonMain/kotlin/io/konform/validation/constraints/MapConstraints.kt create mode 100644 src/commonMain/kotlin/io/konform/validation/constraints/NumberConstraints.kt create mode 100644 src/commonMain/kotlin/io/konform/validation/constraints/StringConstraints.kt delete mode 100644 src/commonMain/kotlin/io/konform/validation/string/ValidationBuilderString.kt rename src/commonTest/kotlin/io/konform/validation/{JSONSchemaStyleConstraintsTest.kt => constraints/ConstraintsTest.kt} (90%) create mode 100644 src/jvmMain/kotlin/io/konform/validation/jsonschema/package-info.java diff --git a/api/konform.api b/api/konform.api index 816c64b..9e4fc24 100644 --- a/api/konform.api +++ b/api/konform.api @@ -109,14 +109,60 @@ public abstract class io/konform/validation/ValidationResult { public final fun map (Lkotlin/jvm/functions/Function1;)Lio/konform/validation/ValidationResult; } +public final class io/konform/validation/constraints/EnumConstraintsKt { + public static final fun const (Lio/konform/validation/ValidationBuilder;Ljava/lang/Object;)Lio/konform/validation/Constraint; + public static final fun enum (Lio/konform/validation/ValidationBuilder;[Ljava/lang/Object;)Lio/konform/validation/Constraint; +} + +public final class io/konform/validation/constraints/IterableConstraintsKt { + public static final fun arrayMaxItems (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun arrayMinItems (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun arrayUniqueItems (Lio/konform/validation/ValidationBuilder;Z)Lio/konform/validation/Constraint; + public static synthetic fun arrayUniqueItems$default (Lio/konform/validation/ValidationBuilder;ZILjava/lang/Object;)Lio/konform/validation/Constraint; + public static final fun maxItems (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun minItems (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun uniqueItems (Lio/konform/validation/ValidationBuilder;Z)Lio/konform/validation/Constraint; + public static synthetic fun uniqueItems$default (Lio/konform/validation/ValidationBuilder;ZILjava/lang/Object;)Lio/konform/validation/Constraint; +} + +public final class io/konform/validation/constraints/MapConstraintsKt { + public static final fun maxItems (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun maxProperties (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun minItems (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun minProperties (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun uniqueItems (Lio/konform/validation/ValidationBuilder;Z)Lio/konform/validation/Constraint; + public static synthetic fun uniqueItems$default (Lio/konform/validation/ValidationBuilder;ZILjava/lang/Object;)Lio/konform/validation/Constraint; +} + +public final class io/konform/validation/constraints/NumberConstraintsKt { + public static final fun exclusiveMaximum (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; + public static final fun exclusiveMinimum (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; + public static final fun maximum (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; + public static final fun minimum (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; + public static final fun multipleOf (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; +} + +public final class io/konform/validation/constraints/StringConstraintsKt { + public static final fun containsPattern (Lio/konform/validation/ValidationBuilder;Ljava/lang/String;)Lio/konform/validation/Constraint; + public static final fun containsPattern (Lio/konform/validation/ValidationBuilder;Lkotlin/text/Regex;)Lio/konform/validation/Constraint; + public static final fun maxLength (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun minLength (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun notBlank (Lio/konform/validation/ValidationBuilder;)Lio/konform/validation/Constraint; + public static final fun pattern (Lio/konform/validation/ValidationBuilder;Ljava/lang/String;)Lio/konform/validation/Constraint; + public static final fun pattern (Lio/konform/validation/ValidationBuilder;Lkotlin/text/Regex;)Lio/konform/validation/Constraint; + public static final fun uuid (Lio/konform/validation/ValidationBuilder;)Lio/konform/validation/Constraint; +} + public final class io/konform/validation/jsonschema/JsonSchemaKt { public static final fun const (Lio/konform/validation/ValidationBuilder;Ljava/lang/Object;)Lio/konform/validation/Constraint; public static final fun enum (Lio/konform/validation/ValidationBuilder;[Ljava/lang/Object;)Lio/konform/validation/Constraint; public static final fun exclusiveMaximum (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; public static final fun exclusiveMinimum (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; public static final fun maxLength (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun maxProperties (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; public static final fun maximum (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; public static final fun minLength (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; + public static final fun minProperties (Lio/konform/validation/ValidationBuilder;I)Lio/konform/validation/Constraint; public static final fun minimum (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; public static final fun multipleOf (Lio/konform/validation/ValidationBuilder;Ljava/lang/Number;)Lio/konform/validation/Constraint; public static final fun pattern (Lio/konform/validation/ValidationBuilder;Ljava/lang/String;)Lio/konform/validation/Constraint; @@ -140,10 +186,6 @@ public final class io/konform/validation/path/ValidationPath$Companion { public final fun fromAny ([Ljava/lang/Object;)Lio/konform/validation/path/ValidationPath; } -public final class io/konform/validation/string/ValidationBuilderStringKt { - public static final fun notBlank (Lio/konform/validation/ValidationBuilder;)Lio/konform/validation/Constraint; -} - public final class io/konform/validation/types/EmptyValidation : io/konform/validation/Validation { public static final field INSTANCE Lio/konform/validation/types/EmptyValidation; public fun equals (Ljava/lang/Object;)Z diff --git a/src/commonMain/kotlin/io/konform/validation/constraints/AnyConstraints.kt b/src/commonMain/kotlin/io/konform/validation/constraints/AnyConstraints.kt new file mode 100644 index 0000000..6a13c39 --- /dev/null +++ b/src/commonMain/kotlin/io/konform/validation/constraints/AnyConstraints.kt @@ -0,0 +1,7 @@ +package io.konform.validation.constraints + +import io.konform.validation.Constraint +import io.konform.validation.ValidationBuilder + +public inline fun ValidationBuilder<*>.type(): Constraint<*> = + addConstraint("must be of type '${T::class.simpleName}'") { it is T } diff --git a/src/commonMain/kotlin/io/konform/validation/constraints/EnumConstraints.kt b/src/commonMain/kotlin/io/konform/validation/constraints/EnumConstraints.kt new file mode 100644 index 0000000..ffc29c1 --- /dev/null +++ b/src/commonMain/kotlin/io/konform/validation/constraints/EnumConstraints.kt @@ -0,0 +1,22 @@ +package io.konform.validation.constraints + +import io.konform.validation.Constraint +import io.konform.validation.ValidationBuilder + +/** Restrict a value to a set of allowed values. */ +public fun ValidationBuilder.enum(vararg allowed: T): Constraint { + val set = allowed.toSet() + return addConstraint("must be one of: ${set.joinToString("', '", "'", "'")}") { + it in allowed + } +} + +/** Restrict a [String] to the entry names of an [Enum]. */ +public inline fun > ValidationBuilder.enum(): Constraint { + val enumNames = enumValues().mapTo(mutableSetOf()) { it.name } + return addConstraint("must be one of: ${enumNames.joinToString("', '", "'", "'")}") { + it in enumNames + } +} + +public fun ValidationBuilder.const(expected: T): Constraint = addConstraint("must be '$expected'") { expected == it } diff --git a/src/commonMain/kotlin/io/konform/validation/constraints/IterableConstraints.kt b/src/commonMain/kotlin/io/konform/validation/constraints/IterableConstraints.kt new file mode 100644 index 0000000..02fffe1 --- /dev/null +++ b/src/commonMain/kotlin/io/konform/validation/constraints/IterableConstraints.kt @@ -0,0 +1,34 @@ +package io.konform.validation.constraints + +import io.konform.validation.Constraint +import io.konform.validation.ValidationBuilder +import kotlin.jvm.JvmName + +public fun > ValidationBuilder.minItems(minSize: Int): Constraint = + addConstraint("must have at least {0} items", minSize.toString()) { it.count() >= minSize } + +@JvmName("arrayMinItems") +public fun ValidationBuilder>.minItems(minSize: Int): Constraint> = + addConstraint("must have at least {0} items", minSize.toString()) { it.count() >= minSize } + +public fun > ValidationBuilder.maxItems(maxSize: Int): Constraint = + addConstraint("must have at most {0} items", maxSize.toString()) { + it.count() <= maxSize + } + +@JvmName("arrayMaxItems") +public fun ValidationBuilder>.maxItems(maxSize: Int): Constraint> = + addConstraint("must have at most {0} items", maxSize.toString()) { + it.count() <= maxSize + } + +public fun > ValidationBuilder.uniqueItems(unique: Boolean = true): Constraint = + addConstraint("all items must be unique") { + !unique || it.distinct().count() == it.count() + } + +@JvmName("arrayUniqueItems") +public fun ValidationBuilder>.uniqueItems(unique: Boolean = true): Constraint> = + addConstraint("all items must be unique") { + !unique || it.distinct().count() == it.count() + } diff --git a/src/commonMain/kotlin/io/konform/validation/constraints/MapConstraints.kt b/src/commonMain/kotlin/io/konform/validation/constraints/MapConstraints.kt new file mode 100644 index 0000000..c060ce6 --- /dev/null +++ b/src/commonMain/kotlin/io/konform/validation/constraints/MapConstraints.kt @@ -0,0 +1,25 @@ +package io.konform.validation.constraints + +import io.konform.validation.Constraint +import io.konform.validation.ValidationBuilder + +public fun > ValidationBuilder.minItems(minSize: Int): Constraint = + addConstraint("must have at least {0} items", minSize.toString()) { + it.count() >= minSize + } + +public fun > ValidationBuilder.maxItems(maxSize: Int): Constraint = + addConstraint("must have at most {0} items", maxSize.toString()) { + it.count() <= maxSize + } + +public fun > ValidationBuilder.minProperties(minSize: Int): Constraint = + minItems(minSize) hint "must have at least {0} properties" + +public fun > ValidationBuilder.maxProperties(maxSize: Int): Constraint = + maxItems(maxSize) hint "must have at most {0} properties" + +public fun > ValidationBuilder.uniqueItems(unique: Boolean = true): Constraint = + addConstraint("all items must be unique") { + !unique || it.values.distinct().count() == it.count() + } diff --git a/src/commonMain/kotlin/io/konform/validation/constraints/NumberConstraints.kt b/src/commonMain/kotlin/io/konform/validation/constraints/NumberConstraints.kt new file mode 100644 index 0000000..c51e2fb --- /dev/null +++ b/src/commonMain/kotlin/io/konform/validation/constraints/NumberConstraints.kt @@ -0,0 +1,38 @@ +package io.konform.validation.constraints + +import io.konform.validation.Constraint +import io.konform.validation.ValidationBuilder +import kotlin.math.roundToInt + +public fun ValidationBuilder.multipleOf(factor: Number): Constraint { + val factorAsDouble = factor.toDouble() + require(factorAsDouble > 0) { "multipleOf requires the factor to be strictly larger than 0" } + return addConstraint("must be a multiple of '{0}'", factor.toString()) { + val division = it.toDouble() / factorAsDouble + division.compareTo(division.roundToInt()) == 0 + } +} + +public fun ValidationBuilder.maximum(maximumInclusive: Number): Constraint = + addConstraint( + "must be at most '{0}'", + maximumInclusive.toString(), + ) { it.toDouble() <= maximumInclusive.toDouble() } + +public fun ValidationBuilder.exclusiveMaximum(maximumExclusive: Number): Constraint = + addConstraint( + "must be less than '{0}'", + maximumExclusive.toString(), + ) { it.toDouble() < maximumExclusive.toDouble() } + +public fun ValidationBuilder.minimum(minimumInclusive: Number): Constraint = + addConstraint( + "must be at least '{0}'", + minimumInclusive.toString(), + ) { it.toDouble() >= minimumInclusive.toDouble() } + +public fun ValidationBuilder.exclusiveMinimum(minimumExclusive: Number): Constraint = + addConstraint( + "must be greater than '{0}'", + minimumExclusive.toString(), + ) { it.toDouble() > minimumExclusive.toDouble() } diff --git a/src/commonMain/kotlin/io/konform/validation/constraints/StringConstraints.kt b/src/commonMain/kotlin/io/konform/validation/constraints/StringConstraints.kt new file mode 100644 index 0000000..15457f4 --- /dev/null +++ b/src/commonMain/kotlin/io/konform/validation/constraints/StringConstraints.kt @@ -0,0 +1,44 @@ +package io.konform.validation.constraints + +import io.konform.validation.Constraint +import io.konform.validation.ValidationBuilder + +public fun ValidationBuilder.notBlank(): Constraint = addConstraint("must not be blank") { it.isNotBlank() } + +/** + * Checks that the string contains a match with the given [Regex]. + * */ +public fun ValidationBuilder.containsPattern(pattern: Regex): Constraint = + addConstraint("must include regex '$pattern'") { + it.contains(pattern) + } + +public fun ValidationBuilder.containsPattern(pattern: String): Constraint = containsPattern(pattern.toRegex()) + +public fun ValidationBuilder.minLength(length: Int): Constraint { + require(length >= 0) { IllegalArgumentException("minLength requires the length to be >= 0") } + return addConstraint( + "must have at least {0} characters", + length.toString(), + ) { it.length >= length } +} + +public fun ValidationBuilder.maxLength(length: Int): Constraint { + require(length >= 0) { IllegalArgumentException("maxLength requires the length to be >= 0") } + return addConstraint( + "must have at most {0} characters", + length.toString(), + ) { it.length <= length } +} + +public fun ValidationBuilder.pattern(pattern: String): Constraint = pattern(pattern.toRegex()) + +/** Enforces the string must be UUID hex format. */ +public fun ValidationBuilder.uuid(): Constraint = + pattern("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") hint "must be a valid UUID string" + +public fun ValidationBuilder.pattern(pattern: Regex): Constraint = + addConstraint( + "must match the expected pattern", + pattern.toString(), + ) { it.matches(pattern) } diff --git a/src/commonMain/kotlin/io/konform/validation/jsonschema/JsonSchema.kt b/src/commonMain/kotlin/io/konform/validation/jsonschema/JsonSchema.kt index b73f800..7565a47 100644 --- a/src/commonMain/kotlin/io/konform/validation/jsonschema/JsonSchema.kt +++ b/src/commonMain/kotlin/io/konform/validation/jsonschema/JsonSchema.kt @@ -2,94 +2,112 @@ package io.konform.validation.jsonschema import io.konform.validation.Constraint import io.konform.validation.ValidationBuilder -import kotlin.math.roundToInt - -public inline fun ValidationBuilder<*>.type(): Constraint<*> = - addConstraint( - "must be of the correct type", - ) { it is T } - -public fun ValidationBuilder.enum(vararg allowed: T): Constraint = - addConstraint( - "must be one of: {0}", - allowed.joinToString("', '", "'", "'"), - ) { it in allowed } - -public inline fun > ValidationBuilder.enum(): Constraint { - val enumNames = enumValues().map { it.name } - return addConstraint( - "must be one of: {0}", - enumNames.joinToString("', '", "'", "'"), - ) { it in enumNames } -} - -public fun ValidationBuilder.const(expected: T): Constraint = - addConstraint( - "must be {0}", - expected?.let { "'$it'" } ?: "null", - ) { expected == it } - -public fun ValidationBuilder.multipleOf(factor: Number): Constraint { - val factorAsDouble = factor.toDouble() - require(factorAsDouble > 0) { "multipleOf requires the factor to be strictly larger than 0" } - return addConstraint("must be a multiple of '{0}'", factor.toString()) { - val division = it.toDouble() / factorAsDouble - division.compareTo(division.roundToInt()) == 0 - } -} - -public fun ValidationBuilder.maximum(maximumInclusive: Number): Constraint = - addConstraint( - "must be at most '{0}'", - maximumInclusive.toString(), - ) { it.toDouble() <= maximumInclusive.toDouble() } - +import io.konform.validation.constraints.const as movedConst +import io.konform.validation.constraints.enum as movedEnum +import io.konform.validation.constraints.exclusiveMaximum as movedExclusiveMaximum +import io.konform.validation.constraints.exclusiveMinimum as movedExclusiveMinimum +import io.konform.validation.constraints.maxLength as movedMaxLength +import io.konform.validation.constraints.maxProperties as movedMaxProperties +import io.konform.validation.constraints.maximum as movedMaximum +import io.konform.validation.constraints.minLength as movedMinLength +import io.konform.validation.constraints.minProperties as movedMinProperties +import io.konform.validation.constraints.minimum as movedMinimum +import io.konform.validation.constraints.multipleOf as movedMultipleOf +import io.konform.validation.constraints.pattern as movedPattern +import io.konform.validation.constraints.type as movedType +import io.konform.validation.constraints.uuid as movedUuid + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("io.konform.validation.constraints.type()", "io.konform.validation.constraints.type"), +) +public inline fun ValidationBuilder<*>.type(): Constraint<*> = movedType() + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("io.konform.validation.constraints.enum(*allowed)", "io.konform.validation.constraints.enum"), +) +public fun ValidationBuilder.enum(vararg allowed: T): Constraint = movedEnum(*allowed) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("io.konform.validation.constraints.enum()", "io.konform.validation.constraints.enum"), +) +public inline fun > ValidationBuilder.enum(): Constraint = movedEnum() + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("io.konform.validation.constraints.const(expected)", "io.konform.validation.constraints.const"), +) +public fun ValidationBuilder.const(expected: T): Constraint = movedConst(expected) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("io.konform.validation.constraints.minLength(length)", "io.konform.validation.constraints.minLength"), +) +public fun ValidationBuilder.minLength(length: Int): Constraint = movedMinLength(length) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("io.konform.validation.constraints.maxLength(length)", "io.konform.validation.constraints.maxLength"), +) +public fun ValidationBuilder.maxLength(length: Int): Constraint = movedMaxLength(length) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("pattern(length)", "io.konform.validation.constraints.pattern"), +) +public fun ValidationBuilder.pattern(pattern: String): Constraint = movedPattern(pattern) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("uuid()", "io.konform.validation.constraints.uuid"), +) +public fun ValidationBuilder.uuid(): Constraint = movedUuid() + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("pattern(pattern)", "io.konform.validation.constraints.pattern"), +) +public fun ValidationBuilder.pattern(pattern: Regex): Constraint = movedPattern(pattern) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("multipleOf(factor)", "io.konform.validation.constraints.multipleOf"), +) +public fun ValidationBuilder.multipleOf(factor: Number): Constraint = movedMultipleOf(factor) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("maximum(maximumInclusive)", "io.konform.validation.constraints.maximum"), +) +public fun ValidationBuilder.maximum(maximumInclusive: Number): Constraint = movedMaximum(maximumInclusive) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("exclusiveMaximum(maximumExclusive)", "io.konform.validation.constraints.exclusiveMaximum"), +) public fun ValidationBuilder.exclusiveMaximum(maximumExclusive: Number): Constraint = - addConstraint( - "must be less than '{0}'", - maximumExclusive.toString(), - ) { it.toDouble() < maximumExclusive.toDouble() } - -public fun ValidationBuilder.minimum(minimumInclusive: Number): Constraint = - addConstraint( - "must be at least '{0}'", - minimumInclusive.toString(), - ) { it.toDouble() >= minimumInclusive.toDouble() } - + movedExclusiveMaximum(maximumExclusive) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("minimum(minimumInclusive)", "io.konform.validation.constraints.minimum"), +) +public fun ValidationBuilder.minimum(minimumInclusive: Number): Constraint = movedMinimum(minimumInclusive) + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("exclusiveMinimum(minimumExclusive)", "io.konform.validation.constraints.exclusiveMinimum"), +) public fun ValidationBuilder.exclusiveMinimum(minimumExclusive: Number): Constraint = - addConstraint( - "must be greater than '{0}'", - minimumExclusive.toString(), - ) { it.toDouble() > minimumExclusive.toDouble() } - -public fun ValidationBuilder.minLength(length: Int): Constraint { - require(length >= 0) { IllegalArgumentException("minLength requires the length to be >= 0") } - return addConstraint( - "must have at least {0} characters", - length.toString(), - ) { it.length >= length } -} - -public fun ValidationBuilder.maxLength(length: Int): Constraint { - require(length >= 0) { IllegalArgumentException("maxLength requires the length to be >= 0") } - return addConstraint( - "must have at most {0} characters", - length.toString(), - ) { it.length <= length } -} - -public fun ValidationBuilder.pattern(pattern: String): Constraint = pattern(pattern.toRegex()) - -/** Enforces the string must be UUID hex format. */ -public fun ValidationBuilder.uuid(): Constraint = - pattern("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") hint "must be a valid UUID string" - -public fun ValidationBuilder.pattern(pattern: Regex): Constraint = - addConstraint( - "must match the expected pattern", - pattern.toString(), - ) { it.matches(pattern) } + movedExclusiveMinimum(minimumExclusive) +@Suppress("UNCHECKED_CAST") +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("minItems(minSize)", "io.konform.validation.constraints.minItems"), +) public inline fun ValidationBuilder.minItems(minSize: Int): Constraint = addConstraint( "must have at least {0} items", @@ -103,6 +121,10 @@ public inline fun ValidationBuilder.minItems(minSize: Int): Const } } +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("maxItems(maxSize)", "io.konform.validation.constraints.maxItems"), +) public inline fun ValidationBuilder.maxItems(maxSize: Int): Constraint = addConstraint( "must have at most {0} items", @@ -116,12 +138,24 @@ public inline fun ValidationBuilder.maxItems(maxSize: Int): Const } } -public inline fun > ValidationBuilder.minProperties(minSize: Int): Constraint = - minItems(minSize) hint "must have at least {0} properties" - -public inline fun > ValidationBuilder.maxProperties(maxSize: Int): Constraint = - maxItems(maxSize) hint "must have at most {0} properties" - +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("minProperties(maxSize)", "io.konform.validation.constraints.minProperties"), +) +public fun ValidationBuilder>.minProperties(minSize: Int): Constraint> = + movedMinProperties(minSize) hint "must have at least {0} properties" + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("maxProperties(maxSize)", "io.konform.validation.constraints.maxProperties"), +) +public fun ValidationBuilder>.maxProperties(maxSize: Int): Constraint> = + movedMaxProperties(maxSize) hint "must have at most {0} properties" + +@Deprecated( + "Moved to io.konform.validation.constraints", + ReplaceWith("uniqueItems(unique)", "io.konform.validation.constraints.uniqueItems"), +) public inline fun ValidationBuilder.uniqueItems(unique: Boolean): Constraint = addConstraint( "all items must be unique", diff --git a/src/commonMain/kotlin/io/konform/validation/string/ValidationBuilderString.kt b/src/commonMain/kotlin/io/konform/validation/string/ValidationBuilderString.kt deleted file mode 100644 index a4566dc..0000000 --- a/src/commonMain/kotlin/io/konform/validation/string/ValidationBuilderString.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.konform.validation.string - -import io.konform.validation.Constraint -import io.konform.validation.ValidationBuilder - -public fun ValidationBuilder.notBlank(): Constraint = addConstraint("must not be blank") { it.isNotBlank() } diff --git a/src/commonTest/kotlin/io/konform/validation/ListValidationTest.kt b/src/commonTest/kotlin/io/konform/validation/ListValidationTest.kt index 2235aec..f20fe9e 100644 --- a/src/commonTest/kotlin/io/konform/validation/ListValidationTest.kt +++ b/src/commonTest/kotlin/io/konform/validation/ListValidationTest.kt @@ -1,6 +1,6 @@ package io.konform.validation -import io.konform.validation.jsonschema.minimum +import io.konform.validation.constraints.minimum import io.konform.validation.types.EmptyValidation import io.konform.validation.types.ValidateAll import io.kotest.assertions.konform.shouldBeInvalid diff --git a/src/commonTest/kotlin/io/konform/validation/ReadmeExampleTest.kt b/src/commonTest/kotlin/io/konform/validation/ReadmeExampleTest.kt index e8c28ff..a6b6867 100644 --- a/src/commonTest/kotlin/io/konform/validation/ReadmeExampleTest.kt +++ b/src/commonTest/kotlin/io/konform/validation/ReadmeExampleTest.kt @@ -1,12 +1,12 @@ package io.konform.validation -import io.konform.validation.jsonschema.maxItems -import io.konform.validation.jsonschema.maxLength -import io.konform.validation.jsonschema.maximum -import io.konform.validation.jsonschema.minItems -import io.konform.validation.jsonschema.minLength -import io.konform.validation.jsonschema.minimum -import io.konform.validation.jsonschema.pattern +import io.konform.validation.constraints.maxItems +import io.konform.validation.constraints.maxLength +import io.konform.validation.constraints.maximum +import io.konform.validation.constraints.minItems +import io.konform.validation.constraints.minLength +import io.konform.validation.constraints.minimum +import io.konform.validation.constraints.pattern import io.kotest.assertions.konform.shouldBeInvalid import io.kotest.assertions.konform.shouldBeValid import io.kotest.assertions.konform.shouldContainError diff --git a/src/commonTest/kotlin/io/konform/validation/JSONSchemaStyleConstraintsTest.kt b/src/commonTest/kotlin/io/konform/validation/constraints/ConstraintsTest.kt similarity index 90% rename from src/commonTest/kotlin/io/konform/validation/JSONSchemaStyleConstraintsTest.kt rename to src/commonTest/kotlin/io/konform/validation/constraints/ConstraintsTest.kt index f2ca81f..a2acae8 100644 --- a/src/commonTest/kotlin/io/konform/validation/JSONSchemaStyleConstraintsTest.kt +++ b/src/commonTest/kotlin/io/konform/validation/constraints/ConstraintsTest.kt @@ -1,30 +1,33 @@ -package io.konform.validation - -import io.konform.validation.JSONSchemaStyleConstraintsTest.TCPPacket.ACK -import io.konform.validation.JSONSchemaStyleConstraintsTest.TCPPacket.SYN -import io.konform.validation.JSONSchemaStyleConstraintsTest.TCPPacket.SYNACK -import io.konform.validation.jsonschema.const -import io.konform.validation.jsonschema.enum -import io.konform.validation.jsonschema.exclusiveMaximum -import io.konform.validation.jsonschema.exclusiveMinimum -import io.konform.validation.jsonschema.maxItems -import io.konform.validation.jsonschema.maxLength -import io.konform.validation.jsonschema.maxProperties -import io.konform.validation.jsonschema.maximum -import io.konform.validation.jsonschema.minItems -import io.konform.validation.jsonschema.minLength -import io.konform.validation.jsonschema.minProperties -import io.konform.validation.jsonschema.minimum -import io.konform.validation.jsonschema.multipleOf -import io.konform.validation.jsonschema.pattern -import io.konform.validation.jsonschema.type -import io.konform.validation.jsonschema.uniqueItems -import io.konform.validation.jsonschema.uuid +package io.konform.validation.constraints + +import io.konform.validation.Valid +import io.konform.validation.Validation +import io.konform.validation.ValidationResult +import io.konform.validation.constraints.ConstraintsTest.TCPPacket.ACK +import io.konform.validation.constraints.ConstraintsTest.TCPPacket.SYN +import io.konform.validation.constraints.ConstraintsTest.TCPPacket.SYNACK +import io.konform.validation.constraints.const +import io.konform.validation.constraints.exclusiveMaximum +import io.konform.validation.constraints.exclusiveMinimum +import io.konform.validation.constraints.maxItems +import io.konform.validation.constraints.maxLength +import io.konform.validation.constraints.maxProperties +import io.konform.validation.constraints.maximum +import io.konform.validation.constraints.minItems +import io.konform.validation.constraints.minLength +import io.konform.validation.constraints.minProperties +import io.konform.validation.constraints.minimum +import io.konform.validation.constraints.multipleOf +import io.konform.validation.constraints.pattern +import io.konform.validation.constraints.type +import io.konform.validation.constraints.uniqueItems +import io.konform.validation.constraints.uuid +import io.konform.validation.countFieldsWithErrors import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith -class JSONSchemaStyleConstraintsTest { +class ConstraintsTest { @Test fun typeConstraint() { val anyValidation = Validation { type() } @@ -42,8 +45,8 @@ class JSONSchemaStyleConstraintsTest { assertEquals(1, countFieldsWithErrors(anyNumberValidation("String"))) assertEquals(1, countFieldsWithErrors(anyNumberValidation(true))) - assertEquals("must be of the correct type", anyValidation(1).get()!![0]) - assertEquals("must be of the correct type", anyNumberValidation("String").get()!![0]) + assertEquals("must be of type 'String'", anyValidation(1).get()!![0]) + assertEquals("must be of type 'Int'", anyNumberValidation("String").get()!![0]) } @Test @@ -110,7 +113,7 @@ class JSONSchemaStyleConstraintsTest { assertEquals(1, countFieldsWithErrors(nullableConstValidation("Konverse"))) assertEquals("must be 'Konform'", validation("Konverse").get()!![0]) - assertEquals("must be null", nullableConstNullValidation("Konform").get()!![0]) + assertEquals("must be 'null'", nullableConstNullValidation("Konform").get()!![0]) assertEquals("must be 'Konform'", nullableConstValidation(null).get()!![0]) } diff --git a/src/commonTest/kotlin/io/konform/validation/validationbuilder/InstanceOfTest.kt b/src/commonTest/kotlin/io/konform/validation/validationbuilder/InstanceOfTest.kt index 47a08fb..6ac3b8d 100644 --- a/src/commonTest/kotlin/io/konform/validation/validationbuilder/InstanceOfTest.kt +++ b/src/commonTest/kotlin/io/konform/validation/validationbuilder/InstanceOfTest.kt @@ -2,7 +2,7 @@ package io.konform.validation.validationbuilder import io.konform.validation.PropertyValidationError import io.konform.validation.Validation -import io.konform.validation.string.notBlank +import io.konform.validation.constraints.notBlank import io.kotest.assertions.konform.shouldBeInvalid import io.kotest.assertions.konform.shouldBeValid import io.kotest.assertions.konform.shouldContainExactlyErrors diff --git a/src/jvmMain/kotlin/io/konform/validation/jsonschema/package-info.java b/src/jvmMain/kotlin/io/konform/validation/jsonschema/package-info.java new file mode 100644 index 0000000..83a9d7b --- /dev/null +++ b/src/jvmMain/kotlin/io/konform/validation/jsonschema/package-info.java @@ -0,0 +1,5 @@ +/** + * @deprecated As of release 0.9.0, replaced by {@link io.konform.validation.constraints} + */ +@Deprecated +package io.konform.validation.jsonschema;