diff --git a/build.gradle.kts b/build.gradle.kts index 89f810b..001050e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,6 +34,9 @@ group = projectGroup version = projectVersion kotlin { + // Since we are a library, prevent accidentally making things part of the public API + explicitApi() + sourceSets.all { languageSettings { languageVersion = kotlinApiTarget diff --git a/src/commonMain/kotlin/io/konform/validation/Validation.kt b/src/commonMain/kotlin/io/konform/validation/Validation.kt index ddeefdf..c992e79 100644 --- a/src/commonMain/kotlin/io/konform/validation/Validation.kt +++ b/src/commonMain/kotlin/io/konform/validation/Validation.kt @@ -2,17 +2,21 @@ package io.konform.validation import io.konform.validation.internal.ValidationBuilderImpl -interface Validation<T> { - companion object { - operator fun <T> invoke(init: ValidationBuilder<T>.() -> Unit): Validation<T> { +public interface Validation<T> { + public companion object { + public operator fun <T> invoke(init: ValidationBuilder<T>.() -> Unit): Validation<T> { val builder = ValidationBuilderImpl<T>() return builder.apply(init).build() } } - fun validate(value: T): ValidationResult<T> + public fun validate(value: T): ValidationResult<T> - operator fun invoke(value: T) = validate(value) + public operator fun invoke(value: T): ValidationResult<T> = validate(value) } -class Constraint<R> internal constructor(val hint: String, val templateValues: List<String>, val test: (R) -> Boolean) +public class Constraint<R> internal constructor( + public val hint: String, + public val templateValues: List<String>, + public val test: (R) -> Boolean, +) diff --git a/src/commonMain/kotlin/io/konform/validation/ValidationBuilder.kt b/src/commonMain/kotlin/io/konform/validation/ValidationBuilder.kt index 666248c..aece51e 100644 --- a/src/commonMain/kotlin/io/konform/validation/ValidationBuilder.kt +++ b/src/commonMain/kotlin/io/konform/validation/ValidationBuilder.kt @@ -13,18 +13,18 @@ import kotlin.reflect.KProperty1 private annotation class ValidationScope @ValidationScope -abstract class ValidationBuilder<T> { - abstract fun build(): Validation<T> +public abstract class ValidationBuilder<T> { + public abstract fun build(): Validation<T> - abstract fun addConstraint( + public abstract fun addConstraint( errorMessage: String, vararg templateValues: String, test: (T) -> Boolean, ): Constraint<T> - abstract infix fun Constraint<T>.hint(hint: String): Constraint<T> + public abstract infix fun Constraint<T>.hint(hint: String): Constraint<T> - abstract operator fun <R> KProperty1<T, R>.invoke(init: ValidationBuilder<R>.() -> Unit) + public abstract operator fun <R> KProperty1<T, R>.invoke(init: ValidationBuilder<R>.() -> Unit) internal abstract fun <R> onEachIterable( prop: KProperty1<T, Iterable<R>>, @@ -32,7 +32,7 @@ abstract class ValidationBuilder<T> { ) @JvmName("onEachIterable") - infix fun <R> KProperty1<T, Iterable<R>>.onEach(init: ValidationBuilder<R>.() -> Unit) = onEachIterable(this, init) + public infix fun <R> KProperty1<T, Iterable<R>>.onEach(init: ValidationBuilder<R>.() -> Unit): Unit = onEachIterable(this, init) internal abstract fun <R> onEachArray( prop: KProperty1<T, Array<R>>, @@ -40,7 +40,7 @@ abstract class ValidationBuilder<T> { ) @JvmName("onEachArray") - infix fun <R> KProperty1<T, Array<R>>.onEach(init: ValidationBuilder<R>.() -> Unit) = onEachArray(this, init) + public infix fun <R> KProperty1<T, Array<R>>.onEach(init: ValidationBuilder<R>.() -> Unit): Unit = onEachArray(this, init) internal abstract fun <K, V> onEachMap( prop: KProperty1<T, Map<K, V>>, @@ -48,31 +48,32 @@ abstract class ValidationBuilder<T> { ) @JvmName("onEachMap") - infix fun <K, V> KProperty1<T, Map<K, V>>.onEach(init: ValidationBuilder<Map.Entry<K, V>>.() -> Unit) = onEachMap(this, init) + public infix fun <K, V> KProperty1<T, Map<K, V>>.onEach(init: ValidationBuilder<Map.Entry<K, V>>.() -> Unit): Unit = + onEachMap(this, init) - abstract infix fun <R> KProperty1<T, R?>.ifPresent(init: ValidationBuilder<R>.() -> Unit) + public abstract infix fun <R> KProperty1<T, R?>.ifPresent(init: ValidationBuilder<R>.() -> Unit) - abstract infix fun <R> KProperty1<T, R?>.required(init: ValidationBuilder<R>.() -> Unit) + public abstract infix fun <R> KProperty1<T, R?>.required(init: ValidationBuilder<R>.() -> Unit) - abstract fun run(validation: Validation<T>) + public abstract fun run(validation: Validation<T>) - abstract val <R> KProperty1<T, R>.has: ValidationBuilder<R> + public abstract val <R> KProperty1<T, R>.has: ValidationBuilder<R> } -fun <T : Any> ValidationBuilder<T?>.ifPresent(init: ValidationBuilder<T>.() -> Unit) { +public fun <T : Any> ValidationBuilder<T?>.ifPresent(init: ValidationBuilder<T>.() -> Unit) { val builder = ValidationBuilderImpl<T>() init(builder) run(OptionalValidation(builder.build())) } -fun <T : Any> ValidationBuilder<T?>.required(init: ValidationBuilder<T>.() -> Unit) { +public fun <T : Any> ValidationBuilder<T?>.required(init: ValidationBuilder<T>.() -> Unit) { val builder = ValidationBuilderImpl<T>() init(builder) run(RequiredValidation(builder.build())) } @JvmName("onEachIterable") -fun <S, T : Iterable<S>> ValidationBuilder<T>.onEach(init: ValidationBuilder<S>.() -> Unit) { +public fun <S, T : Iterable<S>> ValidationBuilder<T>.onEach(init: ValidationBuilder<S>.() -> Unit) { val builder = ValidationBuilderImpl<S>() init(builder) @Suppress("UNCHECKED_CAST") @@ -80,7 +81,7 @@ fun <S, T : Iterable<S>> ValidationBuilder<T>.onEach(init: ValidationBuilder<S>. } @JvmName("onEachArray") -fun <T> ValidationBuilder<Array<T>>.onEach(init: ValidationBuilder<T>.() -> Unit) { +public fun <T> ValidationBuilder<Array<T>>.onEach(init: ValidationBuilder<T>.() -> Unit) { val builder = ValidationBuilderImpl<T>() init(builder) @Suppress("UNCHECKED_CAST") @@ -88,7 +89,7 @@ fun <T> ValidationBuilder<Array<T>>.onEach(init: ValidationBuilder<T>.() -> Unit } @JvmName("onEachMap") -fun <K, V, T : Map<K, V>> ValidationBuilder<T>.onEach(init: ValidationBuilder<Map.Entry<K, V>>.() -> Unit) { +public fun <K, V, T : Map<K, V>> ValidationBuilder<T>.onEach(init: ValidationBuilder<Map.Entry<K, V>>.() -> Unit) { val builder = ValidationBuilderImpl<Map.Entry<K, V>>() init(builder) @Suppress("UNCHECKED_CAST") diff --git a/src/commonMain/kotlin/io/konform/validation/ValidationResult.kt b/src/commonMain/kotlin/io/konform/validation/ValidationResult.kt index 6cddfff..6521a76 100644 --- a/src/commonMain/kotlin/io/konform/validation/ValidationResult.kt +++ b/src/commonMain/kotlin/io/konform/validation/ValidationResult.kt @@ -2,9 +2,9 @@ package io.konform.validation import kotlin.reflect.KProperty1 -interface ValidationError { - val dataPath: String - val message: String +public interface ValidationError { + public val dataPath: String + public val message: String } internal data class PropertyValidationError( @@ -16,7 +16,7 @@ internal data class PropertyValidationError( } } -interface ValidationErrors : List<ValidationError> +public interface ValidationErrors : List<ValidationError> internal object NoValidationErrors : ValidationErrors, List<ValidationError> by emptyList() @@ -26,15 +26,15 @@ internal class DefaultValidationErrors(private val errors: List<ValidationError> } } -sealed class ValidationResult<out T> { - abstract operator fun get(vararg propertyPath: Any): List<String>? +public sealed class ValidationResult<out T> { + public abstract operator fun get(vararg propertyPath: Any): List<String>? - abstract fun <R> map(transform: (T) -> R): ValidationResult<R> + public abstract fun <R> map(transform: (T) -> R): ValidationResult<R> - abstract val errors: ValidationErrors + public abstract val errors: ValidationErrors } -data class Invalid<T>( +public data class Invalid<T>( internal val internalErrors: Map<String, List<String>>, ) : ValidationResult<T>() { override fun get(vararg propertyPath: Any): List<String>? = internalErrors[propertyPath.joinToString("", transform = ::toPathSegment)] @@ -62,7 +62,7 @@ data class Invalid<T>( } } -data class Valid<T>(val value: T) : ValidationResult<T>() { +public data class Valid<T>(val value: T) : ValidationResult<T>() { override fun get(vararg propertyPath: Any): List<String>? = null override fun <R> map(transform: (T) -> R): ValidationResult<R> = Valid(transform(this.value)) diff --git a/src/commonMain/kotlin/io/konform/validation/jsonschema/JsonSchema.kt b/src/commonMain/kotlin/io/konform/validation/jsonschema/JsonSchema.kt index cab5539..2bef18d 100644 --- a/src/commonMain/kotlin/io/konform/validation/jsonschema/JsonSchema.kt +++ b/src/commonMain/kotlin/io/konform/validation/jsonschema/JsonSchema.kt @@ -4,18 +4,18 @@ import io.konform.validation.Constraint import io.konform.validation.ValidationBuilder import kotlin.math.roundToInt -inline fun <reified T> ValidationBuilder<*>.type() = +public inline fun <reified T> ValidationBuilder<*>.type(): Constraint<*> = addConstraint( "must be of the correct type", ) { it is T } -fun <T> ValidationBuilder<T>.enum(vararg allowed: T) = +public fun <T> ValidationBuilder<T>.enum(vararg allowed: T): Constraint<T> = addConstraint( "must be one of: {0}", allowed.joinToString("', '", "'", "'"), ) { it in allowed } -inline fun <reified T : Enum<T>> ValidationBuilder<String>.enum(): Constraint<String> { +public inline fun <reified T : Enum<T>> ValidationBuilder<String>.enum(): Constraint<String> { val enumNames = enumValues<T>().map { it.name } return addConstraint( "must be one of: {0}", @@ -23,13 +23,13 @@ inline fun <reified T : Enum<T>> ValidationBuilder<String>.enum(): Constraint<St ) { it in enumNames } } -fun <T> ValidationBuilder<T>.const(expected: T) = +public fun <T> ValidationBuilder<T>.const(expected: T): Constraint<T> = addConstraint( "must be {0}", expected?.let { "'$it'" } ?: "null", ) { expected == it } -fun <T : Number> ValidationBuilder<T>.multipleOf(factor: Number): Constraint<T> { +public fun <T : Number> ValidationBuilder<T>.multipleOf(factor: Number): Constraint<T> { 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()) { @@ -38,31 +38,31 @@ fun <T : Number> ValidationBuilder<T>.multipleOf(factor: Number): Constraint<T> } } -fun <T : Number> ValidationBuilder<T>.maximum(maximumInclusive: Number) = +public fun <T : Number> ValidationBuilder<T>.maximum(maximumInclusive: Number): Constraint<T> = addConstraint( "must be at most '{0}'", maximumInclusive.toString(), ) { it.toDouble() <= maximumInclusive.toDouble() } -fun <T : Number> ValidationBuilder<T>.exclusiveMaximum(maximumExclusive: Number) = +public fun <T : Number> ValidationBuilder<T>.exclusiveMaximum(maximumExclusive: Number): Constraint<T> = addConstraint( "must be less than '{0}'", maximumExclusive.toString(), ) { it.toDouble() < maximumExclusive.toDouble() } -fun <T : Number> ValidationBuilder<T>.minimum(minimumInclusive: Number) = +public fun <T : Number> ValidationBuilder<T>.minimum(minimumInclusive: Number): Constraint<T> = addConstraint( "must be at least '{0}'", minimumInclusive.toString(), ) { it.toDouble() >= minimumInclusive.toDouble() } -fun <T : Number> ValidationBuilder<T>.exclusiveMinimum(minimumExclusive: Number) = +public fun <T : Number> ValidationBuilder<T>.exclusiveMinimum(minimumExclusive: Number): Constraint<T> = addConstraint( "must be greater than '{0}'", minimumExclusive.toString(), ) { it.toDouble() > minimumExclusive.toDouble() } -fun ValidationBuilder<String>.minLength(length: Int): Constraint<String> { +public fun ValidationBuilder<String>.minLength(length: Int): Constraint<String> { require(length >= 0) { IllegalArgumentException("minLength requires the length to be >= 0") } return addConstraint( "must have at least {0} characters", @@ -70,7 +70,7 @@ fun ValidationBuilder<String>.minLength(length: Int): Constraint<String> { ) { it.length >= length } } -fun ValidationBuilder<String>.maxLength(length: Int): Constraint<String> { +public fun ValidationBuilder<String>.maxLength(length: Int): Constraint<String> { require(length >= 0) { IllegalArgumentException("maxLength requires the length to be >= 0") } return addConstraint( "must have at most {0} characters", @@ -78,15 +78,15 @@ fun ValidationBuilder<String>.maxLength(length: Int): Constraint<String> { ) { it.length <= length } } -fun ValidationBuilder<String>.pattern(pattern: String) = pattern(pattern.toRegex()) +public fun ValidationBuilder<String>.pattern(pattern: String): Constraint<String> = pattern(pattern.toRegex()) -fun ValidationBuilder<String>.pattern(pattern: Regex) = +public fun ValidationBuilder<String>.pattern(pattern: Regex): Constraint<String> = addConstraint( "must match the expected pattern", pattern.toString(), ) { it.matches(pattern) } -inline fun <reified T> ValidationBuilder<T>.minItems(minSize: Int): Constraint<T> = +public inline fun <reified T> ValidationBuilder<T>.minItems(minSize: Int): Constraint<T> = addConstraint( "must have at least {0} items", minSize.toString(), @@ -99,7 +99,7 @@ inline fun <reified T> ValidationBuilder<T>.minItems(minSize: Int): Constraint<T } } -inline fun <reified T> ValidationBuilder<T>.maxItems(maxSize: Int): Constraint<T> = +public inline fun <reified T> ValidationBuilder<T>.maxItems(maxSize: Int): Constraint<T> = addConstraint( "must have at most {0} items", maxSize.toString(), @@ -112,13 +112,13 @@ inline fun <reified T> ValidationBuilder<T>.maxItems(maxSize: Int): Constraint<T } } -inline fun <reified T : Map<*, *>> ValidationBuilder<T>.minProperties(minSize: Int): Constraint<T> = +public inline fun <reified T : Map<*, *>> ValidationBuilder<T>.minProperties(minSize: Int): Constraint<T> = minItems(minSize) hint "must have at least {0} properties" -inline fun <reified T : Map<*, *>> ValidationBuilder<T>.maxProperties(maxSize: Int): Constraint<T> = +public inline fun <reified T : Map<*, *>> ValidationBuilder<T>.maxProperties(maxSize: Int): Constraint<T> = maxItems(maxSize) hint "must have at most {0} properties" -inline fun <reified T> ValidationBuilder<T>.uniqueItems(unique: Boolean): Constraint<T> = +public inline fun <reified T> ValidationBuilder<T>.uniqueItems(unique: Boolean): Constraint<T> = addConstraint( "all items must be unique", ) {