All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.validations.RequiredValues.kt Maven / Gradle / Ivy

There is a newer version: 0.23.0
Show newest version
@file:JvmName("RequiredValues")

package io.kform.validations

import io.kform.*
import io.kform.datatypes.File
import io.kform.datatypes.Table
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

/**
 * Returns whether a value is missing (this checks whether the value is `null`, `false`, or empty).
 * The emptiness of values of type [String], [Collection], [Array] (and variants), [Map], and
 * [Table] is checked.
 *
 * This notion of "missing" portraited by this function is the same one used by the [Required]
 * validation.
 */
public fun Any?.isMissing(): Boolean =
    this == null ||
        this == false ||
        when (this) {
            is CharSequence -> this.isEmpty()
            is Collection<*> -> this.isEmpty()
            is Array<*> -> this.isEmpty()
            is Map<*, *> -> this.isEmpty()
            is Table<*> -> this.isEmpty()
            // Array variants
            is BooleanArray -> this.isEmpty()
            is ByteArray -> this.isEmpty()
            is CharArray -> this.isEmpty()
            is DoubleArray -> this.isEmpty()
            is FloatArray -> this.isEmpty()
            is IntArray -> this.isEmpty()
            is LongArray -> this.isEmpty()
            is ShortArray -> this.isEmpty()
            else -> false
        }

/**
 * Returns whether a value is not missing (this checks that the value is not `null`, `false`, or
 * empty). The emptiness of values of type [String], [Collection], [Array] (and variants), [Map],
 * and [Table] is checked.
 *
 * This notion of "missing" portraited by this function is the same one used by the [Required]
 * validation.
 */
public fun Any?.isNotMissing(): Boolean = !this.isMissing()

/**
 * Validation that checks that a value is not [missing][isMissing] (this checks that the value is
 * not `null`, `false`, or empty). This validation checks the emptiness of values of type [String],
 * [Collection], [Array] (and variants), [Map], and [Table].
 *
 * When the value being validated is missing, then an issue is emitted with the provided [code]
 * (defaults to [DEFAULT_CODE]).
 *
 * A few notes on the [Required] validation:
 * - This validation tries to match the expected notion of what it means for a value to be
 *   "required": this can be thought of as the (∗) icon typically shown in user interfaces to denote
 *   required fields.
 * - If a value is not nullable, and you want to forbid it from being empty, this validation should
 *   be preferred over [NotEmpty] since it is the semantic way of signalling that the value is
 *   required (even though both validations would acchieve the same result).
 * - When a value is nullable and `null` values are not to be allowed, then this validation must be
 *   set on the [NullableSchema][io.kform.schemas.NullableSchema] itself, rather than on its inner
 *   schema.
 * - Custom validations may use the [isMissing]/[isNotMissing] functions to match the notion of
 *   "missing" values used by this validation.
 * - Empty [files][File] (files with size 0) are not considered missing values. This is due to the
 *   fact that empty files still contain some information (the file name itself) and is consistent
 *   with how empty files are treated in web forms. To forbid both the `null` value and empty files,
 *   use [Required] together with [NotEmpty].
 * - To only validate that a value is not `null` or `false`, use [MustNotEqual] instead.
 * - To allow `null` but not empty values, use [NotEmpty] instead.
 * - To prevent both empty and blank strings, use [Required] together with [NotBlank].
 *
 * @param code Issue code to use when the value is missing.
 * @param severity Severity of the issue emitted when the value is missing.
 */
public open class Required
@JvmOverloads
constructor(
    public val code: String = DEFAULT_CODE,
    public val severity: ValidationIssueSeverity = ValidationIssueSeverity.Error
) : Validation() {
    override fun toString(): String = "Required"

    override fun ValidationContext.validate(): Flow = flow {
        if (value.isMissing()) {
            emit(ValidationIssue(code, severity))
        }
    }

    public companion object {
        /** Default issue code representing that the value is missing. */
        public const val DEFAULT_CODE: String = "valueMissing"
    }
}

/**
 * Validation that checks that a value is not empty. Values of type [String], [Collection], [Array]
 * (and variants), [Map], [Table], and [File] are supported.
 *
 * When the value being validated is empty, then an issue is emitted with the provided [code]
 * (defaults to [DEFAULT_CODE]).
 *
 * The [Required] validation should be preferred over this validation to semantically signal that a
 * value is required. Otherwise, this validation should be used when:
 * - The value is nullable and `null` values are accepted, but not empty values.
 * - The value is a [File] and empty files should not be accepted, since [Required] does not forbid
 *   empty files. To forbid both the `null` value and empty files, use [Required] together with
 *   [NotEmpty].
 *
 * @param code Issue code to use when the value is empty.
 * @param severity Severity of the issue emitted when the value is empty.
 */
public open class NotEmpty
@JvmOverloads
constructor(
    public val code: String = DEFAULT_CODE,
    public val severity: ValidationIssueSeverity = ValidationIssueSeverity.Error
) : Validation() {
    override fun toString(): String = "NotEmpty"

    override fun ValidationContext.validate(): Flow = flow {
        if (isEmpty(value)) {
            emit(ValidationIssue(code, severity))
        }
    }

    private fun isEmpty(value: Any): Boolean =
        when (value) {
            is CharSequence -> value.isEmpty()
            is Collection<*> -> value.isEmpty()
            is Array<*> -> value.isEmpty()
            is Map<*, *> -> value.isEmpty()
            is Table<*> -> value.isEmpty()
            is File -> value.size == 0
            // Array variants
            is BooleanArray -> value.isEmpty()
            is ByteArray -> value.isEmpty()
            is CharArray -> value.isEmpty()
            is DoubleArray -> value.isEmpty()
            is FloatArray -> value.isEmpty()
            is IntArray -> value.isEmpty()
            is LongArray -> value.isEmpty()
            is ShortArray -> value.isEmpty()
            else ->
                error(
                    "Unsupported value type: supported types are `String`, `Collection`, `Array` " +
                        "(including variants), `Map`, `Table`, and `File`."
                )
        }

    public companion object {
        /** Default issue code representing that the value is empty. */
        public const val DEFAULT_CODE: String = "valueMissing"
    }
}

/**
 * Validation that checks that a string is not blank (according to [String.isBlank]) when it is also
 * not empty.
 *
 * When the string being validated is not empty and is blank, then an issue is emitted with the
 * provided [code] (defaults to [DEFAULT_CODE]).
 *
 * To forbid both empty and blank values, use this validation together with [Required] (preferred)
 * or [NotEmpty] (if the value is nullable and `null` values should be accepted).
 *
 * @param code Issue code to use when the string is not empty and blank.
 * @param severity Severity of the issue emitted when the string is not empty and blank.
 */
public open class NotBlank
@JvmOverloads
constructor(
    public val code: String = DEFAULT_CODE,
    public val severity: ValidationIssueSeverity = ValidationIssueSeverity.Error
) : Validation() {
    override fun toString(): String = "NotBlank"

    override fun ValidationContext.validate(): Flow = flow {
        if (value.isNotEmpty() && value.isBlank()) {
            emit(ValidationIssue(code, severity))
        }
    }

    public companion object {
        /** Default issue code representing that the string is not empty and blank. */
        public const val DEFAULT_CODE: String = "valueMissing"
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy