commonMain.io.konform.validation.jsonschema.JsonSchema.kt Maven / Gradle / Ivy
package io.konform.validation.jsonschema
import io.konform.validation.Constraint
import io.konform.validation.ValidationBuilder
import kotlin.math.roundToInt
inline fun ValidationBuilder<*>.type() =
addConstraint(
"must be of the correct type"
) { it is T }
fun ValidationBuilder.enum(vararg allowed: T) =
addConstraint(
"must be one of: {0}",
allowed.joinToString("', '", "'", "'")
) { it in allowed }
inline fun > ValidationBuilder.enum(): Constraint {
val enumNames = enumValues().map { it.name }
return addConstraint(
"must be one of: {0}",
enumNames.joinToString("', '", "'", "'")
) { it in enumNames }
}
fun ValidationBuilder.const(expected: T) =
addConstraint(
"must be {0}",
expected?.let { "'$it'" } ?: "null"
) { expected == it }
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
}
}
fun ValidationBuilder.maximum(maximumInclusive: Number) = addConstraint(
"must be at most '{0}'",
maximumInclusive.toString()
) { it.toDouble() <= maximumInclusive.toDouble() }
fun ValidationBuilder.exclusiveMaximum(maximumExclusive: Number) = addConstraint(
"must be less than '{0}'",
maximumExclusive.toString()
) { it.toDouble() < maximumExclusive.toDouble() }
fun ValidationBuilder.minimum(minimumInclusive: Number) = addConstraint(
"must be at least '{0}'",
minimumInclusive.toString()
) { it.toDouble() >= minimumInclusive.toDouble() }
fun ValidationBuilder.exclusiveMinimum(minimumExclusive: Number) = addConstraint(
"must be greater than '{0}'",
minimumExclusive.toString()
) { it.toDouble() > minimumExclusive.toDouble() }
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 }
}
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 }
}
fun ValidationBuilder.pattern(pattern: String) = pattern(pattern.toRegex())
fun ValidationBuilder.pattern(pattern: Regex) = addConstraint(
"must match the expected pattern",
pattern.toString()
) { it.matches(pattern) }
inline fun ValidationBuilder.minItems(minSize: Int): Constraint = addConstraint(
"must have at least {0} items",
minSize.toString()
) {
when (it) {
is Iterable<*> -> it.count() >= minSize
is Array<*> -> it.count() >= minSize
is Map<*, *> -> it.count() >= minSize
else -> throw IllegalStateException("minItems can not be applied to type ${T::class}")
}
}
inline fun ValidationBuilder.maxItems(maxSize: Int): Constraint = addConstraint(
"must have at most {0} items",
maxSize.toString()
) {
when (it) {
is Iterable<*> -> it.count() <= maxSize
is Array<*> -> it.count() <= maxSize
is Map<*, *> -> it.count() <= maxSize
else -> throw IllegalStateException("maxItems can not be applied to type ${T::class}")
}
}
inline fun > ValidationBuilder.minProperties(minSize: Int): Constraint =
minItems(minSize) hint "must have at least {0} properties"
inline fun > ValidationBuilder.maxProperties(maxSize: Int): Constraint =
maxItems(maxSize) hint "must have at most {0} properties"
inline fun ValidationBuilder.uniqueItems(unique: Boolean): Constraint = addConstraint(
"all items must be unique"
) {
!unique || when (it) {
is Iterable<*> -> it.distinct().count() == it.count()
is Array<*> -> it.distinct().count() == it.count()
else -> throw IllegalStateException("uniqueItems can not be applied to type ${T::class}")
}
}